java 使用用户信息端点丰富的Oauth2 jwt令牌

flseospp  于 2023-02-02  发布在  Java
关注(0)|答案(2)|浏览(115)

我有一个spring应用程序,它公开了一些webflux端点,我使用jwt令牌来授权post调用,但我们还需要userinfo端点提供的信息。我现在有一个SecurityWebFilterChain bean,我们正在使用oauth2 ResourceServer配置,然后调用userinfodepoint进行进一步检查。验证jwt令牌,然后获取userinfo端点信息进行进一步验证的最佳方法是什么?
PS:授权服务器是第三部分。
没有外部调用user-info的安全配置

@Bean
  public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {

    http
      .cors()
      .and()
            .httpBasic().disable()
            .formLogin().disable()
            .csrf().disable()
            .logout().disable()
            .oauth2Client()
            .and()
      .authorizeExchange()
            .pathMatchers(HttpMethod.POST).authenticated()
            .anyExchange().permitAll()
            .and().oauth2ResourceServer().jwt()
            ;

    return http.build();
  }
i34xakig

i34xakig1#

UserInfo终结点是OpenID Connect 1.0的一部分,它返回访问令牌的用户信息。Spring Security不会自动从资源服务器(http.oauth2ResourceServer())调用它。
根据您的安全配置,您似乎希望同时使用OAuth2客户端(http.oauth2Client())和OAuth2资源服务器(http.oauth2ResourceServer())。OAuth2客户端不是为此用例设计的(从资源服务器调用UserInfo),因此需要定制以适应这种情况。相反,您可以简单地使用RestTemplateWebClient来自己调用UserInfo端点。
您可以在定制的Converter<Jwt, Collection<GrantedAuthority>>(或者在本例中是React式版本)中执行此操作,如下所示:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        http
            .authorizeExchange((authorize) -> authorize
                .anyExchange().authenticated()
            )
            .oauth2ResourceServer((oauth2) -> oauth2
                .jwt((jwt) -> jwt
                    .jwtAuthenticationConverter(jwtAuthenticationConverter())
                )
            );
        // @formatter:on

        return http.build();
    }

    private Converter<Jwt, Mono<AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        ReactiveJwtAuthenticationConverter converter = new ReactiveJwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter());
        return converter;
    }

    private Converter<Jwt, Flux<GrantedAuthority>> jwtGrantedAuthoritiesConverter() {
        JwtGrantedAuthoritiesConverter delegate = new JwtGrantedAuthoritiesConverter();
        return (jwt) -> getUserInfo(jwt.getTokenValue())
            .flatMapIterable((userInfo) -> {
                Collection<GrantedAuthority> authorities = delegate.convert(jwt);
                // TODO: Add authority from userInfo...
                return authorities;
            });
    }

    private Mono<Map<String, String>> getUserInfo(String accessToken) {
        // TODO: Call user info and extract one or more claims from response
        return Mono.just(new HashMap<>());
    }

}
kpbwa7wx

kpbwa7wx2#

解码来自JWT的数据比查询外部端点要高效得多。
因此,最好的选择是配置授权服务器(即使它是第三方),以使用授权所需的数据来丰富JWT(访问和ID令牌)。大多数OIDC授权服务器都支持它,只需参考其文档(Keycloak、Auth 0、Cognito...)。
一旦你需要的所有声明都在access-token中,你就可以从security-context中的JwtAuthenticationToken示例(或者OAuth2AuthenticationToken,对于使用oauth2login的客户端应用)在resource-server上读取它。

@PostMapping("/answers/{subject}")
@PreAuthorize("#userSubject == #auth.token.claims['sub']")
public ResponseEntity<String> addAnswer(@PathVariable("subject") String userSubject, @RequestBody @Valid AnswerDto, JwtAuthenticationToken auth) {
     ...
}

相关问题