向 /oauth2/authorize 发送请求时如何使用 JWT 验证资源所有者?

问题描述 投票:0回答:1

我正在从 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
,但我发现自己编写了比应有的更复杂的代码,并发现这可能不是实现此目的的方法。

spring-security oauth-2.0 migration spring-security-oauth2 spring-authorization-server
1个回答
0
投票
OAuth 2 规范第 3.1 节要求授权端点:

授权服务器必须首先对资源所有者进行身份验证。授权服务器验证资源所有者的方式(例如,用户名和密码登录、密钥、联合登录或使用已建立的会话)超出了本规范的范围。

Spring 授权服务器文档中的示例重定向到登录表单以提交用户名和密码,但您希望通过不记名令牌进行身份验证。 Spring授权服务器

开发者建议
您可以在authorizationEndpoint的authorizationRequestConverter中配置自定义的AuthenticationConverter:

http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .authorizationEndpoint(endpoint -> endpoint .authorizationRequestConverter(customAuthorizationRequestConverter))

自定义 AuthenticationConverter 实际上会验证承载令牌并将主体放入安全上下文中,而不是将请求转换为身份验证:
@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;
  }
}


© www.soinside.com 2019 - 2024. All rights reserved.