0


spring-authorization-server (1.1.1)自定义认证

maven版本(有部分没有改)
gradle版本(全部最新修改)

前言

注意:我本地没有生成公钥和私钥,所以每次启动项目jwkSource都会重新生成,导致之前认证的token都会失效,具体如何生成私钥和公钥以及怎么配置到授权服务器中,网上有很多方法自行实现即可

之前有个项目用的0.0.3的,正好最近想研究研究,所以就去了官网看文档研究了一下,1.1.1基于的事security6.x的版本, security6与5.7之前的版本有很大的差别,废话不多说,直接上代码(代码中也有一些注释)

最基础的配置官网都有,这里不去体现,主要体现功能:

  1. 自定义认证和授权
  2. 自定义端点拦截器
  3. 持久化到数据库

版本

依赖项版本springboot3.1.2spring-authorization-server1.1.1jdk17dynamic-datasource-spring-boot3-starter4.1.2mybatis-plus3.5.3

sql文件

在这里插入图片描述

代码实操

目录结构

在这里插入图片描述

pom文件

需要额外引入spring security cas包原因是启动时(logging等级:org.springframework.security: trace)会报错:java.lang.ClassNotFoundException:org.springframework.security.cas.jackson2.CasJackson2Module错误。

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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.2</version><relativePath/><!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>demo</description><properties><java.version>17</java.version></properties><dependencyManagement><!--        <dependencies>--><!--            &lt;!&ndash; SpringBoot的依赖配置&ndash;&gt;--><!--            <dependency>--><!--                <groupId>org.springframework.boot</groupId>--><!--                <artifactId>spring-boot-dependencies</artifactId>--><!--                <version>2.5.14</version>--><!--                <type>pom</type>--><!--                <scope>import</scope>--><!--            </dependency>--><!--        </dependencies>--></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-authorization-server</artifactId></dependency><!-- 添加spring security cas支持
         这里需添加spring-security-cas依赖,
         否则启动时报java.lang.ClassNotFoundException: org.springframework.security.cas.jackson2.CasJackson2Module错误。
         --><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-cas</artifactId></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.34</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3</version></dependency><!-- 阿里数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><!-- SpringBoot Web容器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot3-starter</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></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>

授权服务器配置(包名:authenticationServer )

CustomAuthorizationServerConfiguration

importcom.example.demo.config.security.provider.WeChatMiniAppAuthenticationProvider;importcom.example.demo.config.security.provider.converter.WeChatMiniAppAuthenticationConverter;importcom.example.demo.utils.OAuth2ConfigurerUtils;importcom.nimbusds.jose.jwk.source.JWKSource;importcom.nimbusds.jose.proc.SecurityContext;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;importorg.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;importorg.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;importorg.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;importorg.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;importorg.springframework.security.web.util.matcher.RequestMatcher;publicclassCustomAuthorizationServerConfiguration{publicstaticvoidapplyDefaultSecurity(HttpSecurity http,JWKSource<SecurityContext> jwkSource)throwsException{OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =newOAuth2AuthorizationServerConfigurer();OAuth2AuthorizationService authorizationService =OAuth2ConfigurerUtils.getBean(http,OAuth2AuthorizationService.class);// 认证过滤器链
        authorizationServerConfigurer.tokenEndpoint(oAuth2TokenEndpointConfigurer ->{
            oAuth2TokenEndpointConfigurer.accessTokenRequestConverters( customJwtAuthenticationToken ->{
                customJwtAuthenticationToken.add(newOAuth2AuthorizationCodeAuthenticationConverter());
                customJwtAuthenticationToken.add(newOAuth2RefreshTokenAuthenticationConverter());
                customJwtAuthenticationToken.add(newOAuth2ClientCredentialsAuthenticationConverter());
                customJwtAuthenticationToken.add(newWeChatMiniAppAuthenticationConverter());})// 返回accessToken的后置处理器 https://docs.spring.io/spring-authorization-server/docs/current/reference/html/protocol-endpoints.html#oauth2-token-endpoint//            .accessTokenResponseHandler()// 异常返回处理器//            .errorResponseHandler().authenticationProviders((customProviders)->{// 自定义认证提供者
                customProviders.add(newWeChatMiniAppAuthenticationProvider(jwkSource,authorizationService));});});// 端点匹配器RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
        http.securityMatcher(endpointsMatcher)// .authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())// csrf.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher)).apply(authorizationServerConfigurer);}}

认证提供者(包名:provider)

WeChatMiniAppAuthenticationProvider

后来在进一步研究的时候,发现还有另一种比较好的实现方式,比下面的要简单,代码我就不上传了,有兴趣可以看看官网
传送门

packagecom.example.demo.config.security.provider;importcom.alibaba.fastjson2.JSONObject;importcom.example.demo.domain.dto.UserDto;importcom.example.demo.config.security.provider.token.WeChatMiniAppAuthenticationToken;importcom.example.demo.utils.JwtUtils;importcom.nimbusds.jose.jwk.source.JWKSource;importcom.nimbusds.jose.proc.SecurityContext;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.authentication.AnonymousAuthenticationToken;importorg.springframework.security.authentication.AuthenticationProvider;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.core.authority.AuthorityUtils;importorg.springframework.security.crypto.keygen.Base64StringKeyGenerator;importorg.springframework.security.crypto.keygen.StringKeyGenerator;importorg.springframework.security.oauth2.core.*;importorg.springframework.security.oauth2.jwt.*;importorg.springframework.security.oauth2.server.authorization.OAuth2Authorization;importorg.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;importorg.springframework.security.oauth2.server.authorization.OAuth2TokenType;importorg.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;importorg.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;importorg.springframework.security.oauth2.server.authorization.client.RegisteredClient;importorg.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;importorg.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;importjava.security.Principal;importjava.time.Duration;importjava.time.Instant;importjava.util.Base64;importjava.util.Objects;importjava.util.Set;importjava.util.function.Supplier;importstaticcom.example.demo.config.security.provider.type.AuthorizationGrantTypes.WECHAT_MINIAPP;/**
 * 微信小程序
 */publicfinalclassWeChatMiniAppAuthenticationProviderimplementsAuthenticationProvider{publicstaticfinalStringROLE_MOBILE="ROLE_MOBILE";privatefinalLogger                              logger          =LoggerFactory.getLogger(WeChatMiniAppAuthenticationProvider.class);privateAuthorizationServerSettings authorizationServerSettings;@Autowired(required =false)voidsetAuthorizationServerSettings(AuthorizationServerSettings authorizationServerSettings){this.authorizationServerSettings = authorizationServerSettings;}JWKSource<SecurityContext> jwkSource;privatefinalJwtEncoder jwtEncoder;publicWeChatMiniAppAuthenticationProvider(JWKSource<SecurityContext> jwkSource,OAuth2AuthorizationService authorizationService){this.jwkSource = jwkSource;
        jwtEncoder =newNimbusJwtEncoder(jwkSource);this.authorizationService = authorizationService;}privatefinalOAuth2AuthorizationService authorizationService;@OverridepublicAuthenticationauthenticate(Authentication authentication)throwsAuthenticationException{// 转为微信小程序认证tokenWeChatMiniAppAuthenticationToken authenticationToken =(WeChatMiniAppAuthenticationToken) authentication;// 获取经过身份验证的客户端否则抛出无效的客户端OAuth2ClientAuthenticationToken clientPrincipal =getAuthenticatedClientElseThrowInvalidClient(
                authenticationToken);RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
        logger.info("小程序认证 authenticate -> authenticationToken:{}",authenticationToken);
        logger.info("小程序认证 clientPrincipal -> clientPrincipal:{}",clientPrincipal);JwsHeader.Builder headersBuilder =JwtUtils.headers();Set<String> authorizedScopes =null;if(registeredClient !=null){
            authorizedScopes = registeredClient.getScopes();}// 发行者 保持与客户端一致String issuer =this.authorizationServerSettings !=null?this.authorizationServerSettings.getIssuer():null;JwtClaimsSet.Builder claimsBuilder =JwtUtils.accessTokenClaims(Objects.requireNonNull(registeredClient), issuer, clientPrincipal.getName(),
                authorizedScopes);// @formatter:offJwtEncodingContext context =JwtEncodingContext.with(headersBuilder, claimsBuilder)//注册客户端.registeredClient(registeredClient)//资源所有者.principal(clientPrincipal)// 授权范围.authorizedScopes(authorizedScopes)//token类型.tokenType(OAuth2TokenType.ACCESS_TOKEN)// 微信小程序.authorizationGrantType(WECHAT_MINIAPP)//设置代表授权许可.authorizationGrant(authenticationToken).build();// @formatter:onJwsHeader headers = context.getJwsHeader().build();JwtClaimsSet.Builder claims = context.getClaims();//@formatter:offUserDto data =newUserDto().setId("123456").setHosId("147258");//@formatter:on
        claims.claim("data",JSONObject.toJSONString(data));// JWT 定制器//        this.jwtCustomizer.customize(context);// JWT编码器Jwt jwtAccessToken =this.jwtEncoder.encode(JwtEncoderParameters.from(headers, claims.build()));// access tokenOAuth2AccessToken accessToken =newOAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwtAccessToken.getTokenValue(),
                jwtAccessToken.getIssuedAt(), jwtAccessToken.getExpiresAt(),
                authorizedScopes);OAuth2RefreshToken refreshToken =null;if(registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)&&!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)){
            refreshToken =generateRefreshToken(
                    registeredClient.getTokenSettings().getRefreshTokenTimeToLive());}OAuth2Authorization.Builder authorizationBuilder =OAuth2Authorization.withRegisteredClient(registeredClient).principalName(clientPrincipal.getName()).authorizationGrantType(WECHAT_MINIAPP).authorizedScopes(authorizedScopes).token(accessToken,(metadata)-> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
                                jwtAccessToken.getClaims())).attribute(Principal.class.getName(),newAnonymousAuthenticationToken(clientPrincipal.getName(),
                                clientPrincipal.getPrincipal(),AuthorityUtils.createAuthorityList(ROLE_MOBILE)));if(refreshToken !=null){
            authorizationBuilder.refreshToken(refreshToken);}OAuth2Authorization authorization = authorizationBuilder.build();// @formatter:on// 保存入库this.authorizationService.save(authorization);// 构造返回returnnewOAuth2AccessTokenAuthenticationToken(registeredClient,
                authentication, accessToken, refreshToken);// todo//        throw new OAuth2AuthenticationException(OAuth2ErrorCodes.SERVER_ERROR);}@Overridepublicbooleansupports(Class<?> authentication){returnWeChatMiniAppAuthenticationToken.class.isAssignableFrom(authentication);}privatestaticfinalStringKeyGeneratorDEFAULT_REFRESH_TOKEN_GENERATOR=newBase64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(),96);privatefinalSupplier<String>                refreshTokenGenerator           =DEFAULT_REFRESH_TOKEN_GENERATOR::generateKey;privateOAuth2RefreshTokengenerateRefreshToken(Duration tokenTimeToLive){Instant issuedAt =Instant.now();Instant expiresAt = issuedAt.plus(tokenTimeToLive);returnnewOAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);}staticOAuth2ClientAuthenticationTokengetAuthenticatedClientElseThrowInvalidClient(Authentication authentication){OAuth2ClientAuthenticationToken clientPrincipal =null;if(OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())){
            clientPrincipal =(OAuth2ClientAuthenticationToken)authentication.getPrincipal();}if(clientPrincipal !=null&& clientPrincipal.isAuthenticated()){return clientPrincipal;}else{thrownewOAuth2AuthenticationException("invalid_client");}}}

预处理(包名: Converter)

WeChatMiniAppAuthenticationConverter

可以说是预处理类转换token信息

importcom.example.demo.config.security.provider.token.WeChatMiniAppAuthenticationToken;importcom.example.demo.config.security.provider.type.AuthorizationGrantTypes;importjakarta.servlet.http.HttpServletRequest;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.context.SecurityContextHolder;importorg.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;importorg.springframework.security.web.authentication.AuthenticationConverter;importorg.springframework.util.LinkedMultiValueMap;importorg.springframework.util.MultiValueMap;importjava.util.LinkedHashMap;importjava.util.Map;publicfinalclassWeChatMiniAppAuthenticationConverterimplementsAuthenticationConverter{privatefinalLogger logger =LoggerFactory.getLogger(WeChatMiniAppAuthenticationConverter.class);/**
     * 参数对象
     *
     * @param request {@link HttpServletRequest}
     * @return {@link Authentication}
     */@OverridepublicAuthenticationconvert(HttpServletRequest request){String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);if(!AuthorizationGrantTypes.WECHAT_MINIAPP.getValue().equals(grantType)){returnnull;}// 获取参数MultiValueMap<String,String> parameters =getParameters(request);
        logger.info("微信小程序授权入参:{}", parameters);// 微信CODE// 其他参数Map<String,Object> additionalParameters =getOtherParameters(parameters);// clientPrincipalAuthentication clientPrincipal =SecurityContextHolder.getContext().getAuthentication();

        logger.info("小程序授权全部参数:{}", additionalParameters);returnnewWeChatMiniAppAuthenticationToken(clientPrincipal, additionalParameters,"code","appid","encryptedData","ivStr");}/**
     * 获取其他参数
     *
     * @param parameters {@link MultiValueMap}
     * @return {@link Map}
     */privateMap<String,Object>getOtherParameters(MultiValueMap<String,String> parameters){Map<String,Object> additionalParameters =newLinkedHashMap<>(16);
        parameters.forEach((key, value)->{if(!key.equals(OAuth2ParameterNames.GRANT_TYPE)&&!key.equals(OAuth2ParameterNames.SCOPE)){
                additionalParameters.put(key, value.get(0));}});return additionalParameters;}staticMultiValueMap<String,String>getParameters(HttpServletRequest request){Map<String,String[]> parameterMap = request.getParameterMap();MultiValueMap<String,String> parameters =newLinkedMultiValueMap<>(parameterMap.size());
        parameterMap.forEach((key, values)->{for(String value : values){
                parameters.add(key, value);}});return parameters;}}

自定义token (包名: token)

WeChatMiniAppAuthenticationToken
@Setter@GetterpublicclassWeChatMiniAppAuthenticationTokenextendsOAuth2AuthorizationGrantAuthenticationToken{/**
     * appId 应用ID
     */privatefinalString appId;/**
     * code 小程序CODE
     */privatefinalString code;privatefinalString encryptedData;privatefinalString ivStr;/**
     * Sub-class constructor.
     * @param clientPrincipal        the authenticated client principal
     * @param additionalParameters   the additional parameters
     * @param code {@link String } 小程序code
     * @param appId {@link String } 平台appid
     * @param encryptedData {@link String } encryptedData
     * @param ivStr {@link String } ivStr
     */publicWeChatMiniAppAuthenticationToken(Authentication clientPrincipal,Map<String,Object> additionalParameters,String code,String appId,String encryptedData,String ivStr){super(AuthorizationGrantTypes.WECHAT_MINIAPP, clientPrincipal, additionalParameters);this.code = code;this.appId = appId;this.encryptedData = encryptedData;this.ivStr = ivStr;}}

定义认证type (包名:type)

AuthorizationGrantTypes

人获取access_token时,会用到grantType

publicclassAuthorizationGrantTypes{publicstaticfinalAuthorizationGrantTypeWECHAT_MINIAPP=newAuthorizationGrantType("wechat_miniapp");}

资源服务器 (包名:resourceServer)

CustomAuthenticationTokenConverter

自定义jwt 预处理器

publicclassCustomAuthenticationTokenimplementsConverter<Jwt,AbstractAuthenticationToken>{privatefinalConverter<Jwt,Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter =newJwtGrantedAuthoritiesConverter();privatestaticfinalStringPRINCIPAL_CLAIM_NAME="data";privatefinalLogger logger =LoggerFactory.getLogger(CustomAuthenticationToken.class);publicCustomAuthenticationToken(){}publicAbstractAuthenticationTokenconvert(@NonNullJwt jwt){this.logger.info("convert ->jwt:{}",JSONObject.toJSONString(jwt));Collection<GrantedAuthority> authorities =this.extractAuthorities(jwt);// 获取个性化的token信息String principalClaimValue = jwt.getClaimAsString(PRINCIPAL_CLAIM_NAME);if(principalClaimValue ==null){returnnewJwtAuthenticationToken(jwt, authorities);}else{UserDto user =this.extractUserInfo(jwt);
            user.setToken(jwt.getTokenValue());CustomJwtAuthenticationToken jwtAuthenticationToken =newCustomJwtAuthenticationToken(jwt, user, authorities);SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);return jwtAuthenticationToken;}}protectedCollection<GrantedAuthority>extractAuthorities(Jwt jwt){returnthis.jwtGrantedAuthoritiesConverter.convert(jwt);}protectedUserDtoextractUserInfo(Jwt jwt){String principalClaimValue = jwt.getClaimAsString("data");returnJSONObject.parseObject(principalClaimValue,UserDto.class);}}

CustomJwtAuthenticationToken

自定义的jwt

@TransientpublicclassCustomJwtAuthenticationTokenextendsAbstractOAuth2TokenAuthenticationToken<Jwt>{privatestaticfinallong serialVersionUID =560L;privatefinalString name;publicCustomJwtAuthenticationToken(Jwt jwt,Object principal,Collection<?extendsGrantedAuthority> authorities){super(jwt, principal,"", authorities);this.setAuthenticated(true);this.name = jwt.getSubject();}publicMap<String,Object>getTokenAttributes(){return((Jwt)this.getToken()).getClaims();}publicStringgetName(){returnthis.name;}}

CustomOauth2AuthenticationEntryPoint

自定义协议端点

publicclassCustomOauth2AuthenticationEntryPointimplementsAuthenticationEntryPoint{privatestaticfinalLogger logger =LoggerFactory.getLogger(CustomOauth2AuthenticationEntryPoint.class);privateString realmName;publicCustomOauth2AuthenticationEntryPoint(){}publicvoidcommence(HttpServletRequest request,HttpServletResponse response,AuthenticationException e)throwsIOException{
        logger.error(e.getLocalizedMessage(), e);HttpStatus status =HttpStatus.UNAUTHORIZED;Map<String,String> parameters =newLinkedHashMap<>();if(Objects.nonNull(this.realmName)){
            parameters.put("realm",this.realmName);}if(e instanceofOAuth2AuthenticationException oAuth2AuthenticationException){OAuth2Error error = oAuth2AuthenticationException.getError();
            parameters.put("error", error.getErrorCode());if(StringUtils.hasText(error.getDescription())){String errorMessage = error.getDescription();
                parameters.put("error_description", errorMessage);}if(StringUtils.hasText(error.getUri())){
                parameters.put("error_uri", error.getUri());}if(error instanceofBearerTokenError bearerTokenError){if(StringUtils.hasText(bearerTokenError.getScope())){
                    parameters.put("scope", bearerTokenError.getScope());}

                status =((BearerTokenError)error).getHttpStatus();}}ResponseEntity<String> unauthenticated =newResponseEntity<String>("Unauthenticated",HttpStatusCode.valueOf(status.value()));String message =JSON.toJSONString(unauthenticated);String wwwAuthenticate =WwwAuthenticateHeaderBuilder.computeWwwAuthenticateHeaderValue(parameters);
        response.addHeader("WWW-Authenticate", wwwAuthenticate);
        response.setStatus(status.value());
        response.setContentType("application/json");
        response.getWriter().write(message);}}

WwwAuthenticateHeaderBuilder

publicfinalclassWwwAuthenticateHeaderBuilder{publicWwwAuthenticateHeaderBuilder(){}publicstaticStringcomputeWwwAuthenticateHeaderValue(Map<String,String> parameters){StringJoiner wwwAuthenticate =newStringJoiner(", ","Bearer ","");if(!parameters.isEmpty()){
            parameters.forEach((k, v)->{
                wwwAuthenticate.add(k +"=\""+ v +"\"");});}return wwwAuthenticate.toString();}}

Oauth2ResourceServerConfigurer

资源服务器配置

publicfinalclassOauth2ResourceServerConfigurer{publicOauth2ResourceServerConfigurer(){}publicstaticvoidapplyDefaultSecurity(HttpSecurity http)throwsException{
        http.oauth2ResourceServer((oauth2ResourceServerConfigurer)->
                oauth2ResourceServerConfigurer
                   // 无权限处理器//                 .accessDeniedHandler()// 自定义协议端点.authenticationEntryPoint(newCustomOauth2AuthenticationEntryPoint()).jwt((jwtConfigurer)-> jwtConfigurer.jwtAuthenticationConverter(newCustomAuthenticationToken())));}}

SecurityConfig

实现授权及资源服务器

packagecom.example.demo.config.security;importcom.example.demo.config.security.authenticationServer.CustomAuthorizationServerConfiguration;importcom.example.demo.config.security.resourceServer.Oauth2ResourceServerConfigurer;importcom.example.demo.service.CustomUserDetailsService;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.nimbusds.jose.jwk.JWKSet;importcom.nimbusds.jose.jwk.RSAKey;importcom.nimbusds.jose.jwk.source.ImmutableJWKSet;importcom.nimbusds.jose.jwk.source.JWKSource;importcom.nimbusds.jose.proc.SecurityContext;importlombok.extern.slf4j.Slf4j;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.annotation.Order;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.jackson2.CoreJackson2Module;importorg.springframework.security.jackson2.SecurityJackson2Modules;importorg.springframework.security.oauth2.core.*;importorg.springframework.security.oauth2.core.oidc.OidcScopes;importorg.springframework.security.oauth2.jwt.JwtDecoder;importorg.springframework.security.oauth2.server.authorization.*;importorg.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;importorg.springframework.security.oauth2.server.authorization.client.RegisteredClient;importorg.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;importorg.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;importorg.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;importorg.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;importorg.springframework.security.oauth2.server.authorization.settings.ClientSettings;importorg.springframework.security.oauth2.server.authorization.settings.TokenSettings;importorg.springframework.security.oauth2.server.authorization.token.*;importorg.springframework.security.web.SecurityFilterChain;importjava.security.KeyPair;importjava.security.KeyPairGenerator;importjava.security.interfaces.RSAPrivateKey;importjava.security.interfaces.RSAPublicKey;importjava.time.Duration;importjava.util.UUID;/***
 *
 * @author qb
 * @since 2023/7/21 15:14
 * @version 1.0
 */@Slf4j@Configuration@EnableWebSecuritypublicclassSecurityConfig{// 协议端点过滤器链@Bean@Order(1)publicSecurityFilterChainauthorizationServerSecurityFilterChain(HttpSecurity http)throwsException{CustomAuthorizationServerConfiguration.applyDefaultSecurity(http,jwkSource());//        // 开启oidc connect 1.0//        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());//        // 重定向到未通过身份验证的登录页面 授权终结点//        http.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(//                        new LoginUrlAuthenticationEntryPoint("/login"),//                        new MediaTypeRequestMatcher(MediaType.TEXT_HTML)//                ))//                // 授权服务 接受的用户信息和/或客户端注册的访问令牌//                .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));return http.build();}// 协议认证筛选器@Bean@Order(2)publicSecurityFilterChaindefaultSecurityFilterChain(HttpSecurity http)throwsException{Oauth2ResourceServerConfigurer.applyDefaultSecurity(http);
        http
                .csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(auth ->
                        auth.requestMatchers("/login","/callback","/oauth2/client/**").permitAll().anyRequest().authenticated());return http.build();}// 自定义认证service@BeanpublicUserDetailsServiceuserDetailsService(){returnnewCustomUserDetailsService();}// 密码加密@BeanpublicPasswordEncoderpasswordEncoder(){//        return PasswordEncoderFactories.createDelegatingPasswordEncoder();returnnewBCryptPasswordEncoder();}/**
     * 管理客户端
     * @return /
     */@BeanpublicRegisteredClientRepositoryregisteredClientRepository(JdbcTemplate jdbcTemplate){//        RegisteredClient oidcClient = defaultClient();// 配置模式//        JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);//        if (null == jdbcRegisteredClientRepository.findByClientId("client")) {//            jdbcRegisteredClientRepository.save(oidcClient);//        }returnnewJdbcRegisteredClientRepository(jdbcTemplate);}privatestaticRegisteredClientdefaultClient(){// 方便测试,先注册一个测试客户端RegisteredClient oidcClient =RegisteredClient.withId(UUID.randomUUID().toString()).clientId("client")// 123456.clientSecret("$2a$10$gJUJo9Ad3wDIhBGVH.8/i.Ox82tSCR4.UkbiDWEDUVQnIzcTMPjKK")// 可以基于 basic 的方式和授权服务器进行认证.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)// 授权码.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)// 刷新token.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)// 客户端模式.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)// 密码模式.authorizationGrantType(AuthorizationGrantType.JWT_BEARER)// 重定向url.redirectUri("http://127.0.0.1:9000/callback").postLogoutRedirectUri("http://127.0.0.1:9000/")// 客户端申请的作用域,也可以理解这个客户端申请访问用户的哪些信息,比如:获取用户信息,获取用户照片等.scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).clientSettings(ClientSettings.builder()// 是否需要用户确认一下客户端需要获取用户的哪些权限// 比如:客户端需要获取用户的 用户信息、用户照片 但是此处用户可以控制只给客户端授权获取 用户信息。.requireAuthorizationConsent(true).build()).tokenSettings(TokenSettings.builder()// accessToken 的有效期.accessTokenTimeToLive(Duration.ofHours(1))// refreshToken 的有效期.refreshTokenTimeToLive(Duration.ofDays(3))// 是否可重用刷新令牌.reuseRefreshTokens(true).build()).build();return oidcClient;}/**
     * 自定义授权service
     * @return /
     */@BeanpublicOAuth2AuthorizationServiceauthorizationService(JdbcTemplate jdbcTemplate,RegisteredClientRepository registeredClientRepository){//        JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);//        JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);//        ClassLoader classLoader = JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper.class.getClassLoader();//        ObjectMapper objectMapper = new ObjectMapper();//        objectMapper.registerModules(new CoreJackson2Module());//        objectMapper.registerModules(SecurityJackson2Modules.getModules(classLoader));//        objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());//        rowMapper.setObjectMapper(objectMapper);//        authorizationService.setAuthorizationRowMapper(rowMapper);//        return authorizationService;returnnewJdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);}/**
     * 自定义确认授权 service 配置
     *
     */@BeanpublicOAuth2AuthorizationConsentServiceauthorizationConsentService(JdbcTemplate jdbcTemplate,RegisteredClientRepository registeredClientRepository){returnnewJdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);}/**
     * 签名实例
     * 此处注意,目前重启项目就会导致之前已存在的token失效,需要改为固定私钥和公钥,生成到项目目录下
     * @return /
     */@BeanpublicJWKSource<SecurityContext>jwkSource(){var keyPair =generateRsaKeyPair();// 公钥RSAPublicKey publicKey =(RSAPublicKey)  keyPair.getPublic();RSAPrivateKey privateKey =(RSAPrivateKey) keyPair.getPrivate();RSAKey rsaKey =newRSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();var jwkSet =newJWKSet(rsaKey);returnnewImmutableJWKSet<>(jwkSet);}// 解析JWKSource访问令牌,构建 JwtDecoder 实例@BeanpublicJwtDecoderjwtDecoder(JWKSource<SecurityContext> jwkSource){returnOAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}// 启动时生成的 with 密钥的实例,用于创建上述内容。java.security.KeyPairJWKSourceprivatestaticKeyPairgenerateRsaKeyPair(){KeyPair  keyPair ;try{KeyPairGenerator keyPairGenerator  =KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();}catch(Exception e){thrownewIllegalStateException(e);}return keyPair;}/**
     * 自定义jwt信息,全局(自定义jwt实现例外)
     * @return /
     */@BeanpublicOAuth2TokenCustomizer<JwtEncodingContext>jwtCustomizer(){return context ->{
            context.getJwsHeader().header("client-id", context.getRegisteredClient().getClientId());
            context.getClaims().claim("test","哈哈哈").build();
            log.info("jwtCustomizer -> getJwsHeader:{}",context.getJwsHeader());
            log.info("jwtCustomizer -> claim:{}", context.getClaims());// Customize claims};}/**
     *  自定义token属性 预留
     * @return /
     */@BeanpublicOAuth2TokenCustomizer<OAuth2TokenClaimsContext>accessTokenCustomizer(){return context ->{OAuth2TokenClaimsSet.Builder claims = context.getClaims();
            claims.claim("test200","哈哈哈哈哈").build();
            log.info("accessTokenCustomizer -> claims:{}",claims);// Customize claims};}// Jwt编码上下文 拓展token//    @Bean//    public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {//        return context -> {//            JwsHeader.Builder headers = context.getJwsHeader();//            JwtClaimsSet.Builder claims = context.getClaims();//            headers.header("client-id", context.getRegisteredClient().getClientId());//            log.info("jwtCustomizer headers:{}",headers);//            if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {//                // Customize headers/claims for access_token////            } else if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {//                // Customize headers/claims for id_token//            }//        };//    }// 自定义 授权服务器的实例,设置用于配置spring授权服务器@BeanpublicAuthorizationServerSettingsauthorizationServerSettings(){// 总之server服务,例如:个性化认证及授权相关的路径returnAuthorizationServerSettings.builder().build();}// 会话管理//    @Bean//    public SessionRegistry sessionRegistry() {//        return new SessionRegistryImpl();//    }//    @Bean//    public HttpSessionEventPublisher httpSessionEventPublisher() {//        return new HttpSessionEventPublisher();//    }}

MybatisPlusConfig

@ConfigurationpublicclassMybatisPlusConfig{/**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptor interceptor =newMybatisPlusInterceptor();
        interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}

controller包

ClientController

注册客户端

@RequestMapping("/oauth2/client")@RestController@RequiredArgsConstructorpublicclassClientController{privatefinalRegisteredClientRepository registeredClientRepository;privatefinalPasswordEncoder passwordEncoder;/**
     * 注册客户端
     */@PostMappingpublicResponseEntity<Boolean>registeredClientRepository(@RequestBodyRegisteredOauth2Client registeredClient){// @formatter:offRegisteredClient entity =RegisteredClient.withId(UUID.randomUUID().toString())// ID.clientId(registeredClient.getClientId())// 秘钥.clientSecret(passwordEncoder.encode(registeredClient.getClientSecret()))// POST 请求方法.clientAuthenticationMethods(clientAuthenticationMethods ->
                        clientAuthenticationMethods.addAll(registeredClient.getClientAuthenticationMethods()))// CLIENT_CREDENTIALS.authorizationGrantTypes(authorizationGrantTypes ->
                        authorizationGrantTypes.addAll(registeredClient.getAuthorizationGrantTypes()))// 范围.scopes(strings ->{// 超级
                    strings.addAll(registeredClient.getScopes());}).tokenSettings(registeredClient.getTokenSettings())// 客户端配置.clientSettings(registeredClient.getClientSettings()).build();// Save registered client in db
        registeredClientRepository.save(entity);returnResponseEntity.ok(true);}}

InfoController

测试 当前认证上下文信息

@RestController@RequestMapping("/info")publicclassInfoController{@GetMapping("/token")publicObjecttoken(){returnSecurityContextHolder.getContext().getAuthentication().getPrincipal();}}

domain

BaseResponseDto

@Data@AllArgsConstructor@NoArgsConstructor@BuilderpublicclassBaseResponseDto{privateString code;privateString message;}

LoginRequest

@DatapublicclassLoginRequest{privateString username;privateString password;}

UserDto

@Data@Accessors(chain =true)publicclassUserDto{privateString id;privateString hosId;privateString token;}

RegisteredOauth2Client 重要

这个类可以着重看一下

@Data@NoArgsConstructorpublicclassRegisteredOauth2ClientimplementsSerializable{/**
     * 客户端ID
     */privateString clientId;/**
     * 客户端秘钥
     */privateString clientSecret;/**
     * 客户端名称
     */privateString clientName;/**
     * 权限范围
     */privateSet<String> scopes;privateInstant clientIdIssuedAt;privateInstant clientSecretExpiresAt;privateSet<ClientAuthenticationMethod> clientAuthenticationMethods;privateSet<AuthorizationGrantType> authorizationGrantTypes;privateSet<String> redirectUris;privateClientSettings clientSettings =ClientSettings.builder()// 是否需要用户确认一下客户端需要获取用户的哪些权限// 比如:客户端需要获取用户的 用户信息、用户照片 但是此处用户可以控制只给客户端授权获取 用户信息。.requireAuthorizationConsent(true).build();privateTokenSettings tokenSettings =TokenSettings.builder()// accessToken 的有效期.accessTokenTimeToLive(Duration.ofHours(1))// refreshToken 的有效期.refreshTokenTimeToLive(Duration.ofDays(3))// 是否可重用刷新令牌.reuseRefreshTokens(true).build();}

UserEntity

@Data@TableName("user")publicclassUserEntity{@TableId("id_")privateLong id;@TableField("username")privateString username;@TableField("password")privateString password;}

mapper

UserMapper

publicinterfaceUserMapperextendsBaseMapper<UserEntity>{}

service

IUserService

publicinterfaceIUserServiceextendsIService<UserEntity>{}

UserServiceImpl

@Service@RequiredArgsConstructorpublicclassUserServiceImplextendsServiceImpl<UserMapper,UserEntity>implementsIUserService{}

utils

HelperUtils

publicclassHelperUtils{publicstaticfinalObjectWriterJSON_WRITER=newObjectMapper().writer().withDefaultPrettyPrinter();}

JwtUtils

publicfinalclassJwtUtils{privateJwtUtils(){}publicstaticJwsHeader.Builderheaders(){returnJwsHeader.with(SignatureAlgorithm.RS256);}publicstaticJwtClaimsSet.BuilderaccessTokenClaims(RegisteredClient registeredClient,String issuer,String subject,Set<String> authorizedScopes){Instant issuedAt =Instant.now();Instant expiresAt = issuedAt
            .plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive());/**
         * iss (issuer):签发人/发行人
         * sub (subject):主题
         * aud (audience):用户
         * exp (expiration time):过期时间
         * nbf (Not Before):生效时间,在此之前是无效的
         * iat (Issued At):签发时间
         * jti (JWT ID):用于标识该 JWT
         */// @formatter:offJwtClaimsSet.Builder claimsBuilder =JwtClaimsSet.builder();if(StringUtils.hasText(issuer)){
            claimsBuilder.issuer(issuer);}
        claimsBuilder
                .subject(subject).audience(Collections.singletonList(registeredClient.getClientId())).issuedAt(issuedAt).expiresAt(expiresAt).notBefore(issuedAt);if(!CollectionUtils.isEmpty(authorizedScopes)){
            claimsBuilder.claim(OAuth2ParameterNames.SCOPE, authorizedScopes);}// @formatter:onreturn claimsBuilder;}publicstaticJwtClaimsSet.BuilderidTokenClaims(RegisteredClient registeredClient,String issuer,String subject,String nonce){Instant issuedAt =Instant.now();// TODO Allow configuration for ID Token time-to-liveInstant expiresAt = issuedAt.plus(30,ChronoUnit.MINUTES);// @formatter:offJwtClaimsSet.Builder claimsBuilder =JwtClaimsSet.builder();if(StringUtils.hasText(issuer)){
            claimsBuilder.issuer(issuer);}
        claimsBuilder
                .subject(subject).audience(Collections.singletonList(registeredClient.getClientId())).issuedAt(issuedAt).expiresAt(expiresAt).claim(IdTokenClaimNames.AZP, registeredClient.getClientId());if(StringUtils.hasText(nonce)){
            claimsBuilder.claim(IdTokenClaimNames.NONCE, nonce);}// TODO Add 'auth_time' claim// @formatter:onreturn claimsBuilder;}}

OAuth2ConfigurerUtils

/**
 * 复制security底层的工具类
 */publicclassOAuth2ConfigurerUtils{privateOAuth2ConfigurerUtils(){}publicstaticRegisteredClientRepositorygetRegisteredClientRepository(HttpSecurity httpSecurity){RegisteredClientRepository registeredClientRepository =(RegisteredClientRepository)httpSecurity.getSharedObject(RegisteredClientRepository.class);if(registeredClientRepository ==null){
            registeredClientRepository =(RegisteredClientRepository)getBean(httpSecurity,RegisteredClientRepository.class);
            httpSecurity.setSharedObject(RegisteredClientRepository.class, registeredClientRepository);}return registeredClientRepository;}publicstaticOAuth2AuthorizationServicegetAuthorizationService(HttpSecurity httpSecurity){OAuth2AuthorizationService authorizationService =(OAuth2AuthorizationService)httpSecurity.getSharedObject(OAuth2AuthorizationService.class);if(authorizationService ==null){
            authorizationService =(OAuth2AuthorizationService)getOptionalBean(httpSecurity,OAuth2AuthorizationService.class);if(authorizationService ==null){
                authorizationService =newInMemoryOAuth2AuthorizationService();}

            httpSecurity.setSharedObject(OAuth2AuthorizationService.class, authorizationService);}return(OAuth2AuthorizationService)authorizationService;}publicstaticOAuth2AuthorizationConsentServicegetAuthorizationConsentService(HttpSecurity httpSecurity){OAuth2AuthorizationConsentService authorizationConsentService =(OAuth2AuthorizationConsentService)httpSecurity.getSharedObject(OAuth2AuthorizationConsentService.class);if(authorizationConsentService ==null){
            authorizationConsentService =(OAuth2AuthorizationConsentService)getOptionalBean(httpSecurity,OAuth2AuthorizationConsentService.class);if(authorizationConsentService ==null){
                authorizationConsentService =newInMemoryOAuth2AuthorizationConsentService();}

            httpSecurity.setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);}return(OAuth2AuthorizationConsentService)authorizationConsentService;}publicstaticOAuth2TokenGenerator<?extendsOAuth2Token>getTokenGenerator(HttpSecurity httpSecurity){OAuth2TokenGenerator<?extendsOAuth2Token> tokenGenerator =(OAuth2TokenGenerator)httpSecurity.getSharedObject(OAuth2TokenGenerator.class);if(tokenGenerator ==null){
            tokenGenerator =(OAuth2TokenGenerator)getOptionalBean(httpSecurity,OAuth2TokenGenerator.class);if(tokenGenerator ==null){JwtGenerator jwtGenerator =getJwtGenerator(httpSecurity);OAuth2AccessTokenGenerator accessTokenGenerator =newOAuth2AccessTokenGenerator();OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer =getAccessTokenCustomizer(httpSecurity);if(accessTokenCustomizer !=null){
                    accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer);}OAuth2RefreshTokenGenerator refreshTokenGenerator =newOAuth2RefreshTokenGenerator();if(jwtGenerator !=null){
                    tokenGenerator =newDelegatingOAuth2TokenGenerator(newOAuth2TokenGenerator[]{jwtGenerator, accessTokenGenerator, refreshTokenGenerator});}else{
                    tokenGenerator =newDelegatingOAuth2TokenGenerator(newOAuth2TokenGenerator[]{accessTokenGenerator, refreshTokenGenerator});}}

            httpSecurity.setSharedObject(OAuth2TokenGenerator.class, tokenGenerator);}return(OAuth2TokenGenerator)tokenGenerator;}privatestaticJwtGeneratorgetJwtGenerator(HttpSecurity httpSecurity){JwtGenerator jwtGenerator =(JwtGenerator)httpSecurity.getSharedObject(JwtGenerator.class);if(jwtGenerator ==null){JwtEncoder jwtEncoder =getJwtEncoder(httpSecurity);if(jwtEncoder !=null){
                jwtGenerator =newJwtGenerator(jwtEncoder);OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer =getJwtCustomizer(httpSecurity);if(jwtCustomizer !=null){
                    jwtGenerator.setJwtCustomizer(jwtCustomizer);}

                httpSecurity.setSharedObject(JwtGenerator.class, jwtGenerator);}}return jwtGenerator;}privatestaticJwtEncodergetJwtEncoder(HttpSecurity httpSecurity){JwtEncoder jwtEncoder =(JwtEncoder)httpSecurity.getSharedObject(JwtEncoder.class);if(jwtEncoder ==null){
            jwtEncoder =(JwtEncoder)getOptionalBean(httpSecurity,JwtEncoder.class);if(jwtEncoder ==null){JWKSource<SecurityContext> jwkSource =getJwkSource(httpSecurity);if(jwkSource !=null){
                    jwtEncoder =newNimbusJwtEncoder(jwkSource);}}if(jwtEncoder !=null){
                httpSecurity.setSharedObject(JwtEncoder.class, jwtEncoder);}}return(JwtEncoder)jwtEncoder;}publicstaticJWKSource<SecurityContext>getJwkSource(HttpSecurity httpSecurity){JWKSource<SecurityContext> jwkSource =(JWKSource)httpSecurity.getSharedObject(JWKSource.class);if(jwkSource ==null){ResolvableType type =ResolvableType.forClassWithGenerics(JWKSource.class,newClass[]{SecurityContext.class});
            jwkSource =(JWKSource)getOptionalBean(httpSecurity, type);if(jwkSource !=null){
                httpSecurity.setSharedObject(JWKSource.class, jwkSource);}}return jwkSource;}privatestaticOAuth2TokenCustomizer<JwtEncodingContext>getJwtCustomizer(HttpSecurity httpSecurity){ResolvableType type =ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,newClass[]{JwtEncodingContext.class});return(OAuth2TokenCustomizer)getOptionalBean(httpSecurity, type);}privatestaticOAuth2TokenCustomizer<OAuth2TokenClaimsContext>getAccessTokenCustomizer(HttpSecurity httpSecurity){ResolvableType type =ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,newClass[]{OAuth2TokenClaimsContext.class});return(OAuth2TokenCustomizer)getOptionalBean(httpSecurity, type);}publicstaticAuthorizationServerSettingsgetAuthorizationServerSettings(HttpSecurity httpSecurity){AuthorizationServerSettings authorizationServerSettings =(AuthorizationServerSettings)httpSecurity.getSharedObject(AuthorizationServerSettings.class);if(authorizationServerSettings ==null){
            authorizationServerSettings =(AuthorizationServerSettings)getBean(httpSecurity,AuthorizationServerSettings.class);
            httpSecurity.setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings);}return authorizationServerSettings;}publicstatic<T>TgetBean(HttpSecurity httpSecurity,Class<T> type){return((ApplicationContext)httpSecurity.getSharedObject(ApplicationContext.class)).getBean(type);}publicstatic<T>TgetBean(HttpSecurity httpSecurity,ResolvableType type){ApplicationContext context =(ApplicationContext)httpSecurity.getSharedObject(ApplicationContext.class);String[] names = context.getBeanNamesForType(type);if(names.length ==1){return(T) context.getBean(names[0]);}elseif(names.length >1){thrownewNoUniqueBeanDefinitionException(type, names);}else{thrownewNoSuchBeanDefinitionException(type);}}publicstatic<T>TgetOptionalBean(HttpSecurity httpSecurity,Class<T> type){Map<String,T> beansMap =BeanFactoryUtils.beansOfTypeIncludingAncestors((ListableBeanFactory)httpSecurity.getSharedObject(ApplicationContext.class), type);if(beansMap.size()>1){int var10003 = beansMap.size();String var10004 = type.getName();thrownewNoUniqueBeanDefinitionException(type, var10003,"Expected single matching bean of type '"+ var10004 +"' but found "+ beansMap.size()+": "+StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));}else{return!beansMap.isEmpty()? beansMap.values().iterator().next():null;}}publicstatic<T>TgetOptionalBean(HttpSecurity httpSecurity,ResolvableType type){ApplicationContext context =(ApplicationContext)httpSecurity.getSharedObject(ApplicationContext.class);String[] names = context.getBeanNamesForType(type);if(names.length >1){thrownewNoUniqueBeanDefinitionException(type, names);}else{return names.length ==1?(T) context.getBean(names[0]):null;}}}

效果截图

注册客户端

{"clientId":"123456","clientSecret":"8b30c1482ff973cfc92e51e1ec636966","clientName":"test","scopes":["super"],"clientAuthenticationMethods":["client_secret_post"],"authorizationGrantTypes":["refresh_token","client_credentials","sms_app,wechat_miniapp"]}

在这里插入图片描述

客户端授权

grant_type:wechat_miniapp
scope:super
client_id:123456
client_secret:8b30c1482ff973cfc92e51e1ec636966
appId:123456789

在这里插入图片描述

不带access_token访问资源

在这里插入图片描述

带access_token访问资源

在这里插入图片描述

数据库截图

客户端

在这里插入图片描述

授权

在这里插入图片描述

标签: spring java spring boot

本文转载自: https://blog.csdn.net/weixin_43861630/article/details/131952233
版权归原作者 离别又见离别 所有, 如有侵权,请联系我们删除。

“spring-authorization-server (1.1.1)自定义认证”的评论:

还没有评论