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 

访问测试客户端模式

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/60720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 优先算法 —— 三数之和(双指针)

目录 题目&#xff1a;三数之和 1. 题目解析 2. 算法原理 ①. 暴力枚举 ②. 双指针算法 不漏的处理&#xff1a; 去重处理&#xff1a; 固定一个数 a 的优化&#xff1a; 3. 代码实现 Ⅰ. 暴力枚举&#xff08;会超时 O&#xff08;N&#xff09;&#xff09; Ⅱ.…

(三十三)队列(queue)

文章目录 1. 队列&#xff08;queue&#xff09;1.1 定义1.2 函数1.3 习题1.3.1 例题&#xff08;周末舞会&#xff09; 2. 双向队列&#xff08;deque&#xff09;2.1 定义2.2 函数2.3 题目2.3.1 例题&#xff08;打BOSS&#xff09; 1. 队列&#xff08;queue&#xff09; 队…

《FreeRTOS任务基础知识以及任务创建相关函数》

目录 1.FreeRTOS多任务系统与传统单片机单任务系统的区别 2.FreeRTOS中的任务&#xff08;Task&#xff09;介绍 2.1 任务特性 2.2 FreeRTOS中的任务状态 2.3 FreeRTOS中的任务优先级 2.4 在任务函数中退出 2.5 任务控制块和任务堆栈 2.5.1 任务控制块 2.5.2 任务堆栈…

springboot的社区团购系统设计录像

springboot的社区团购系统设计录像 springboot的社区团购系统设计

力扣每日一题

行变成回文&#xff1a; 对于每一行&#xff0c;遍历前半部分的元素&#xff0c;与后半部分的元素比较。如果不相等&#xff0c;计数器加 1&#xff0c;表示需要翻转一次。 列变成回文&#xff1a; 将矩阵转置&#xff0c;使用与行类似的方式对每一列进行统计。可以使用 Python…

linux c 语言回调函数学习

动机 最近在看 IO多路复用&#xff0c;包括 select() poll () epoll() 的原理以及libevent&#xff0c; 对里面提及的回调机制 比较头大&#xff0c;特写此文用例记录学习笔记。 什么是回调函数 网上看到的最多的一句话便是&#xff1a;回调函数 就是 函数指针的一种用法&am…

游戏引擎学习第九天

视频参考:https://www.bilibili.com/video/BV1ouUPYAErK/ 修改之前的方波数据&#xff0c;改播放正弦波 下面主要讲关于浮点数 1. char&#xff08;字符类型&#xff09; 大小&#xff1a;1 字节&#xff08;8 位&#xff09;表示方式&#xff1a;char 存储的是一个字符的 A…

# Python IDE的介绍和选择 --- 《跟着小王学Python》

Python IDE的介绍和选择 — 《跟着小王学Python》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌握Python的核心概念…

柯桥生活英语口语学习“面坨了”英语怎么表达?

“面坨了”英语怎么表达&#xff1f; 要想搞清楚这个表达&#xff0c;首先&#xff0c;我们要搞明白“坨”是啥意思&#xff1f; 所谓“坨”就是指&#xff0c;面条在汤里泡太久&#xff0c;从而变涨&#xff0c;黏糊凝固在一起的状态。 有一个词汇&#xff0c;很适合用来表达这…

ZeroSSL HTTPS SSL证书ACMESSL申请3个月证书

目录 一、引言 二、准备工作 三、申请 SSL 证书 四、证书选型 五、ssl重要性 一、引言 目前免费 Lets Encrypt、ZeroSSL、BuyPass、Google Public CA SSL 证书&#xff0c;一般免费3-6个月。从申请难易程度分析&#xff0c;zerossl申请相对快速和简单&#xff0c;亲测速度非…

Java连接MySQL(测试build path功能)

Java连接MySQL&#xff08;测试build path功能&#xff09; 实验说明下载MySQL的驱动jar包连接测试的Java代码 实验说明 要测试该情况&#xff0c;需要先安装好MySQL的环境&#xff0c;其实也可以通过测试最后提示的输出来判断build path是否成功&#xff0c;因为如果不成功会直…

第四节-OSI-网络层

数据链路层&#xff1a;二层--MAC地址精确定位 Ethernet 2&#xff1a; 报头长度&#xff1a;18B 携带的参数&#xff1a;D MAC /S MAC/TYPE(标识上层协议)/FCS 802.3 报头长度&#xff1a;26B 携带的参数&#xff1a;D MAC/S MAC/LLC(标识上层协议)/SNAP&#xff08;标识…

labview实现功能性全局变量

在日常的项目中&#xff0c;笔者最长使用的就是全局变量&#xff0c;这样用起来不仅省心省力&#xff0c;而且传值也很方便&#xff0c;没有什么阻碍&#xff0c;想要传什么数据一根线拉过去就可以了。后面才知道如果一直使用全局变量会导致读写卡死的状态&#xff0c;而且还有…

网络安全之SQLMAP _DNS注入配置方法

网上针对sqlmap进行dns注入的相关文章太少&#xff0c;只是简单介绍了下–dns-domain参数&#xff0c;相关的实战文章要么就模糊或者一笔带过&#xff0c;。然后参考网上的方法重新整理了一遍&#xff0c;简单理解。 需要准备的东西&#xff0c;sqlmap、windows盲注一个、两个…

pycharm快速更换虚拟环境

目录 1. 选择Conda 虚拟环境2. 创建环境3. 直接选择现有虚拟环境 1. 选择Conda 虚拟环境 2. 创建环境 3. 直接选择现有虚拟环境

联想“喜新厌旧”

科技新知 原创作者丨萧维 编辑丨蕨影 十月份&#xff0c;联想很忙。 先是2024联想科技创新大会15日在美国华盛顿州西雅图举行&#xff0c;联想大秀了一下自己在人工智能领域的创新产品、技术和解决方案&#xff0c;英特尔、AMD、英伟达三巨头更同时为其站台&#xff1b;后是与…

[白月黑羽]关于仿写类postman功能软件题目的解答

原题&#xff1a; 答&#xff1a; python文件如下 from PySide6.QtWidgets import QApplication, QMessageBox,QTableWidgetItem,QHeaderView,QWidget,QTableWidget from PySide6.QtCore import QEvent,QObject from PySide6.QtUiTools import QUiLoader import time import …

零基础Java第十八期:图书管理系统

目录 一、package book 1.1. Book 1.2. BookList 二、package user 2.1. User 2.2. NormalUser与AdminiUser 三、Main 四、NormalUser与AdminiUser的菜单界面 五、package operation 5.1. 设计管理员菜单 六、业务逻辑 七、完整代码 今天博主来带大家实现一个…

系统架构师考试极限18天备考复盘(2024年11月)

前言 写下这篇复盘笔记的时候还没有出成绩。目前泽崽还是在读研究生&#xff0c;在经过 大概2周多个全日 的极限备考之后&#xff0c;于11月10日参加了软考的系统架构师考试&#xff08;高级&#xff09;。目前对于“基础知识-案例分析-论文”的估分预期大概是&#xff1a;55-…

Unity肢体控制(关节控制)

前面的基础搭建网上自己搜&#xff0c;我这个任务模型网上也有&#xff0c;可以去官网看看更多模型&#xff0c;这里只讲述有模型如何驱动肢体的操作方式 第一步&#xff1a;创建脚本 第二步&#xff1a;创建Rig Builder 建空容器 加部件&#xff08;Rig&#xff09;,加了之后…