我正在从 spring-boot
2.7.18
迁移到 3.3.3
并从
spring-security-oauth2
至 spring-boot-starter-oauth2-authorization-server
更新:
我的用例: 我有位于外部世界和我的私人网络之间的gateway服务。基本上它会拦截例如 https://example.com 域下的每个请求,现在当我的公共客户调用 https://example.com/oauth2/authorize gateway 服务将拦截请求时,它会弄清楚该用户没有活动会话,然后它将用户重定向到托管在域上的登录页面,该域与我的 oauth2 服务器的域不同。用户成功登录后gateway服务将重定向到最初请求的urlhttps://example.com/oauth2/authorize并将不记名令牌添加到请求标头。
接下来,在我的 oauth2 服务器上,我需要验证 JWT 承载令牌,如果它有效,则让授权库执行链中的下一个提供程序/过滤器。
我的
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
,但我发现自己编写了比应有的更复杂的代码,并发现这可能不是实现此目的的方法。
授权服务器必须首先对资源所有者进行身份验证。授权服务器验证资源所有者的方式(例如,用户名和密码登录、密钥、联合登录或使用已建立的会话)超出了本规范的范围。Spring 授权服务器文档中的示例重定向到登录表单以提交用户名和密码,但您希望通过不记名令牌进行身份验证。 Spring授权服务器
开发者建议您可以在authorizationEndpoint的authorizationRequestConverter中配置自定义的AuthenticationConverter:
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.authorizationEndpoint(endpoint -> endpoint
.authorizationRequestConverter(customAuthorizationRequestConverter))
自定义的 AuthenticationConverter 验证不记名令牌并从不记名令牌获取委托人:
@RequiredArgsConstructor
public class CustomAuthorizationRequestConverter implements AuthenticationConverter {
private static final OAuth2AuthorizationCodeRequestAuthenticationConverter DELEGATE =
new OAuth2AuthorizationCodeRequestAuthenticationConverter();
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;
}
var authentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) DELEGATE.convert(request);
if (authentication == null) {
return null;
}
Jwt jwt = getJwt(accessTokenValue);
String username = jwt.getClaimAsString(JwtClaimNames.SUB);
Collection<GrantedAuthority> authorities = GRANTED_AUTHORITIES_CONVERTER.convert(jwt);
var principal = new UsernamePasswordAuthenticationToken(username, null, authorities);
return new OAuth2AuthorizationCodeRequestAuthenticationToken(
authentication.getAuthorizationUri(),
authentication.getClientId(),
principal,
authentication.getRedirectUri(),
authentication.getState(),
authentication.getScopes(),
authentication.getAdditionalParameters());
}
}