Spring Boot多认证配置:HS256 JWT与OAuth2共存实现问询
可以实现,具体方案如下
核心思路是给目标端点配置多认证过滤器链:让自定义的HS256 JWT认证过滤器优先执行,若认证失败则不终止请求链,继续执行原有的OAuth2资源服务器过滤器。以下是分步实现细节:
1. 配置HS256对称密钥的JWT解码器与认证管理器
首先创建HS256专用的JWT解码器和认证管理器,用于验证对称签名的令牌:
import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtAuthenticationProvider; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.JwtValidators; import org.springframework.security.oauth2.jwt.MacAlgorithm; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; @Value("${app.hs256.secret}") // 从配置文件读取HS256对称密钥 private String hs256Secret; @Bean public JwtDecoder hs256JwtDecoder() { SecretKey secretKey = new SecretKeySpec( hs256Secret.getBytes(StandardCharsets.UTF_8), MacAlgorithm.HS256.getName() ); NimbusJwtDecoder decoder = NimbusJwtDecoder.withSecretKey(secretKey) .macAlgorithm(MacAlgorithm.HS256) .build(); // 可选:添加令牌有效期等通用校验 OAuth2TokenValidator<Jwt> validator = JwtValidators.createDefault(); decoder.setJwtValidator(validator); return decoder; } @Bean public AuthenticationManager hs256AuthenticationManager(JwtDecoder hs256JwtDecoder) { JwtAuthenticationProvider provider = new JwtAuthenticationProvider(hs256JwtDecoder); // 可选:如果HS256令牌需要解析scope权限,配置转换器 // provider.setJwtAuthenticationConverter(hs256JwtAuthenticationConverter()); return new ProviderManager(provider); } // 可选:HS256令牌的scope权限转换器(若需要和原有OAuth2权限规则兼容) // @Bean // public JwtAuthenticationConverter hs256JwtAuthenticationConverter() { // JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter(); // authoritiesConverter.setAuthorityPrefix("SCOPE_"); // authoritiesConverter.setScopeAttributeName("scope"); // // JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); // converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter); // return converter; // }
2. 自定义HS256认证过滤器
继承AbstractAuthenticationProcessingFilter,仅处理目标端点,且认证失败后不终止请求链,让后续的OAuth2过滤器继续尝试:
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HS256JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public HS256JwtAuthenticationFilter(AuthenticationManager authenticationManager) { // 指定仅处理目标端点 super("/api/target-endpoint-1/**", "/api/target-endpoint-2/**"); setAuthenticationManager(authenticationManager); // 配置认证失败逻辑:清空上下文,继续执行后续过滤器 setAuthenticationFailureHandler((request, response, exception) -> { SecurityContextHolder.clearContext(); getSuccessHandler().onAuthenticationSuccess(request, response, null); }); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String token = extractBearerToken(request); if (token == null) { throw new BadCredentialsException("No Bearer token found in request"); } // 解析令牌并封装为认证请求 Jwt jwt = ((JwtDecoder) getAuthenticationManager().getProviders().get(0).getJwtDecoder()).decode(token); JwtAuthenticationToken authRequest = new JwtAuthenticationToken(jwt); return getAuthenticationManager().authenticate(authRequest); } // 从请求头提取Bearer令牌 private String extractBearerToken(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { return authHeader.substring(7); } return null; } }
3. 修改原有SecurityFilterChain配置
将自定义的HS256过滤器添加到OAuth2资源服务器过滤器之前,并调整目标端点的授权规则:
@Bean public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager hs256AuthenticationManager) throws Exception { if (properties.isSecured()) { // 注册自定义HS256过滤器 HS256JwtAuthenticationFilter hs256Filter = new HS256JwtAuthenticationFilter(hs256AuthenticationManager); http.cors(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/actuator/**", "/system/v1/resources").permitAll() // 目标端点允许两种认证方式通过 .requestMatchers("/api/target-endpoint-1/**", "/api/target-endpoint-2/**").authenticated() .anyRequest().access(hasScope(SCOPE)) ) // 将HS256过滤器放在OAuth2资源服务器过滤器之前 .addFilterBefore(hs256Filter, OAuth2ResourceServerFilter.class) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); } return http.build(); }
关键注意事项
- HS256对称密钥需妥善保管,建议通过环境变量或加密配置文件加载,避免硬编码。
- 若HS256令牌需要兼容原有
hasScope(SCOPE)的授权规则,需配置JwtAuthenticationConverter解析令牌中的scope字段,生成对应权限。 - 自定义过滤器的失败处理必须清空Security上下文,否则会影响后续OAuth2认证的执行。
内容的提问来源于stack exchange,提问作者Fredo
相关产品推荐
相关产品推荐

