当前位置: 首页 > news >正文

Spring Authorization Server OAuth2.1

 Spring Authorization Server介绍

Spring Authorization Server 是一个框架,它提供了 OAuth 2.1 OpenID Connect 1.0 规范以及其他相关规范的实现。

它建立在 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供者和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础

因为随着网络和设备的发展,原先的 OAuth 2.0 已经不能满足现今的需求了,OAuth 社区对 OAuth 2.0 中的几种授权模式进行了取舍和优化,并增加一些新的特性, 于是推出了 OAuth 2.1,

而原先的 Spring Security OAuth 2.0 使用的是 OAuth 2.0 协议,为满足新的变化,Spring Security 团队重新写了一套叫 Spring Authorization Server 的认证授权框架来替换原先的 Spring Security OAuth 2.0

 

实现方式的不同 

Springboot2.x Oauth2实现

  • Spring Security Oauth2 支持搭建授权服务器和资源服务器

Springboot3.x Oauth2实现

  • Spring Security6 自身提供资源和客户端类库支持
  • Spring Authorization Server支持授权服务器搭建

OAuth2.1协议

OAuth 2.1去掉了OAuth2.0中的密码模式、简化模式,增加了设备授权码模式,同时也对授权码模式增加了PKCE扩展

OAuth2.1协议 官网:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07

OAuth 2.1 的变化 有道云笔记:https://note.youdao.com/s/DRnLx34U

授权码模式+PKCE扩展 

在OAuth2.0版本的授权码的基础上,进行了扩展。下面的之前授权码模式的流程:

  • 1、用户访问客户端,后者将前者导向授权服务器。
  • 2、用户选择是否给予客户端授权。
  • 3、假设用户给予授权,授权服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
  • 4、客户端收到授权码,附上早先的"重定向URI",向授权服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
  • 5、授权服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)

在第3个步骤中,授权服务器通过重定向URL,将授权码Code发送给客户端,此时可能会被黑客拦截,他得到了code,他去利用code进行申请token,进而去访问资源服务器 为了减轻这种攻击,官方增加PKCE扩展


  • 1. 客户端通过“/oauth2/authorize”地址向认证服务器发起获取授权码请求的时候增加两个参数,即 code_challenge 和 code_challenge_method
  • 2. 认证服务器给客户端返回授权码,同时记录下 code_challenge、code_challenge_method 的值。
  • 3. 客户端使用 code 向认证服务器获取 Access Token 的时候,带上 code_verifier 参数,其值为步骤A加密前的初始值。
  • 4. 认证服务器收到步骤 C 的请求时,将 code_verifier 的值使用 code_challenge_method 的方法进行加密,然后将加密后的值与步骤A中的 code_challenge 进行比较,看看是否一致

code_challenge_method 是加密方法(例如:S256 或 plain(废弃))code_challenge 是使用code_challenge_method加密方法加密后的值

搭建授权服务

版本要求

spring boot 3.x

jdk版本 17

Spring Authorization Server重要组件

  • SecurityFilterChain -> authorizationServerSecurityFilterChain Spring Security的过滤器链,用于协议端点的。
  • SecurityFilterChain -> defaultSecurityFilterChain Spring Security的过滤器链,用于Spring Security的身份认证
  • UserDetailsService 主要进行用户身份验证(存入库中)
  • RegisteredClientRepository 主要用于管理客户端
  • JWKSource 用于签名访问令牌
  • KeyPair 启动时生成的带有密钥的KeyPair实例,用于创建上面的JWKSource
  • JwtDecoder JwtDecoder的一个实例,用于解码已签名的访问令牌
  • AuthorizationServerSettings 用于配置Spring Authorization Server的AuthorizationServerSettings实例

准备sql

授权相关的表

-- 用户授权确认表
CREATE TABLE oauth2_authorization_consent
(registered_client_id varchar(100)  NOT NULL,principal_name       varchar(200)  NOT NULL,authorities          varchar(1000) NOT NULL,PRIMARY KEY (registered_client_id, principal_name)
);
-- 用户认证信息表
CREATE TABLE oauth2_authorization
(id                            varchar(100) NOT NULL,registered_client_id          varchar(100) NOT NULL,principal_name                varchar(200) NOT NULL,authorization_grant_type      varchar(100) NOT NULL,authorized_scopes             varchar(1000) DEFAULT NULL,attributes                    blob          DEFAULT NULL,state                         varchar(500)  DEFAULT NULL,authorization_code_value      blob          DEFAULT NULL,authorization_code_issued_at  DATETIME      DEFAULT NULL,authorization_code_expires_at DATETIME      DEFAULT NULL,authorization_code_metadata   blob          DEFAULT NULL,access_token_value            blob          DEFAULT NULL,access_token_issued_at        DATETIME      DEFAULT NULL,access_token_expires_at       DATETIME      DEFAULT NULL,access_token_metadata         blob          DEFAULT NULL,access_token_type             varchar(100)  DEFAULT NULL,access_token_scopes           varchar(1000) DEFAULT NULL,oidc_id_token_value           blob          DEFAULT NULL,oidc_id_token_issued_at       DATETIME      DEFAULT NULL,oidc_id_token_expires_at      DATETIME      DEFAULT NULL,oidc_id_token_metadata        blob          DEFAULT NULL,refresh_token_value           blob          DEFAULT NULL,refresh_token_issued_at       DATETIME      DEFAULT NULL,refresh_token_expires_at      DATETIME      DEFAULT NULL,refresh_token_metadata        blob          DEFAULT NULL,user_code_value               blob          DEFAULT NULL,user_code_issued_at           DATETIME      DEFAULT NULL,user_code_expires_at          DATETIME      DEFAULT NULL,user_code_metadata            blob          DEFAULT NULL,device_code_value             blob          DEFAULT NULL,device_code_issued_at         DATETIME      DEFAULT NULL,device_code_expires_at        DATETIME      DEFAULT NULL,device_code_metadata          blob          DEFAULT NULL,PRIMARY KEY (id)
);
-- 客户端表
CREATE TABLE oauth2_registered_client
(id                            varchar(100)                            NOT NULL,client_id                     varchar(100)                            NOT NULL,client_id_issued_at           DATETIME      DEFAULT CURRENT_TIMESTAMP NOT NULL,client_secret                 varchar(200)  DEFAULT NULL,client_secret_expires_at      DATETIME      DEFAULT NULL,client_name                   varchar(200)                            NOT NULL,client_authentication_methods varchar(1000)                           NOT NULL,authorization_grant_types     varchar(1000)                           NOT NULL,redirect_uris                 varchar(1000) DEFAULT NULL,post_logout_redirect_uris     varchar(1000) DEFAULT NULL,scopes                        varchar(1000)                           NOT NULL,client_settings               varchar(2000)                           NOT NULL,token_settings                varchar(2000)                           NOT NULL,PRIMARY KEY (id)
);

用户表

CREATE TABLE `sys_user` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '用户名',`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '密码',`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '姓名',`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '描述',`status` tinyint DEFAULT NULL COMMENT '状态(1:正常 0:停用)',PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `idx_username` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户表';INSERT INTO sys_user (`id`, `username`, `password`, `name`, `description`, `status`) VALUES (1, 'admin', '$2a$10$8fyY0WbNAr980e6nLcPL5ugmpkLLH3serye5SJ3UcDForTW5b0Sx.', '测试用户', 'Spring Security 测试用户', 1);

 pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>org.example</groupId><artifactId>oauthServer</artifactId><version>0.0.1-SNAPSHOT</version><name>oauthServer</name><description>oauthServer</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-authorization-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

yml配置文件

 

spring:application:name: oauthServerdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://39.102.208.240:3306/auth?serverTimezone=UTC&userUnicode=true&characterEncoding=utf-8username: rootpassword: root123456
server:port: 9800

在config下建配置类AuthorizationConfig

package org.example.oauthserver.config;import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.example.oauthserver.util.SecurityUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;/*** 认证配置* {@link EnableWebSecurity} 注解有两个作用:* 1. 加载了WebSecurityConfiguration配置类, 配置安全认证策略。* 2. 加载了AuthenticationConfiguration, 配置了认证信息。** ==============================================重要组件===============================================* SecurityFilterChain -> authorizationServerSecurityFilterChain Spring Security的过滤器链,用于协议端点的。* SecurityFilterChain -> defaultSecurityFilterChain Spring Security的过滤器链,用于Spring Security的身份认证* UserDetailsService 主要进行用户身份验证* RegisteredClientRepository 主要用于管理客户端* JWKSource 用于签名访问令牌* KeyPair 启动时生成的带有密钥的KeyPair实例,用于创建上面的JWKSource* JwtDecoder JwtDecoder的一个实例,用于解码已签名的访问令牌* AuthorizationServerSettings 用于配置Spring Authorization Server的AuthorizationServerSettings实例**/
@Configuration
@EnableWebSecurity
public class AuthorizationConfig {/*** 配置端点的过滤器链** @param http spring security核心配置类* @return 过滤器链* @throws Exception 抛出*/@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,RegisteredClientRepository registeredClientRepository,AuthorizationServerSettings authorizationServerSettings) throws Exception {// 配置默认的设置,忽略认证端点的csrf校验OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 开启OpenID Connect 1.0协议相关端点.oidc(Customizer.withDefaults());http//将需要认证的请求,重定向到login进行登录认证。.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),new MediaTypeRequestMatcher(MediaType.TEXT_HTML)));http// 处理使用access token访问用户信息端点和客户端注册端点.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));return http.build();}/*** 配置认证相关的过滤器链** @param http spring security核心配置类* @return 过滤链配置* @throws Exception 抛出*/@Bean@Order(2)public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize// 所有请求都需要认证.anyRequest().authenticated()).formLogin(Customizer.withDefaults());// 由Spring Security过滤链中UsernamePasswordAuthenticationFilter过滤器拦截处理“login”页面提交的登录信息 和异常处理http.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()).accessDeniedHandler(SecurityUtils::exceptionHandler).authenticationEntryPoint(SecurityUtils::exceptionHandler));return http.build();}/*** 自定义jwt,将权限信息放至jwt中** @return OAuth2TokenCustomizer的实例*/@Beanpublic OAuth2TokenCustomizer<JwtEncodingContext> oAuth2TokenCustomizer() {return context -> {// 检查登录用户信息是不是UserDetails,排除掉没有用户参与的流程if (context.getPrincipal().getPrincipal() instanceof UserDetails user) {// 获取申请的scopesSet<String> scopes = context.getAuthorizedScopes();// 获取用户的权限Collection<? extends GrantedAuthority> authorities = user.getAuthorities();// 提取权限并转为字符串Set<String> authoritySet = Optional.ofNullable(authorities).orElse(Collections.emptyList()).stream()// 获取权限字符串.map(GrantedAuthority::getAuthority)// 去重.collect(Collectors.toSet());// 合并scope与用户信息authoritySet.addAll(scopes);JwtClaimsSet.Builder claims = context.getClaims();// 将权限信息放入jwt的claims中(也可以生成一个以指定字符分割的字符串放入)claims.claim("authorities", authoritySet);}};}/*** 自定义jwt解析器,设置解析出来的权限信息的前缀与在jwt中的key** @return jwt解析器 JwtAuthenticationConverter*/@Beanpublic JwtAuthenticationConverter jwtAuthenticationConverter() {JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();// 设置解析权限信息的前缀,设置为空是去掉前缀grantedAuthoritiesConverter.setAuthorityPrefix("");// 设置权限信息在jwt claims中的keygrantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);return jwtAuthenticationConverter;}/*** 配置密码解析器,使用BCrypt的方式对密码进行加密和验证** @return BCryptPasswordEncoder*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** Spring Security的配置* 设置用户信息,校验用户名、密码* 正常的流程是自定义一个service类,实现UserDetailsService接口,去查询DB 查询用户信息,封装为一个UserDetails对象返回* 这里就直接写一个user存入内存中进行测试* @return*/
//    @Bean
//    public UserDetailsService users(PasswordEncoder passwordEncoder) {
//        UserDetails user = User.withUsername("admin")
//                .password(passwordEncoder.encode("123456"))
//                .roles("admin", "normal")
//                .authorities("app", "web")
//                .build();
//        return new InMemoryUserDetailsManager(user);
//    }/*** 配置客户端Repository** @param jdbcTemplate    db 数据源信息* @param passwordEncoder 密码解析器* @return 基于数据库的repository 对应表:oauth2_registered_client*/@Beanpublic RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())// 客户端id.clientId("oidc-client")// 加密 客户端秘钥{noop}开头,表示“secret”以明文存储.clientSecret(passwordEncoder.encode("123456"))// 客户端认证方式,就使用默认的认证方式基于请求头的认证.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)// 配置资源服务器使用该客户端获取授权时支持的方式.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)// 授权码模式回调地址,oauth2.1已改为精准匹配,不能只设置域名,并且屏蔽了localhost,本机使用127.0.0.1访问.redirectUri("http://127.0.0.1:9700/login/oauth2/code/messaging-client-oidc").redirectUri("https://www.baidu.com")// 该客户端的授权范围,OPENID与PROFILE是IdToken的scope,获取授权时请求OPENID的scope时认证服务会返回IdToken.scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE)// 自定scope.scope("read").scope("write")// 客户端设置,设置用户需要确认授权.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();// 基于db存储客户端,还有一个基于内存的实现 InMemoryRegisteredClientRepositoryJdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);// 初始化客户端RegisteredClient repositoryByClientId = registeredClientRepository.findByClientId(registeredClient.getClientId());if (repositoryByClientId == null) {registeredClientRepository.save(registeredClient);}// PKCE客户端RegisteredClient pkceClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("pkce-oidc-client")// 公共客户端.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)// 授权码模式,因为是扩展授权码流程,所以流程还是授权码的流程,改变的只是参数.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)// 授权码模式回调地址,oauth2.1已改为精准匹配,不能只设置域名,并且屏蔽了localhost,本机使用127.0.0.1访问.redirectUri("http://oauthClient:9700/login/oauth2/code/messaging-client-oidc").redirectUri("https://www.baidu.com").clientSettings(ClientSettings.builder().requireProofKey(Boolean.TRUE).build())// 自定scope.scope("read").scope("write").build();RegisteredClient findPkceClient = registeredClientRepository.findByClientId(pkceClient.getClientId());if (findPkceClient == null) {registeredClientRepository.save(pkceClient);}return registeredClientRepository;}/*** 授权信息* 对应表:oauth2_authorization*/@Beanpublic OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {// 基于db的oauth2认证服务,还有一个基于内存的服务实现InMemoryOAuth2AuthorizationServicereturn new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);}/*** 授权确认*对应表:oauth2_authorization_consent*/@Beanpublic OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {// 基于db的授权确认管理服务,还有一个基于内存的服务实现InMemoryOAuth2AuthorizationConsentServicereturn new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);}/*** 配置jwk源,使用非对称加密,公开用于检索匹配指定选择器的JWK的方法** @return JWKSource*/@Beanpublic JWKSource<SecurityContext> jwkSource() {KeyPair keyPair = generateRsaKey();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();JWKSet jwkSet = new JWKSet(rsaKey);return new ImmutableJWKSet<>(jwkSet);}/*** 生成rsa密钥对,提供给jwk** @return 密钥对*/private static KeyPair generateRsaKey() {KeyPair keyPair;try {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(2048);keyPair = keyPairGenerator.generateKeyPair();} catch (Exception ex) {throw new IllegalStateException(ex);}return keyPair;}/*** 配置jwt解析器** @param jwkSource jwk源* @return JwtDecoder*/@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}/*** 添加授权服务器配置,配置授权服务器请求地址* @return AuthorizationServerSettings*/@Beanpublic AuthorizationServerSettings authorizationServerSettings() {// 什么都不配置,则使用默认地址return AuthorizationServerSettings.builder().build();}}

 添加用户信息

实体信息如下

package org.example.oauthserver.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("sys_user")
public class SysUserEntity implements Serializable {/*** 主键*/@TableId(type = IdType.AUTO)private Integer id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 名字*/private String name;/*** 描述*/private String description;/*** 状态*/private Integer status;}

mapper如下 

package org.example.oauthserver.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.oauthserver.entity.SysUserEntity;@Mapper
public interface SysUserMapper extends BaseMapper<SysUserEntity> {}

service如下 

package org.example.oauthserver.service;import org.example.oauthserver.entity.SysUserEntity;public interface SysUserService {/**** 根据用户名查询用户信息*/SysUserEntity selectByUsername(String username);}
package org.example.oauthserver.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.oauthserver.entity.SysUserEntity;
import org.example.oauthserver.mapper.SysUserMapper;
import org.example.oauthserver.service.SysUserService;
import org.springframework.stereotype.Service;@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements SysUserService {@Overridepublic SysUserEntity selectByUsername(String username) {LambdaQueryWrapper<SysUserEntity> lambdaQueryWrapper = new LambdaQueryWrapper();lambdaQueryWrapper.eq(SysUserEntity::getUsername,username);return this.getOne(lambdaQueryWrapper);}
}

 UserDetailsService如下

 

package org.example.oauthserver.service.impl;import jakarta.annotation.Resource;
import org.example.oauthserver.entity.SysUserEntity;
import org.example.oauthserver.service.SysUserService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate SysUserService sysUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUserEntity sysUserEntity = sysUserService.selectByUsername(username);List<SimpleGrantedAuthority> grantedAuthorityList = Arrays.asList("USER").stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());return new User(username,sysUserEntity.getPassword(),grantedAuthorityList);}
}

访问测试授权码模式

访问授权端点

http://127.0.0.1:9800/oauth2/authorize?client_id=oidc-client&response_type=code&scope=read&redirect_uri=https://www.baidu.com
http://127.0.0.1:9800/oauth2/authorize?client_id=oidc-client&response_type=code&scope=read&redirect_uri=https://www.baidu.com

  • response_type=code 表示当前是授权码模式     
  •  client_id、scope这两个请求参数要和客户端往授权服务器注册信息对应上,如果客户端注册时没有profile openid中的某一个,那么授权服务器就不会返回code
  • redirect_uri 重定向地址,也就是用户授权之后 code应该发给谁,目前我们还没有编写客户端的接口,所以这里就直接写百度,然后从浏览器拿code进行测试

 拿到授权码

申请token端点/oauth/token ,别忘记填写客户端信息

 

刷新token 

访问测试客户端模式

客户端模式高度信任,所以很简单,直接输入客户端信息和授权类型即可

相关文章:

Spring Authorization Server OAuth2.1

Spring Authorization Server介绍 Spring Authorization Server 是一个框架&#xff0c;它提供了 OAuth 2.1 和 OpenID Connect 1.0 规范以及其他相关规范的实现。 它建立在 Spring Security 之上&#xff0c;为构建 OpenID Connect 1.0 身份提供者和 OAuth2 授权服务器产品提供…...

解决”重复文件名重命名“问题【根据Word系统方式】

提示&#xff1a;工作中遇到的功能需求&#xff0c;在此记录&#xff0c;不喜勿喷&#xff01;谢谢 文章目录 前言一、需求分析二、需求实现 前言 最近工作中遇到的我认为有必要记录的需求实现&#xff0c;希望可以帮助到有同样需求的小伙伴们&#xff01; 提示&#xff1a;以…...

【PyTorch】PyTorch Geometric(PyG)安装指南:如何高效配置图神经网络环境

目录 引言一、通过 Anaconda 安装二、通过 PyPi 安装三、从 Wheels 安装四、从 ROCm 安装五、从源代码安装5.1 确保 CUDA 环境设置正确5.1.1 检查 PyTorch 是否支持 CUDA5.1.2 设置 CUDA 环境变量5.1.3 验证 nvcc 是否可用 5.2 安装 PyTorch Geometric 所需软件包5.3 强制重新安…...

SolidWorks21装配体中一个零件无法改为线架图

右键零件弹出栏中选择零部件显示改为默认显示&#xff0c;再切换线架图&#xff0c;就会发现整个装配体都能切换为线架图了&#xff01;...

11.11机器学习_介绍和定义

一、 机器学习介绍与定义 1. 机器学习定义 机器学习&#xff08;Machine Learning&#xff09;本质上就是让计算机自己在数据中学习规律&#xff0c;并根据所得到的规律对未来数据进行预测。 机器学习包括如聚类、分类、决策树、贝叶斯、神经网络、深度学习&#xff08;Deep…...

【代码审计】常见漏洞专项审计-业务逻辑漏洞审计

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 0x01 漏洞介绍 1、 原理 业务逻辑漏洞是一类特殊的安全漏洞&#xff0c;业务逻辑漏洞属于设计漏洞而非实…...

SpringBoot单体服务无感更新启动,动态检测端口号并动态更新

SpringBoot单体服务无感更新启动 package com.basaltic.warn;import cn.hutool.core.io.IoUtil; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplic…...

CSS基础知识04

文本溢出通常是指在限定的空间内不能容纳所输入的文字&#xff0c;导致文字超出了容器的边界 一、文本溢出 1.1.css属性处理 所用到的属性 属性属性值overflowvisible&#xff1a;默认值&#xff0c;内容不会被修剪&#xff0c;会呈现在元素框之外。hidden&#xff1a;内容会…...

python程序对服务器cpu和内存资源占用的管理。

背景 在服务器上部署了一套目标检测的程序&#xff0c;做成while true 的轮询检测数据更新的定时任务。 结果没想到那台服务器还有一套可视化程序要给领导演示看&#xff0c;结果演示的时候平台各种报错。 然后通过top查看了一下资源利用率发现python的程序cpu 130。&#xf…...

java算法性能调优:详尽探讨时间复杂度与空间复杂度的分析与优化“

接下来我将带领大家进入Java数据结构的深入学习&#xff0c;让我们一同享受Java数据结构中的奥秘。 一、引言 二、时间复杂度 三、空间复杂度 四、Java中的时间复杂度和空间复杂度 五、优化时间复杂度和空间复杂度 七、时间复杂度和空间复杂度的重要性 一&#xff1a;时间…...

人工智能:塑造未来的工作与生活

目录 人工智能技术的应用前景与影响 人工智能的历史与现状 人工智能的应用领域 人工智能的前景与挑战 个人视角&#xff1a;人工智能的应用前景与未来 人工智能在生活中的潜力 面对人工智能带来的挑战 我的观点与建议 结语 人工智能技术的应用前景与影响 随着人工智能…...

RK3568笔记六十九: 事件回调处理之Libevent 简单使用

若该文为原创文章,转载请注明原文出处。 一、前言 在项目开发过程中,事件处理使用相当多,特别是在UI处理的过程中,UI不能在非UI程里直接操作,否则会出现内存等异常,即不能在子线程里操作UI,所以用事件消息的方式通知UI线程刷新UI界面,在这一细节上掉了好多次坑。 Lib…...

MySQL如何解决幻读?

目录 一、什么是幻读&#xff1f; 1.1 幻读的定义 1.2 幻读的示例 1.3 幻读产生的原因&#xff1f; 1.4 读已提交&#xff08;Read Committed&#xff09; 1.4.1 确定事务等级 1.4.2 非锁定读取 准备 示例 结论 1.4.3 锁定读取 准备 示例 分析 结论 1.5 可重复读…...

Javascript_设计模式(二)

什么是迭代器模式?一般用在什么场景? 迭代器模式是一种行为型设计模式&#xff0c;它用于提供一种顺序访问聚合对象中各个元素的方法&#xff0c;而又不暴露该对象的内部表示。通过使用迭代器模式&#xff0c;可以遍历一个聚合对象&#xff0c;而无需关心该对象的内部结构和…...

时间同步服务器

1、时间同步服务&#xff1a;在多台主机协作时&#xff0c;确保时间同步&#xff0c;防止时间不一致造成的故障。 2、时间按同步实现&#xff1a; ntp 、chrony 3、命令&#xff1a;timedatectl timedatectl set-time "2024-02-13 10:41:55" timedatect…...

react+hook+vite项目使用eletron打包成桌面应用+可以热更新

使用Hooks-Admin的架构 Hooks-Admin: &#x1f680;&#x1f680;&#x1f680; Hooks Admin&#xff0c;基于 React18、React-Router V6、React-Hooks、Redux、TypeScript、Vite2、Ant-Design 开源的一套后台管理框架。https://gitee.com/HalseySpicy/Hooks-Adminexe桌面应用…...

STM32 ADC --- DMA乒乓缓存

STM32 ADC — DMA乒乓缓存 文章目录 STM32 ADC --- DMA乒乓缓存软件切换实现乒乓利用DMA双缓冲实现乒乓 通过cubeMX配置生成HAL工程这里使用的是上篇文章&#xff08;STM32 ADC — DMA采样&#xff09;中生成的工程配置 软件切换实现乒乓 cubeMX默认生成的工程中是打开DMA中断…...

SpringCloud基础 入门级 学习SpringCloud 超详细(简单通俗易懂)

Spring Cloud 基础入门级学习 超详细&#xff08;简单通俗易懂&#xff09; 一、SpringCloud核心组件第一代&#xff1a;SpringCloud Netflix组件第二代&#xff1a;SpringCloud Alibaba组件SpringCloud原生组件 二、SpringCloud体系架构图三、理解分布式与集群分布式集群 四、…...

【Windows 常用工具系列 20 -- MobaXterm 登录 WSL】

文章目录 MobaXterm 登录 WSL MobaXterm 登录 WSL 在 WSL 启动之后&#xff0c;打开 MobaXterm&#xff1a; 在 Distribution 中选择自己本地安装的 ubuntu 版本&#xff0c;我这里使用的是ubuntu-20.4&#xff0c;然后在 runmethod 中选择 Localhost connection. 连接成功之…...

【vmware+ubuntu16.04】ROS学习_博物馆仿真克隆ROS-Academy-for-Beginners软件包处理依赖报错问题

首先安装git 进入终端&#xff0c;输入sudo apt-get install git 安装后&#xff0c;创建一个工作空间名为tutorial_ws&#xff0c; 输入 mkdir tutorial_ws#创建工作空间 cd tutorial_ws#进入 mkdir src cd src git clone https://github.com/DroidAITech/ROS-Academy-for-Be…...

UniApp的Vue3版本中H5配置代理的最佳方法

UniApp的Vue3版本中H5项目在本地开发时需要配置跨域请求调试 最开始在 manifest.json中配置 总是报404&#xff0c;无法通过代理请求远程的接口并返回404错误。 经过验证在项目根目录创建 vite.config.js文件 vite.config.js内容: // vite.config.js import {defineConfig }…...

深入了解Pod

Pod是Kubernetes中最小的单元,它由一组、一个或多个容器组成,每个Pod还包含了一个Pause容器,Pause容器是Pod的父容器,主要负责僵尸进程的回收管理,通过Pause容器可以使同一个Pod里面的多个容器共享存储、网络、PID、IPC等。 1、Pod 是由一组紧耦合的容器组成的容器组,当然…...

基于Spider异步爬虫框架+JS动态参数逆向+隧道代理+自定义中间件的猎聘招聘数据爬取

在本篇博客中&#xff0c;我们将介绍如何使用 Scrapy 框架结合 JS 逆向技术、代理服务器和自定义中间件&#xff0c;来爬取猎聘网站的招聘数据。猎聘是一个国内知名的招聘平台&#xff0c;提供了大量的企业招聘信息和职位信息。本项目的目标是抓取指定城市的招聘信息&#xff0…...

Spring 中的 BeanDefinitionParserDelegate 和 NamespaceHandler

一、BeanDefinitionParserDelegate Spring在解析xml文件的时候&#xff0c;在遇到<bean>标签的时候&#xff0c;我们会使用BeanDefinitionParserDelegate对象类解析<bean>标签的内容&#xff0c;包括<bean>标签的多个属性&#xff0c;例如 id name class in…...

BERT模型核心组件详解及其实现

摘要 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种基于Transformer架构的预训练模型&#xff0c;在自然语言处理领域取得了显著的成果。本文详细介绍了BERT模型中的几个关键组件及其实现&#xff0c;包括激活函数、变量初始化…...

图论-代码随想录刷题记录[JAVA]

文章目录 前言深度优先搜索理论基础所有可达路径岛屿数量岛屿最大面积孤岛的总面积沉默孤岛Floyd 算法dijkstra&#xff08;朴素版&#xff09;最小生成树之primkruskal算法 前言 新手小白记录第一次刷代码随想录 1.自用 抽取精简的解题思路 方便复盘 2.代码尽量多加注释 3.记录…...

c#加载shellcode

本地加载bin文件 SharpPELoader项目如下&#xff1a; using System; using System.IO; using System.Runtime.InteropServices;namespace TestShellCode {internal class Program{private const uint MEM_COMMIT 0x1000;private const uint PAGE_EXECUTE_READWRITE 0x40;pr…...

HarmonyOS 开发环境搭建

HarmonyOS&#xff08;鸿蒙操作系统&#xff09;作为一种面向全场景多设备的智能操作系统&#xff0c;正逐渐在市场上崭露头角。为了进入HarmonyOS生态&#xff0c;开发者需要搭建一个高效的开发环境。本文将详细介绍如何搭建HarmonyOS开发环境&#xff0c;特别是如何安装和配置…...

【网络云计算】2024第46周周考-磁盘管理的基础知识-RAID篇

文章目录 1、画出各个RAID的结构图&#xff0c;6句话说明优点和缺点&#xff0c;以及磁盘可用率和坏盘数量&#xff0c;磁盘总的数量2、写出TCP五层模型以及对应的常用协议 【网络云计算】2024第46周周考-磁盘管理的基础知识-RAID篇 1、画出各个RAID的结构图&#xff0c;6句话说…...

深入理解 SQL_MODE 之 ANSI_QUOTES

引言 在 MySQL 数据库中&#xff0c;sql_mode 是一个重要的配置参数&#xff0c;它定义了 MySQL 应该遵循的 SQL 语法标准以及数据验证规则。其中&#xff0c;ANSI_QUOTES 是 sql_mode 中的一个重要选项&#xff0c;它改变了 MySQL 对于字符串和标识符的识别方式&#xff0c;使…...