如何在 Spring 授权服务器上使用 JWT 令牌对 /oauth2/authorize 请求进行身份验证?

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

我正在从 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-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 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()); } }
    
© www.soinside.com 2019 - 2024. All rights reserved.