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

网站建设与维护流程图仓库管理系统app

网站建设与维护流程图,仓库管理系统app,推广网络营销外包,视觉设计公司排名主要分析spring-security-oauth2-resource-server的源码#xff0c;介绍OAuth2.0授权码模式下Spring Boot OAuth2资源服务的运行流程#xff0c;分析其是如何对令牌进行认证的#xff0c;并展示资源服务配置 代码版本信息 Spring Boot 2.7.10 spring-security-oauth2-resou… 主要分析spring-security-oauth2-resource-server的源码介绍OAuth2.0授权码模式下Spring Boot OAuth2资源服务的运行流程分析其是如何对令牌进行认证的并展示资源服务配置 代码版本信息 Spring Boot 2.7.10 spring-security-oauth2-resource-server 5.7.7大致过程 Spring Security OAuth2 资源服务对token的大致认证过程 BearerTokenAuthenticationFilter 过滤器会从 HTTP 请求的 Authorization 头中提取 Bearer Token将提取到的 Token 包装成 BearerTokenAuthenticationToken 认证对象交给 Spring Security 的认证管理器 (AuthenticationManager) 进行处理。如果配置了 JWT 认证BearerTokenAuthenticationFilter 会使用JwtAuthenticationProvider 作为认证提供者来验证和解析 JWT Token。而JwtAuthenticationProvider 会使用一个 JwtDecoder 来解析和验证 Token。在 application.yml 中如果配置了Spring Security 资源服务Resource Server的issuer-uri那么 Spring Security 会使用这个 URI 自动发现授权服务器的 JWKS 端点。并通过JwtDecoder 的实现类NimbusJwtDecoder 构建一个 JWK Set URL通过这个 URL 向授权服务器请求 JWKSJSON Web Key Set获取公钥用于验证 JWT 的签名。NimbusJwtDecoder 会使用获取的密钥验证 JWT 的签名检查 JWT 的有效性如过期时间、受众等。如果 JWT 验证成功认证通过客户端即可访问受保护的资源。 配置issuer-uri作用 在Spring Security OAuth资源服务器配置中设置issuer-uri的目的是为了确保JWTJSON Web Token的可信来源并自动获取JWKJSON Web Key集用于验证JWT的签名和内容。 目的和作用 确保JWT的可信来源 issuer-uri表示JWT签发者的URI。配置此URI可以确保资源服务器仅接受由该特定签发者签发的JWT。这有助于防止来自不可信来源的令牌被接受和使用。在验证过程中资源服务器会检查JWT的ississuer声明确保其与配置的issuer-uri匹配。如果不匹配验证将失败。 自动发现和获取JWK集 配置issuer-uri后资源服务器可以自动从签发者的OIDCOpenID Connect发现端点或OAuth 2.0授权服务器元数据端点获取JWK集。JWK集包含用于验证JWT签名的公钥信息。资源服务器会定期从该端点获取最新的JWK集以确保验证过程中使用的是最新的密钥。 配置示例 spring:security:oauth2:resourceserver:jwt:issuer-uri: http://127.0.0.1:9000在上述配置中 issuer-uri设置为https://example.com/issuer表示资源服务器将从该URI获取JWT签发者信息和JWK集。当资源服务器接收到一个JWT时它会检查该JWT的签发者声明iss是否与配置的issuer-uri匹配并从该URI获取用于验证的JWK集。 工作机制 JWT签发者验证 当资源服务器接收到JWT时它会检查JWT中的iss声明是否匹配issuer-uri。例如如果issuer-uri配置为https://example.com/issuerJWT的iss声明也必须是https://example.com/issuer。 JWK集获取和使用 资源服务器从issuer-uri的OIDC发现端点获取JWK集例如https://example.com/issuer/.well-known/openid-configuration。获取的JWK集包含用于验证JWT签名的公钥。资源服务器会使用这些公钥来验证JWT的签名确保JWT未被篡改且来自可信的签发者。 总结 配置issuer-uri可以确保JWT的来源可信并自动获取和更新验证所需的公钥信息从而增强了安全性和可靠性。这是确保资源服务器只接受合法、未篡改的JWT的重要步骤。 过滤器源码解析 BearerTokenAuthenticationFilter过滤器 BearerTokenAuthenticationFilter 是 Spring Security 用于处理 Token 认证的过滤器。它扩展了 OncePerRequestFilter确保在一次请求过程中只执行一次。这个过滤器从请求中提取 Bearer Token然后尝试对令牌进行认证。 主要职责 提取 Bearer Token从请求中解析出 Bearer Token。创建认证请求用提取的 Token 创建一个认证请求。尝试认证使用 AuthenticationManager 尝试认证请求。处理认证结果根据认证结果设置安全上下文或处理认证失败。 简单来说该过滤器会获取客户端资源请求中的token并验证其有效性如果有效就允许客户端访问资源。 详细解析源码 构造函数 public BearerTokenAuthenticationFilter(AuthenticationManagerResolverHttpServletRequest authenticationManagerResolver) {Assert.notNull(authenticationManagerResolver, authenticationManagerResolver cannot be null);this.authenticationManagerResolver authenticationManagerResolver; }public BearerTokenAuthenticationFilter(AuthenticationManager authenticationManager) {Assert.notNull(authenticationManager, authenticationManager cannot be null);this.authenticationManagerResolver (request) - authenticationManager; }BearerTokenAuthenticationFilter 接受一个 AuthenticationManagerResolver 或 AuthenticationManager。这两个构造函数允许使用不同的方式来解析 AuthenticationManager。 doFilterInternal 方法 Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String token;try {//解析 Token//使用DefaultBearerTokenResolver从请求中获取token分别从请求头及请求域中获取只能在其中一个包含token this.bearerTokenResolver.resolve(request);} catch (OAuth2AuthenticationException invalid) {this.logger.trace(Sending to authentication entry point since failed to resolve bearer token, invalid);//解析失败处理this.authenticationEntryPoint.commence(request, response, invalid);return;}//如果没有找到 Token直接调用过滤器链的下一个过滤器if (token null) {this.logger.trace(Did not process request since did not find bearer token);filterChain.doFilter(request, response);return;}//根据token创建认证对象并设置认证详情即设置详细信息BearerTokenAuthenticationToken authenticationRequest new BearerTokenAuthenticationToken(token);authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));try {//获取Manager用于验证认证对象。如果没有单独配置默认情况下返回ProviderManagerAuthenticationManager authenticationManager this.authenticationManagerResolver.resolve(request);Authentication authenticationResult authenticationManager.authenticate(authenticationRequest);SecurityContext context SecurityContextHolder.createEmptyContext();context.setAuthentication(authenticationResult);SecurityContextHolder.setContext(context);this.securityContextRepository.saveContext(context, request, response);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format(Set SecurityContextHolder to %s, authenticationResult));}filterChain.doFilter(request, response);} catch (AuthenticationException failed) {SecurityContextHolder.clearContext();this.logger.trace(Failed to process authentication request, failed);this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);} }解析 Token token this.bearerTokenResolver.resolve(request);默认使用 DefaultBearerTokenResolver 从请求中提取 Bearer Token。如果解析失败调用 AuthenticationEntryPoint 处理异常。 Token 不存在 if (token null) {this.logger.trace(Did not process request since did not find bearer token);filterChain.doFilter(request, response);return; }如果没有找到 Token直接调用过滤器链的下一个过滤器。 创建认证对象 BearerTokenAuthenticationToken authenticationRequest new BearerTokenAuthenticationToken(token); authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));使用 Token 创建一个 BearerTokenAuthenticationToken认证对象并设置认证详情。 尝试认证 AuthenticationManager authenticationManager this.authenticationManagerResolver.resolve(request); Authentication authenticationResult authenticationManager.authenticate(authenticationRequest);resolve方法获取AuthenticationManager用于认证上面的认证对象 BearerTokenAuthenticationToken如果配置类没有指定默认返回ProviderManager使用委托模式调用authenticate方法进行认证 使用ProviderManager尝试认证。如果认证成功设置安全上下文并保存认证信息。 ProviderManager使用JwtAuthenticationProvider实现类进行认证下面是其重写的authenticate方法 Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {BearerTokenAuthenticationToken bearer (BearerTokenAuthenticationToken) authentication;//通过jwt解析器解析tokenJwt jwt getJwt(bearer);AbstractAuthenticationToken token this.jwtAuthenticationConverter.convert(jwt);token.setDetails(bearer.getDetails());this.logger.debug(Authenticated token);return token; }getJwt方法中解析Token getJwt方法中获取解析器JwtDecoder并解析token private Jwt getJwt(BearerTokenAuthenticationToken bearer) {try {//使用NimbusJwtDecoder实现类return this.jwtDecoder.decode(bearer.getToken());}catch (BadJwtException failed) {this.logger.debug(Failed to authenticate since the JWT was invalid);throw new InvalidBearerTokenException(failed.getMessage(), failed);}catch (JwtException failed) {throw new AuthenticationServiceException(failed.getMessage(), failed);} }解析器装配 解析器JwtDecoder实现为NimbusJwtDecoder通过读取yaml由配置类OAuth2ResourceServerJwtConfiguration进行配置 OAuth2ResourceServerJwtConfiguration通过内部类JwtDecoderConfiguration的jwtDecoderByIssuerUri方法进行配置 Configuration(proxyBeanMethods false) ConditionalOnMissingBean(JwtDecoder.class) static class JwtDecoderConfiguration {//对应配置文件spring.security.oauth2.resourceserver配置private final OAuth2ResourceServerProperties.Jwt properties;//...........省略源码BeanConditional(IssuerUriCondition.class)SupplierJwtDecoder jwtDecoderByIssuerUri() {return new SupplierJwtDecoder(() - {String issuerUri this.properties.getIssuerUri();//调用JwtDecoderProviderConfigurationUtils的getConfiguration(String issuer, URI... uris)方法获取授权服务元数据端点信息包含jwks_uri等依靠端点信息创建出NimbusJwtDecoderNimbusJwtDecoder jwtDecoder JwtDecoders.fromIssuerLocation(issuerUri);jwtDecoder.setJwtValidator(getValidators(() - JwtValidators.createDefaultWithIssuer(issuerUri)));return jwtDecoder;});}} 会读取如下yaml配置信息 spring:security:oauth2:resourceserver:jwt:#项目启动初始化时在JwtDecoderProviderConfigurationUtils的getConfiguration方法处发起http://127.0.0.1:9000/.well-known/openid-configuration请求获取元数据断点信息后续用于token认证#在JwtDecoders的withProviderConfiguration方法中为NimbusJwtDecoder赋予jwks_uriissuer-uri: http://127.0.0.1:9000 可配置项包括 spring.security.oauth2.resourceserver.jwt.jwk-set-uri用于验证 JWT 令牌的 JSON Web Key (JWK) URI。spring.security.oauth2.resourceserver.jwt.issuer-uri值为OAuth 2.0 授权服务器ip加端口在OAuth2ResourceServerJwtConfiguration配置类的jwtDecoderByIssuerUri方法处根据issuer-uri配置了NimbusJwtDecoder解析器spring.security.oauth2.resourceserver.jwt.public-key-location包含用于验证 JWT 的公钥的文件的位置。spring.security.oauth2.resourceserver.jwt.jws-algorithms用于验证数字签名的 JSON Web 算法列表默认为 RS256。spring.security.oauth2.resourceserver.jwt.audiences标识 JWT 预期接收者的受众列表。 下面看OAuth2ResourceServerJwtConfiguration的内部类JwtDecoderConfiguration的jwtDecoderByIssuerUri装配过程 通过内部的如下代码进行 Bean Conditional(IssuerUriCondition.class) SupplierJwtDecoder jwtDecoderByIssuerUri() {return new SupplierJwtDecoder(() - {String issuerUri this.properties.getIssuerUri();//调用JwtDecoderProviderConfigurationUtils的getConfiguration(String issuer, URI... uris)方法获取授权服务元数据端点信息包含jwks_uri等依靠端点信息创建出NimbusJwtDecoderNimbusJwtDecoder jwtDecoder JwtDecoders.fromIssuerLocation(issuerUri);jwtDecoder.setJwtValidator(getValidators(() - JwtValidators.createDefaultWithIssuer(issuerUri)));return jwtDecoder;}); }调用的JwtDecoders.fromIssuerLocation方法代码 public static T extends JwtDecoder T fromIssuerLocation(String issuer) {Assert.hasText(issuer, issuer cannot be empty);//1.通过issuer配置的地址发起请求获得元数据端点数据MapString, Object configuration JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer);//2.配置解析器NimbusJwtDecoder为其指定验证类用于JWTtoken验证return (T) withProviderConfiguration(configuration, issuer);}请求元数据端点JwtDecoderProviderConfigurationUtils的getConfigurationForIssuerLocation方法会再通过getConfiguration方法发起请求获取元数据信息截取源码片段 static MapString, Object getConfigurationForIssuerLocation(String issuer) {URI uri URI.create(issuer);//调用下面的getConfigurationreturn getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri)); }private static MapString, Object getConfiguration(String issuer, URI... uris) {String errorMessage Unable to resolve the Configuration with the provided Issuer of \ issuer \;for (URI uri : uris) {try {//向授权服务发起请求RequestEntityVoid request RequestEntity.get(uri).build();ResponseEntityMapString, Object response rest.exchange(request, STRING_OBJECT_MAP);MapString, Object configuration response.getBody();Assert.isTrue(configuration.get(jwks_uri) ! null, The public JWK set URI must not be null);return configuration;}catch (IllegalArgumentException ex) {throw ex;}catch (RuntimeException ex) {if (!(ex instanceof HttpClientErrorException ((HttpClientErrorException) ex).getStatusCode().is4xxClientError())) {throw new IllegalArgumentException(errorMessage, ex);}// else try another endpoint}}throw new IllegalArgumentException(errorMessage); }根据上一步获取的信息配置解析器NimbusJwtDecoder。 JwtDecoders.withProviderConfiguration内部 private static JwtDecoder withProviderConfiguration(MapString, Object configuration, String issuer) {JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);//创建JwtTimestampValidator与JwtIssuerValidator作为NimbusJwtDecoder的验证器OAuth2TokenValidatorJwt jwtValidator JwtValidators.createDefaultWithIssuer(issuer);String jwkSetUri configuration.get(jwks_uri).toString();//创建NimbusJwtDecoderNimbusJwtDecoder jwtDecoder NimbusJwtDecoder//设置验证 JWT 令牌的url.withJwkSetUri(jwkSetUri)//配置支持的 JWS 算法用来验证 JWT 的签名.jwtProcessorCustomizer(JwtDecoderProviderConfigurationUtils::addJWSAlgorithms).build();jwtDecoder.setJwtValidator(jwtValidator);return jwtDecoder; }JwtValidators.createDefaultWithIssuer添加验证器 public static OAuth2TokenValidatorJwt createDefaultWithIssuer(String issuer) {ListOAuth2TokenValidatorJwt validators new ArrayList();validators.add(new JwtTimestampValidator());validators.add(new JwtIssuerValidator(issuer));return new DelegatingOAuth2TokenValidator(validators); }验证器添加目的是NimbusJwtDecoder的decode方法调用validateJwt方法解析token为JWT并使用JwtIssuerValidator对token进行验证 Override public Jwt decode(String token) throws JwtException {//将token字符串转为JWT通过token字符串头部解析出不同的JWT// Algorithm.NONE无签名的 JWT返回 PlainJWT。// JWSAlgorithm签名的 JWT返回 SignedJWT。// JWEAlgorithm加密的 JWT返回 EncryptedJWT。// 其他算法类型则抛出异常JWT jwt parse(token);//无签名的 JWT不支持if (jwt instanceof PlainJWT) {this.logger.trace(Failed to decode unsigned token);throw new BadJwtException(Unsupported algorithm of jwt.getHeader().getAlgorithm());}Jwt createdJwt createJwt(token, jwt);// 将token转化的JWT进行验证return validateJwt(createdJwt); }JwtIssuerValidator内部又使用JwtClaimValidator验证由其构造方法决定 public JwtIssuerValidator(String issuer) {Assert.notNull(issuer, issuer cannot be null);PredicateObject testClaimValue (claimValue) - (claimValue ! null) issuer.equals(claimValue.toString());this.validator new JwtClaimValidator(JwtClaimNames.ISS, testClaimValue); }调用JwtClaimValidator的验证方法validate时其使用的this.test.test方法实际为JwtIssuerValidator构造方法的函数式接口testClaimValue对比的就是JWT中的claimValue是否与配置的issuer一致一致则认证成功 Override public OAuth2TokenValidatorResult validate(Jwt token) {Assert.notNull(token, token cannot be null);T claimValue token.getClaim(this.claim);//对比JWT中的claimValue是否与配置的issuer一致if (this.test.test(claimValue)) {return OAuth2TokenValidatorResult.success();}this.logger.debug(this.error.getDescription());return OAuth2TokenValidatorResult.failure(this.error); }jwtProcessorCustomizer配置JWS算法 调用JwtDecoderProviderConfigurationUtils::addJWSAlgorithms方法方法参数ConfigurableJWTProcessor为其实现类DefaultJWTProcessor static C extends SecurityContext void addJWSAlgorithms(ConfigurableJWTProcessorC jwtProcessor) {JWSKeySelectorC selector jwtProcessor.getJWSKeySelector();if (selector instanceof JWSVerificationKeySelector) {//使用RemoteJWKSet实现类用于从授权服务远程获取JWK密钥来验证JWT签名JWKSourceC jwkSource ((JWSVerificationKeySelectorC) selector).getJWKSource();SetJWSAlgorithm algorithms getJWSAlgorithms(jwkSource);selector new JWSVerificationKeySelector(algorithms, jwkSource);jwtProcessor.setJWSKeySelector(selector);} }此代码的作用是在NimbusJwtDecoder进行token解析时使用RemoteJWKSet从远程授权服务中获取最新的、有效的JWK以便用于JWT的签名验证。 JWT认证 NimbusJwtDecoder的decode方法 Override public Jwt decode(String token) throws JwtException {//将token字符串转为JWT通过token字符串头部解析出不同的JWT// Algorithm.NONE无签名的 JWT返回 PlainJWT。// JWSAlgorithm签名的 JWT返回 SignedJWT。// JWEAlgorithm加密的 JWT返回 EncryptedJWT。// 其他算法类型则抛出异常JWT jwt parse(token);//无签名的 JWT 不支持if (jwt instanceof PlainJWT) {this.logger.trace(Failed to decode unsigned token);throw new BadJwtException(Unsupported algorithm of jwt.getHeader().getAlgorithm());}//验证JWT签名、提取头部信息和声明并创建一个Jwt对象Jwt createdJwt createJwt(token, jwt);// 将token转化的Jwt进行验证return validateJwt(createdJwt); }parse方法的JWTcom.nimbusds.jwt.JWT 用于底层JWT操作通常与低级别的JWT操作如验证签名、加密/解密有关。createJwt方法的Jwt org.springframework.security.oauth2.jwt.Jwt 属于Spring Security集成在Spring Security框架中处理Spring Security中的JWT认证和授权。 createJwt创建Jwt对象 核心功能是通过验证JWT签名、提取头部信息和声明最终创建一个Jwt对象并处理在此过程中可能出现的各种异常。 private Jwt createJwt(String token, JWT parsedJwt) {try {// 验证签名, jwtProcessor使用的实现类为DefaultJWTProcessor// DefaultJWTProcessor使用了RemoteJWKSet实现类用于从授权服务远程获取JWK密钥来验证JWT签名JWTClaimsSet jwtClaimsSet this.jwtProcessor.process(parsedJwt, null);// 提取头部和声明MapString, Object headers new LinkedHashMap(parsedJwt.getHeader().toJSONObject());MapString, Object claims this.claimSetConverter.convert(jwtClaimsSet.getClaims());// 创建Jwt对象return Jwt.withTokenValue(token).headers((h) - h.putAll(headers)).claims((c) - c.putAll(claims)).build();} catch (RemoteKeySourceException ex) {this.logger.trace(Failed to retrieve JWK set, ex);if (ex.getCause() instanceof ParseException) {throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, Malformed Jwk set), ex);}throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);} catch (JOSEException ex) {this.logger.trace(Failed to process JWT, ex);throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);} catch (Exception ex) {this.logger.trace(Failed to process JWT, ex);if (ex.getCause() instanceof ParseException) {throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, Malformed payload), ex);}throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);} }这段代码是用来从给定的JWT字符串创建一个Jwt对象。它通过以下步骤来实现这个过程 验证签名 JWTClaimsSet jwtClaimsSet this.jwtProcessor.process(parsedJwt, null);通过jwtProcessor处理解析后的JWT对象parsedJwt来验证签名并提取JWT声明集jwtClaimsSet。jwtProcessor使用已配置的JWKJSON Web Key源来验证JWT的签名。 提取头部和声明 MapString, Object headers new LinkedHashMap(parsedJwt.getHeader().toJSONObject());从解析后的JWT对象中提取头部信息并转换为一个Map。MapString, Object claims this.claimSetConverter.convert(jwtClaimsSet.getClaims());使用claimSetConverter将JWT声明集中的声明转换为一个Map。 创建Jwt对象 return Jwt.withTokenValue(token).headers((h) - h.putAll(headers)).claims((c) - c.putAll(claims)).build();使用提取的头部信息和声明创建一个Jwt对象。这个Jwt对象包含了原始的JWT字符串、头部信息和声明。 处理异常 如果在验证或处理JWT的过程中发生异常会捕获并处理这些异常。异常类型包括RemoteKeySourceException、JOSEException和通用的Exception。根据具体的异常类型记录调试信息并抛出相应的JwtException或BadJwtException。 关键点 jwtProcessor用于处理JWT并验证其签名。claimSetConverter用于将JWT声明集转换为一个Map。异常处理通过不同的catch块处理不同类型的异常并记录相关的调试信息。 validateJwt验证 validateJwt方法代码 private Jwt validateJwt(Jwt jwt) {OAuth2TokenValidatorResult result this.jwtValidator.validate(jwt);if (result.hasErrors()) {CollectionOAuth2Error errors result.getErrors();String validationErrorString getJwtValidationExceptionMessage(errors);throw new JwtValidationException(validationErrorString, errors);}return jwt; }this.jwtValidator.validate使用JwtIssuerValidator对token进行验证JwtIssuerValidator内部又使用JwtClaimValidator验证由其构造方法决定 public JwtIssuerValidator(String issuer) {Assert.notNull(issuer, issuer cannot be null);PredicateObject testClaimValue (claimValue) - (claimValue ! null) issuer.equals(claimValue.toString());this.validator new JwtClaimValidator(JwtClaimNames.ISS, testClaimValue); }调用JwtClaimValidator的验证方法validate时其使用的this.test.test方法实际为JwtIssuerValidator构造方法的函数式接口testClaimValue对比的就是JWT中的claimValue是否与配置的issuer一致一致则认证成功 Override public OAuth2TokenValidatorResult validate(Jwt token) {Assert.notNull(token, token cannot be null);T claimValue token.getClaim(this.claim);//对比JWT中的claimValue是否与配置的issuer一致if (this.test.test(claimValue)) {return OAuth2TokenValidatorResult.success();}this.logger.debug(this.error.getDescription());return OAuth2TokenValidatorResult.failure(this.error); }JWT转换 NimbusJwtDecoder的decode方法解析成功后将Jwt返回到JwtAuthenticationProvider并使用convert将jwt转为AbstractAuthenticationToken Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {BearerTokenAuthenticationToken bearer (BearerTokenAuthenticationToken) authentication;Jwt jwt getJwt(bearer);//转换方法AbstractAuthenticationToken token this.jwtAuthenticationConverter.convert(jwt);token.setDetails(bearer.getDetails());this.logger.debug(Authenticated token);return token; }//解析成功返回Jwt到上面的Jwt jwt getJwt(bearer); private Jwt getJwt(BearerTokenAuthenticationToken bearer) {try {return this.jwtDecoder.decode(bearer.getToken());}catch (BadJwtException failed) {this.logger.debug(Failed to authenticate since the JWT was invalid);throw new InvalidBearerTokenException(failed.getMessage(), failed);}catch (JwtException failed) {throw new AuthenticationServiceException(failed.getMessage(), failed);} }convert源码 Override public final AbstractAuthenticationToken convert(Jwt jwt) {//从 JWT 中提取权限。获取scope权限将其转换为 Spring Security 的 GrantedAuthority 集合。CollectionGrantedAuthority authorities extractAuthorities(jwt);//获取 JWT 中特定声明如 sub 或 user_name的值作为主体principalString principalClaimValue jwt.getClaimAsString(this.principalClaimName);//创建并返回一个 JwtAuthenticationToken 对象。该对象包含原始 JWT、提取的权限和主体声明值。return new JwtAuthenticationToken(jwt, authorities, principalClaimValue); }保存上下文 JwtAuthenticationProvider验证完成后将token对象返回到BearerTokenAuthenticationFilter并将token对象保存上下文 Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {//............ try {AuthenticationManager authenticationManager this.authenticationManagerResolver.resolve(request);//JwtAuthenticationProvider验证完成后将token对象返回Authentication authenticationResult authenticationManager.authenticate(authenticationRequest);//保存authenticationResult到上下文SecurityContext context SecurityContextHolder.createEmptyContext();context.setAuthentication(authenticationResult);SecurityContextHolder.setContext(context);this.securityContextRepository.saveContext(context, request, response);if (this.logger.isDebugEnabled()) {this.logger.debug(LogMessage.format(Set SecurityContextHolder to %s, authenticationResult));}filterChain.doFilter(request, response);} catch (AuthenticationException failed) {SecurityContextHolder.clearContext();this.logger.trace(Failed to process authentication request, failed);this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);} }认证失败 catch (AuthenticationException failed) {SecurityContextHolder.clearContext();this.logger.trace(Failed to process authentication request, failed);this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed); }如果认证失败清除安全上下文并调用 AuthenticationFailureHandler 处理失败。 配置项 authenticationEntryPoint处理认证异常的入口点。authenticationFailureHandler处理认证失败的处理器。bearerTokenResolver解析 Bearer Token 的策略。authenticationDetailsSource提供认证详情的来源。securityContextRepository保存安全上下文的仓库。 总结 通过BearerTokenAuthenticationFilter过滤器来总结资源服务的认证流程 BearerTokenAuthenticationFilter 会从 HTTP 请求的 Authorization 头中提取 Bearer Token将提取到的 Token 包装成 BearerTokenAuthenticationToken 认证对象交给 Spring Security 的认证管理器 (AuthenticationManager) 进行处理。如果配置了 JWT 认证BearerTokenAuthenticationFilter 会使用JwtAuthenticationProvider 作为认证提供者来验证和解析 JWT Token。而JwtAuthenticationProvider 会使用一个 JwtDecoder 来解析和验证 Token。在 application.yml 中如果配置了Spring Security 资源服务Resource Server的issuer-uri那么 Spring Security 会使用这个 URI 自动发现授权服务器的 JWKS 端点。并通过JwtDecoder 的实现类NimbusJwtDecoder 构建一个 JWK Set URL通过这个 URL 向授权服务器请求 JWKSJSON Web Key Set获取公钥用于验证 JWT 的签名。NimbusJwtDecoder 会使用获取的密钥验证 JWT 的签名检查 JWT 的有效性如过期时间、受众等。如果 JWT 验证成功认证通过客户端即可访问受保护的资源。 BearerTokenAuthenticationFilter 是处理 OAuth2 Bearer Token 认证的核心过滤器。它从请求中提取 Token尝试认证并根据认证结果设置安全上下文或处理认证失败。通过灵活的配置项和处理器开发者可以自定义过滤器的行为以适应不同的需求。 JwtAuthenticationProvider 在 Spring Security 中JwtAuthenticationProvider 类是用于验证 JWTJSON Web Token的核心组件。这个类的主要功能是解码和验证 JWT 令牌并根据令牌内容创建一个认证对象。下面是对该类及其方法的详细解释 主要成员变量 jwtDecoder用于解码 JWT 令牌的组件。jwtAuthenticationConverter用于将 JWT 转换为 AbstractAuthenticationToken 的组件默认是 JwtAuthenticationConverter。 构造方法 public JwtAuthenticationProvider(JwtDecoder jwtDecoder) {Assert.notNull(jwtDecoder, jwtDecoder cannot be null);this.jwtDecoder jwtDecoder; }作用初始化 jwtDecoder确保其不为 null。 authenticate 方法 Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {BearerTokenAuthenticationToken bearer (BearerTokenAuthenticationToken) authentication;Jwt jwt getJwt(bearer);AbstractAuthenticationToken token this.jwtAuthenticationConverter.convert(jwt);token.setDetails(bearer.getDetails());this.logger.debug(Authenticated token);return token; }作用 将传入的 authentication 对象转换为 BearerTokenAuthenticationToken。调用 getJwt 方法解码并验证 JWT 令牌。使用 jwtAuthenticationConverter 将 JWT 转换为 AbstractAuthenticationToken。设置认证对象的细节信息。返回生成的认证对象。 getJwt 方法 private Jwt getJwt(BearerTokenAuthenticationToken bearer) {try {return this.jwtDecoder.decode(bearer.getToken());}catch (BadJwtException failed) {this.logger.debug(Failed to authenticate since the JWT was invalid);throw new InvalidBearerTokenException(failed.getMessage(), failed);}catch (JwtException failed) {throw new AuthenticationServiceException(failed.getMessage(), failed);} }作用 调用 jwtDecoder.decode 方法解码 JWT 令牌。如果解码失败捕获 BadJwtException 异常并记录调试信息抛出 InvalidBearerTokenException。如果捕获到其他 JwtException 异常抛出 AuthenticationServiceException。 supports 方法 Override public boolean supports(Class? authentication) {return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication); }作用确定当前 AuthenticationProvider 是否支持指定的认证类型这里是 BearerTokenAuthenticationToken。 setJwtAuthenticationConverter 方法 public void setJwtAuthenticationConverter(ConverterJwt, ? extends AbstractAuthenticationToken jwtAuthenticationConverter) {Assert.notNull(jwtAuthenticationConverter, jwtAuthenticationConverter cannot be null);this.jwtAuthenticationConverter jwtAuthenticationConverter; }作用允许设置自定义的 jwtAuthenticationConverter。 配置 YAML 示例 为了使 JWT 验证正常工作你需要在应用的配置文件中指定一些属性。以下是一个 YAML 配置示例 spring:security:oauth2:resourceserver:jwt:jwk-set-uri: https://example.com/.well-known/jwks.jsonissuer-uri: https://example.com/issuerspring.security.oauth2.resourceserver.jwt.jwk-set-uri用于验证 JWT 的 JWK 集 URI。spring.security.oauth2.resourceserver.jwt.issuer-uri授权服务器的发布者 URI。 总结 JwtAuthenticationProvider 通过 JwtDecoder 解码和验证 JWT 令牌并使用 jwtAuthenticationConverter 将解码后的 JWT 转换为认证对象。配置项如 jwk-set-uri 和 issuer-uri 用于指示从何处获取验证 JWT 的必要信息。这个机制确保了 JWT 令牌的安全性和有效性从而保障了应用的安全访问。 NimbusJwtDecoder如何使用jwk-set-uri进行jwt验证的 验证过程 NimbusJwtDecoder 使用 jwk-set-uri 进行 JWT 验证的过程如下 初始化: NimbusJwtDecoder 使用提供的 jwk-set-uri 初始化一个 JWKSourceSecurityContext。该 JWK 源负责从指定的 URI 获取 JWK 集。初始化代码如下 public static NimbusJwtDecoder withJwkSetUri(String jwkSetUri) {Assert.hasText(jwkSetUri, jwkSetUri 不能为空);RemoteJWKSetSecurityContext jwkSet new RemoteJWKSet(new URL(jwkSetUri));return new NimbusJwtDecoder(jwkSet); }解析 JWT: decode 方法首先使用 parse 方法将 JWT 字符串解析为 JWT 对象 JWT jwt parse(token);检查是否为 PlainJWT: 解码器检查 JWT 是否为 PlainJWT未签名的 JWT。如果是记录跟踪消息并抛出 BadJwtException 异常 if (jwt instanceof PlainJWT) {this.logger.trace(Failed to decode unsigned token);throw new BadJwtException(Unsupported algorithm of jwt.getHeader().getAlgorithm()); }创建 Jwt 对象: 调用 createJwt 方法从解析的 JWT 和原始 token 字符串创建一个 Jwt 对象。这涉及从 JWT 中提取声明和头部信息 Jwt createdJwt createJwt(token, jwt);验证 Jwt: 最后validateJwt 方法用于验证创建的 Jwt 对象 return validateJwt(createdJwt);具体到代码decode 方法如下 Override public Jwt decode(String token) throws JwtException {JWT jwt parse(token);if (jwt instanceof PlainJWT) {this.logger.trace(Failed to decode unsigned token);throw new BadJwtException(Unsupported algorithm of jwt.getHeader().getAlgorithm());}Jwt createdJwt createJwt(token, jwt);return validateJwt(createdJwt); }这个过程确保从 jwk-set-uri 获取的 JWK 被用于验证传入的 JWT以确保它的有效性和真实性。 核心方法简介 NimbusJwtDecoder核心方法介绍在 NimbusJwtDecoder 中使用 jwk-set-uri 获取的 JWK 集主要在 createJwt(token, jwt) 方法中被使用。 以下是各个方法的具体作用 parse(token): 该方法将传入的 JWT 字符串解析为 JWT 对象但不涉及验证。这仅是对 JWT 字符串的语法解析例如 private JWT parse(String token) throws JwtException {try {return JWTParser.parse(token);} catch (ParseException ex) {throw new JwtException(JWT 解析失败, ex);} }解析后的 JWT 对象可以是 SignedJWT、EncryptedJWT 或 PlainJWT。 createJwt(token, jwt): 该方法根据传入的 JWT 对象和原始 token 字符串创建一个 Jwt 对象。在这个过程中会使用到 JWK 集进行签名验证。关键步骤如下 提取 SignedJWT 中的签名信息。使用 JWK 集中的公钥来验证签名的有效性。 例如 private Jwt createJwt(String token, JWT parsedJwt) {SignedJWT signedJwt (SignedJWT) parsedJwt;JWSVerifier verifier new DefaultJWSVerifierFactory().createJWSVerifier(signedJwt.getHeader(), getJWKSource());if (!signedJwt.verify(verifier)) {throw new BadJwtException(JWT 签名验证失败);}// 其他处理 }这里的 getJWKSource() 方法会从 JWK 集中获取公钥信息来验证签名。 validateJwt(createdJwt): 该方法用于对创建的 Jwt 对象进行进一步的验证例如检查过期时间、受众等但不会直接使用 JWK 集。示例代码 private Jwt validateJwt(Jwt jwt) {// 检查过期时间、受众等if (jwt.getExpiresAt().isBefore(Instant.now())) {throw new JwtException(JWT 已过期);}// 其他验证return jwt; }总结在 NimbusJwtDecoder 中createJwt(token, jwt) 方法中使用了从 jwk-set-uri 获取的 JWK 集来进行 JWT 的签名验证。 createJwt方法 这段代码展示了 NimbusJwtDecoder 类中的 createJwt 方法该方法使用 JWK 集对传入的 JWT 进行验证和处理。以下是详细解释 private Jwt createJwt(String token, JWT parsedJwt) {try {// Verify the signatureJWTClaimsSet jwtClaimsSet this.jwtProcessor.process(parsedJwt, null);MapString, Object headers new LinkedHashMap(parsedJwt.getHeader().toJSONObject());MapString, Object claims this.claimSetConverter.convert(jwtClaimsSet.getClaims());// formatter:offreturn Jwt.withTokenValue(token).headers((h) - h.putAll(headers)).claims((c) - c.putAll(claims)).build();// formatter:on}catch (RemoteKeySourceException ex) {this.logger.trace(Failed to retrieve JWK set, ex);if (ex.getCause() instanceof ParseException) {throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, Malformed Jwk set), ex);}throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);}catch (JOSEException ex) {this.logger.trace(Failed to process JWT, ex);throw new JwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);}catch (Exception ex) {this.logger.trace(Failed to process JWT, ex);if (ex.getCause() instanceof ParseException) {throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, Malformed payload), ex);}throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);} }代码分析 JWTClaimsSet jwtClaimsSet this.jwtProcessor.process(parsedJwt, null); 这一行代码使用 jwtProcessor 对 parsedJwt 进行处理和验证。jwtProcessor 会根据 JWK 集中的公钥来验证 JWT 的签名。jwtProcessor 通常是一个 ConfigurableJWTProcessor 实例它使用配置的 JWKSource 来获取验证 JWT 签名所需的公钥。 MapString, Object headers new LinkedHashMap(parsedJwt.getHeader().toJSONObject()); 这行代码提取 JWT 的头部信息并将其转换为一个 Map 对象。 MapString, Object claims this.claimSetConverter.convert(jwtClaimsSet.getClaims()); 这行代码提取 JWT 的声明信息并使用 claimSetConverter 进行转换。转换后的声明信息也被存储在一个 Map 对象中。 return Jwt.withTokenValue(token) .headers((h) - h.putAll(headers)) .claims(© - c.putAll(claims)) .build(); 这段代码使用提取的头部信息和声明信息来构建一个新的 Jwt 对象。 异常处理 RemoteKeySourceException: 如果在获取 JWK 集时发生异常代码会捕获 RemoteKeySourceException 并记录错误信息。如果异常的原因是 ParseException则抛出一个 JwtException指明 JWK 集格式错误。否则抛出一个带有详细错误信息的 JwtException。 JOSEException: 如果在处理 JWT 时发生 JOSEException代码会捕获异常并记录错误信息然后抛出一个 JwtException。 其他异常: 如果发生其他类型的异常代码会捕获并记录错误信息。如果异常的原因是 ParseException则抛出一个 BadJwtException指明 JWT 负载格式错误。否则抛出一个带有详细错误信息的 BadJwtException。 使用 JWK 集进行 JWT 验证 createJwt 方法的核心部分是 this.jwtProcessor.process(parsedJwt, null);。这里的 jwtProcessor 使用 JWK 集中的公钥对 parsedJwt 进行签名验证。jwtProcessor 通常配置了一个 JWKSource它会从指定的 jwk-set-uri 获取 JWK 集确保 JWT 的签名是可信的。 通过这种方式NimbusJwtDecoder 使用 JWK 集对 JWT 进行验证和处理以确保 JWT 的有效性和完整性。 资源服务配置类 OAuth2ResourceServerConfigurer 是 Spring Security 提供的一个类用于配置 OAuth2 资源服务器的安全设置。以下是代码中每个方法的详细解释 方法详细解释 accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) public OAuth2ResourceServerConfigurerH accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {Assert.notNull(accessDeniedHandler, accessDeniedHandler cannot be null);this.accessDeniedHandler accessDeniedHandler;return this; }这个方法用于设置自定义的 AccessDeniedHandler。当客户端尝试访问受保护的资源但权限不足时将会调用这个处理器来处理 AccessDeniedException。 authenticationEntryPoint(AuthenticationEntryPoint entryPoint) public OAuth2ResourceServerConfigurerH authenticationEntryPoint(AuthenticationEntryPoint entryPoint) {Assert.notNull(entryPoint, entryPoint cannot be null);this.authenticationEntryPoint entryPoint;return this; }这个方法用于设置自定义的 AuthenticationEntryPoint。当认证失败或客户端尝试访问受保护的资源但未进行身份验证时将会调用这个入口点。 authenticationManagerResolver(AuthenticationManagerResolverHttpServletRequest authenticationManagerResolver) public OAuth2ResourceServerConfigurerH authenticationManagerResolver(AuthenticationManagerResolverHttpServletRequest authenticationManagerResolver) {Assert.notNull(authenticationManagerResolver, authenticationManagerResolver cannot be null);this.authenticationManagerResolver authenticationManagerResolver;return this; }这个方法用于设置自定义的 AuthenticationManagerResolver。它根据传入的 HttpServletRequest 决定使用哪个 AuthenticationManager 进行认证。 bearerTokenResolver(BearerTokenResolver bearerTokenResolver) public OAuth2ResourceServerConfigurerH bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {Assert.notNull(bearerTokenResolver, bearerTokenResolver cannot be null);this.bearerTokenResolver bearerTokenResolver;return this; }这个方法用于设置自定义的 BearerTokenResolver它用于从 HTTP 请求中解析 Bearer Token通常是从请求头中获取。 jwt() public JwtConfigurer jwt() {if (this.jwtConfigurer null) {this.jwtConfigurer new JwtConfigurer(this.context);}return this.jwtConfigurer; }这个方法启用对 JWT 编码的 Bearer Token 的支持并返回一个 JwtConfigurer 对象以便进一步配置 JWT 的相关设置。 jwt(CustomizerJwtConfigurer jwtCustomizer) public OAuth2ResourceServerConfigurerH jwt(CustomizerJwtConfigurer jwtCustomizer) {if (this.jwtConfigurer null) {this.jwtConfigurer new JwtConfigurer(this.context);}jwtCustomizer.customize(this.jwtConfigurer);return this; }这个方法启用对 JWT 编码的 Bearer Token 的支持并允许通过 Customizer 对 JwtConfigurer 进行进一步配置。 opaqueToken() public OpaqueTokenConfigurer opaqueToken() {if (this.opaqueTokenConfigurer null) {this.opaqueTokenConfigurer new OpaqueTokenConfigurer(this.context);}return this.opaqueTokenConfigurer; }这个方法启用对不透明 Bearer Token 的支持并返回一个 OpaqueTokenConfigurer 对象以便进一步配置不透明令牌的相关设置。 opaqueToken(CustomizerOpaqueTokenConfigurer opaqueTokenCustomizer) public OAuth2ResourceServerConfigurerH opaqueToken(CustomizerOpaqueTokenConfigurer opaqueTokenCustomizer) {if (this.opaqueTokenConfigurer null) {this.opaqueTokenConfigurer new OpaqueTokenConfigurer(this.context);}opaqueTokenCustomizer.customize(this.opaqueTokenConfigurer);return this; }这个方法启用对不透明 Bearer Token 的支持并允许通过 Customizer 对 OpaqueTokenConfigurer 进行进一步配置。 总结 OAuth2ResourceServerConfigurer 类提供了一系列方法用于配置 OAuth2 资源服务器的各种安全设置包括处理访问拒绝、身份验证入口点、自定义令牌解析器、以及支持 JWT 和不透明令牌的配置。这些方法允许开发者根据具体需求自定义资源服务器的行为以确保其安全性和灵活性。 资源服务YAML配置 OAuth2ResourceServerProperties 类用于配置 Spring Security OAuth2 资源服务器的一些属性。以下是该类中的配置项以及对应的 YAML 配置的解释和作用 OAuth2ResourceServerProperties 这个类包含两个内部类Jwt 和 OpaqueToken分别用于配置 JWT 和不透明令牌的属性。 YAML 配置示例 spring:security:oauth2:resourceserver:jwt:jwk-set-uri: https://example.com/.well-known/jwks.jsonissuer-uri: https://example.com/issuerpublic-key-location: classpath:public-key.pemjws-algorithms:- RS256audiences:- your-audienceopaqueToken:client-id: your-client-idclient-secret: your-client-secretintrospection-uri: https://example.com/introspect具体配置项及作用 JWT 配置项 spring:security:oauth2:resourceserver:jwt:jwk-set-uri: https://example.com/.well-known/jwks.jsonissuer-uri: https://example.com/issuerpublic-key-location: classpath:public-key.pemjws-algorithms:- RS256audiences:- your-audiencespring.security.oauth2.resourceserver.jwt.jwk-set-uri用于验证 JWT 令牌的 JSON Web Key (JWK) URI。spring.security.oauth2.resourceserver.jwt.issuer-uri可以是 OpenID Connect 发现端点或 RFC 8414 定义的 OAuth 2.0 授权服务器元数据端点的 URI。spring.security.oauth2.resourceserver.jwt.public-key-location包含用于验证 JWT 的公钥的文件的位置。spring.security.oauth2.resourceserver.jwt.jws-algorithms用于验证数字签名的 JSON Web 算法列表默认为 RS256。spring.security.oauth2.resourceserver.jwt.audiences标识 JWT 预期接收者的受众列表。 不透明令牌配置项 spring:security:oauth2:resourceserver:opaqueToken:client-id: your-client-idclient-secret: your-client-secretintrospection-uri: https://example.com/introspectspring.security.oauth2.resourceserver.opaqueToken.client-id用于与令牌检查端点进行认证的客户端 ID。spring.security.oauth2.resourceserver.opaqueToken.client-secret用于与令牌检查端点进行认证的客户端密钥。spring.security.oauth2.resourceserver.opaqueToken.introspection-uri用于执行令牌检查的 OAuth 2.0 端点。 总结 OAuth2ResourceServerProperties 类和相应的 YAML 配置项为 Spring Security OAuth2 资源服务器提供了丰富的配置选项。这些配置项允许开发者灵活地设置 JWT 和不透明令牌的验证细节从而确保应用程序的安全性和可靠性。
http://www.dnsts.com.cn/news/130127.html

相关文章:

  • 品牌网站建设小7a蝌蚪河南省建设厅网站中级职称
  • 做ppt的网站叫什么jsp酒店预订网站开发
  • 搭建博客网站网站开发需要做什么工作
  • 网站怎么做交易市场长春网站选网诚传媒
  • 做网站常用什么软件广州建网站站公司
  • 企业网站开发报价单自建团队网站开发要多少钱
  • 微网站怎么做的好名字吗58企业网站怎么做
  • 企业注册平台南京seo网站优化
  • 网站域名转出收费网站解决方案
  • 宁津有培训做网站的萝岗公司网站建设
  • 分类网站模版高端定制开发网站
  • 网站建设网站设计哪家专业阿里云ace+wordpress
  • 做电池网站的引导页沈阳全网推广公司哪家好
  • 网站的关键词在哪设置网站编程论文
  • 国外设计师作品网站做网站编辑有人带吗
  • 花瓣网网站模板修改wordpress的tag页
  • 嘉鱼网站建设前十磁力珠
  • 网站做移动适配网站开发转码手机
  • 网站布局设计规则网站的服务器每年都要续费的吗
  • 旅游的网站怎么做的网站怎么提升关键词排名
  • 可以自己做直播网站吗网络营销方式分析论文
  • 网站建设柒金手指花总15佛山专业网站建设公司
  • 电子商务网站用户行为分析及服务推荐农村自建房设计图纸及效果图大全
  • 网站公司企业宗旨此网站无法提供安全连接 建设银行
  • 做网站平台wordpress文章省略
  • 门户网站开发公司排名软件定制开发推荐
  • 国内优秀html网站织梦做的网站织梦修改网页模板
  • 网站怎么做修改商城平台
  • 网站建设服务费怎么入账天津的网站建设公司哪家好
  • 东明住房和城乡建设局网站网站建设存在的问题