【问题标题】:spring jwt decoder openid tokenspring jwt解码器openid令牌
【发布时间】:2019-06-27 23:02:16
【问题描述】:

外部 OAuth2 提供程序没有公共 JwkUri,所以我也尝试使用以下代码 sn-p 覆盖默认行为:

@EnableWebSecurity
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
 @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("**/oauth2/code/esia**", "**/code/esia**", "**esia**").permitAll()
            .antMatchers("/user").fullyAuthenticated()
            .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .cors().disable()
            .oauth2Client()
            .clientRegistrationRepository(this.clientRegistrationRepository)
            .authorizationCodeGrant()
            .authorizationRequestResolver(new CustomAuthorizationRequestResolver(
                    this.clientRegistrationRepository, esiaConfig, signatureUtil, timeUtil))
            .accessTokenResponseClient(customAccessTokenResponseClient())
            .and().and().oauth2Login().tokenEndpoint().accessTokenResponseClient(customAccessTokenResponseClient())
            .and().and().oauth2ResourceServer().jwt();
}
@Bean
JwtDecoder jwtDecoder() {
 return  new CustomJwtDecoder();
}
}

class CustomJwtDecoder implements JwtDecoder {
@Override
public Jwt decode(String token) throws JwtException {
    System.out.println(token);
    return null;
}
}

但是 Spring Security 不知何故仍然使用默认实现,我收到以下错误...

[missing_signature_verifier] Failed to find a Signature Verifier for Client Registration: 'esia'. Check to ensure you have configured the JwkSet URI.

另外,我尝试设置自定义 AuthenticationProvider 但 spring 忽略了它。

我想问题是 spring 的 OAuth2LoginConfigurer 方法 init(B http) 调用了 new OidcAuthorizationCodeAuthenticationProvider(accessTokenResponseClient, oidcUserService)

【问题讨论】:

    标签: spring spring-security oauth-2.0 spring-security-oauth2


    【解决方案1】:

    对于 5.1.3.RELEASE,您似乎无法轻松解决此问题。

    源于OidcAuthorizationCodeAuthenticationProvider.getJwtDecoder

    这发生在line 156,这是对私有方法的调用

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            ...
            OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
            ...
        }
    

    我看到的一个选项是,如果您复制此代码并自己修改它。扩展类本身没有意义,因为所有逻辑都发生在 authenticate 方法中。所以你仍然覆盖它。然后你使用http.authenticationProvider 方法添加你的提供者

    另一种选择是覆盖OAuth2LoginConfigurer 类的SecurityConfigurerAdapter.postProcess 方法并在那里做一些聪明的事情。就像通过反射填充JWT decoder map

    两者都不是公认的首选解决方案。我相信这就是 5.2 版本发生重构的原因。

    鉴于最新的 5.2.x 版本 你快到了,但你必须覆盖正确的 bean

        @Bean
        public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
            final JwtDecoder decoder = jwtDecoder();
            return context -> decoder;
    
        }
    

    如果你不想使用 lambdas

        @Bean
        public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
            final JwtDecoder decoder = jwtDecoder();
            return new JwtDecoderFactory<ClientRegistration>() {
                @Override
                public JwtDecoder createDecoder(ClientRegistration context) {
                    return decoder;
                }
            };
    
        }
    

    我是怎么想出来的,我看了一下OAuth2LoginConfigurer.java

        JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = getJwtDecoderFactoryBean();
    

    获取 bean 的私有方法看起来像 this

        private JwtDecoderFactory<ClientRegistration> getJwtDecoderFactoryBean() {
            ResolvableType type = ResolvableType.forClassWithGenerics(JwtDecoderFactory.class, ClientRegistration.class);
            String[] names = this.getBuilder().getSharedObject(ApplicationContext.class).getBeanNamesForType(type);
            if (names.length > 1) {
                throw new NoUniqueBeanDefinitionException(type, names);
            }
            if (names.length == 1) {
                return (JwtDecoderFactory<ClientRegistration>) this.getBuilder().getSharedObject(ApplicationContext.class).getBean(names[0]);
            }
            return null;
        }
    

    【讨论】:

    • 我用的是最新版本5.1.3.Release,还没有这个Factory。
    • 我已经更新了答案。除了用您自己的身份验证提供程序替换整个身份验证提供程序之外,没有好的解决方案。
    【解决方案2】:

    即使是 5.2.x 版本,我也面临同样的问题。就我而言,真正的问题不在于 JwtDecoder。我已经通过设置 jwk-set-uri 属性解决了这个问题(您可以通过您使用的提供商更改提供商名称,例如 okta、google 等):

    security.oauth2.client.provider.azure.jwk-set-uri: https://login.microsoftonline.com/{tenant}/discovery/keys
    
    

    【讨论】:

      【解决方案3】:

      (在寻找覆盖 Jwt 和 Oidc 令牌验证的解决方案时发现了这个。Filip 的 answer 帮助我找到了解决方案,所以我想我会添加这个来帮助任何遵循相同搜索的人。)

      对于时间旅行测试场景,我们的 jvm 时钟设置为未来几个月。由于对 Jwt 和 Oidc 令牌时间戳进行验证,登录失败。

      此添加适用于我们在 Spring Security 5.2.1 上的应用

      @Bean
      public JwtDecoderFactory<ClientRegistration> getJWTDecoder() {
          OidcIdTokenDecoderFactory factory = new OidcIdTokenDecoderFactory();
          factory.setJwtValidatorFactory(new Function<ClientRegistration, OAuth2TokenValidator<Jwt>>() {
              @Override
              public OAuth2TokenValidator<Jwt> apply(ClientRegistration clientRegistration) {
                  return new CustomTimestampIgnoringOidcTokenValidator(clientRegistration);
              }
          });
      }
      

      这只是将默认验证器替换为仅验证其他声明的自定义验证器。

      【讨论】:

        猜你喜欢
        • 2021-12-30
        • 2018-10-05
        • 2021-07-11
        • 2019-05-19
        • 2018-08-14
        • 2020-05-23
        • 2016-11-15
        • 2021-06-30
        相关资源
        最近更新 更多