我正在从 spring-boot
2.7.18
迁移到 3.3.3
并从
spring-security-oauth2
至 spring-boot-starter-oauth2-authorization-server
我的用例与新版
oauth2-authorization-server
中的示例有点不同。资源所有者不使用基本的身份验证登录表单进行身份验证,而是使用由不同服务生成的不记名令牌访问我的 OAuth2 服务器。我需要通过 JWT Bearer 令牌对它们进行身份验证。
我的
oauth2-server
有这个defaultSecurityFilterChain
配置
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/oauth/authorize")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterAfter(new CustomJwtAuthenticationFilter(secret), BasicAuthenticationFilter.class)
.addFilterBefore(new CustomHSTSFilter(), CustomJwtAuthenticationFilter.class)
与旧的
spring-security-oauth2
库配合得很好
现在,由于新的 Spring Security 中发生了很多变化,并且
oauth2-authorization-server
我已将 defaultSecurityFilterChain
配置更改为这个
http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/oauth2/authorize","/hello").authenticated())
.addFilterAfter(new CustomJwtAuthenticationFilter(secret), BasicAuthenticationFilter.class)
.addFilterBefore(new CustomHSTSFilter(), CustomJwtAuthenticationFilter.class);
不幸的是,这不起作用,如果有人可以解释我应该如何配置安全过滤器链才能工作,我将不胜感激。
我定义的
/hello
端点工作正常,但是当我向 GET /oauth2/authorize?client_id=....
发送请求时,我得到 403 Pre-authenticated entry point called. Rejecting access
我可以在我的日志中看到我的 CustomJwtAuthenticationFilter
被调用并且 GET /oauth2/authorize?client_id=....
得到保护,但随后它尝试保护 GET /error?client_id=...
并在 AuthorizationFilter
中失败,我收到拒绝访问消息 Pre-authenticated entry point called. Rejecting access
。
有谁知道我的配置出了什么问题,我错过了吗?
我尝试在我的
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
中添加 defaultSecurityFilterChain
,然后 /error
获得安全保护,但我收到 404,没有任何消息。
我也尝试添加
.requestMatchers("/error").permitAll()
具有相同的 404 结果
我还尝试通过在
authorizationServerSecurityFilterChain
上添加自定义 authorizationRequestConverter
来更改我的 authorizationEndpoint
,但我发现自己编写了比应有的更复杂的代码,并发现这可能不是实现此目的的方法。
授权服务器必须首先对资源所有者进行身份验证。授权服务器验证资源所有者的方式(例如,用户名和密码登录、密钥、联合登录或使用已建立的会话)超出了本规范的范围。
您可以在authorizationEndpoint的authorizationRequestConverter中配置自定义的AuthenticationConverter:自定义 AuthenticationConverter 实际上会验证承载令牌并将主体放入安全上下文中,而不是将请求转换为身份验证:Spring 授权服务器文档中的示例重定向到登录表单以提交用户名和密码,但您希望通过不记名令牌进行身份验证。 Spring授权服务器
开发者建议
@RequiredArgsConstructor
public class CustomAuthorizationRequestConverter implements AuthenticationConverter {
private static final DefaultBearerTokenResolver BEARER_TOKEN_RESOLVER =
new DefaultBearerTokenResolver();
private static final JwtGrantedAuthoritiesConverter GRANTED_AUTHORITIES_CONVERTER =
new JwtGrantedAuthoritiesConverter();
private final JwtDecoder jwtDecoder;
private Jwt getJwt(String encoded) {
try {
return jwtDecoder.decode(encoded);
} catch (JwtException e) {
throw new OAuth2AuthorizationCodeRequestAuthenticationException(
new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, e.getMessage(), null), e, null);
}
}
@Override
public Authentication convert(HttpServletRequest request) {
String accessTokenValue = BEARER_TOKEN_RESOLVER.resolve(request);
if (accessTokenValue == null) {
return null;
}
Jwt jwt = getJwt(accessTokenValue);
String username = jwt.getClaimAsString(JwtClaimNames.SUB);
Collection<GrantedAuthority> authorities = GRANTED_AUTHORITIES_CONVERTER.convert(jwt);
var authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
return null;
}
}