【问题标题】:No AuthenticationProvider found on refresh token - Spring OAuth2 java config在刷新令牌上找不到 AuthenticationProvider - Spring OAuth2 java config
【发布时间】:2019-05-05 00:59:49
【问题描述】:

我有一个 Spring Boot 项目,我在其中配置了部分有效的 Spring OAuth2 身份验证过程。我可以进行身份​​验证,但是当我尝试获取刷新令牌时出现异常。

OAuth 配置:

@Configuration
public class OAuth2ServerConfiguration {

    private static final String RESOURCE_ID = "xxx";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests()
            .antMatchers("/api/**").authenticated();
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Value("${clientDetailsService.clientName}")
        private String clientName;

        @Value("${clientDetailsService.clientSecret}")
        private String clientSecret;

        @Autowired
        @Qualifier("authenticationManager")
        private AuthenticationManager authenticationManager;

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        @Qualifier("tokenServices")
        private AuthorizationServerTokenServices tokenServices;

        @Autowired
        @Qualifier("codeServices")
        private AuthorizationCodeServices codeServices;

        @Autowired
        @Qualifier("requestFactory")
        private OAuth2RequestFactory requestFactory;

        @Autowired
        @Qualifier("tokenGranter")
        private TokenGranter tokenGranter;

        private final TokenStore tokenStore = new InMemoryTokenStore();

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.setClientDetailsService(clientDetailsService);
            endpoints.tokenServices(tokenServices)
                        .tokenStore(tokenStore)
                        .authorizationCodeServices(codeServices)
                        .authenticationManager(authenticationManager)
                        .requestFactory(requestFactory)
                        .tokenGranter(tokenGranter);
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.withClientDetails(clientDetailsService);
        }


        @Bean(name = "tokenGranter")
        @Primary
        public TokenGranter tokenGranter() {
            final List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();

            tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, codeServices, clientDetailsService, requestFactory));
            tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
            tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));

            return new CompositeTokenGranter(tokenGranters);
        }

        @Bean
        @Primary
        public ClientDetailsService clientDetailsService(){
            final InMemoryClientDetailsServiceBuilder builder = new InMemoryClientDetailsServiceBuilder();
            builder.withClient(clientName)
                    .authorizedGrantTypes("password", "refresh_token")
                    .authorities("USER")
                    .scopes("read", "write")
                    .resourceIds(RESOURCE_ID)
                    .secret(clientSecret);

            try {
                return builder.build();
            } catch (final Exception e) {
                e.printStackTrace();
            }

            return null;

        }

        @Bean(name = "tokenServices")
        @Primary
        public AuthorizationServerTokenServices tokenServices() {
            final DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setClientDetailsService(clientDetailsService);
            tokenServices.setTokenStore(tokenStore);
            tokenServices.setAuthenticationManager(authenticationManager);
            return tokenServices;
        }

        @Bean(name = "requestFactory")
        @Primary
        public OAuth2RequestFactory requestFactory() {
            return new DefaultOAuth2RequestFactory(clientDetailsService);
        }

        @Bean(name = "codeServices")
        @Primary
        public AuthorizationCodeServices authorizationCodeServices() {
            return new InMemoryAuthorizationCodeServices();
        }
    }

我还定义了一些自定义组件,例如自定义令牌授予者、自定义身份验证提供程序等。如有必要,我会发布它们。

正如我所说,身份验证流程可以正常工作。当我发布到 /oauth/token 时,我得到了一个令牌和一个刷新令牌,但是当我尝试用我的刷新令牌交换一个新令牌时(通过使用 grant_type=refresh_token 和 refresh_token=my refresh token 发布 http://localhost:8080/oauth/token)我得到一个例外:

未找到 org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken 的 AuthenticationProvider

在哪里设置身份验证提供程序?如何让 Spring 也将我的自定义身份验证提供程序用于刷新令牌?

【问题讨论】:

    标签: java spring authentication oauth spring-boot


    【解决方案1】:

    我通过显式定义 PreAuthenticationProvider 解决了这个问题:

    @Component("preAuthProvider")
    public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider {
    
        @Autowired
        private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> userService;
    
        public CustomPreAuthProvider(){
            super();
        }
    
        @PostConstruct
        public void init(){
            super.setPreAuthenticatedUserDetailsService(userService);
        }
    }
    

    然后是自定义用户服务:

    @Service
    public class CustomPreAuthUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {
    
        @Override
        public final UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token) {
        ...    
        }
    }
    

    并将此提供程序添加到 oauth2 配置中:

    @Autowired
    private AuthenticationProvider authenticationProvider;
    
    @Autowired
    @Qualifier("preAuthProvider")
    private AuthenticationProvider preAuthProvider;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider).authenticationProvider(preAuthProvider);
    }
    

    【讨论】:

    • 这对我帮助很大,但你能指导我一点关于 loadUserDetails 的操作吗?谢谢
    • loadUserDetails 是您加载请求用户的位置。如何执行此操作(从数据库、内存、通过 Web 服务调用)取决于您的实现。 @AciD
    【解决方案2】:

    作为 Jesper 回答的另一种方法,如果您想为此目的重用当前的 UserDetailsService,您可以像 Spring 使用他们的 DefaultTokenServices 一样执行此操作:

    @Bean
    public CustomTokenServices tokenServices() {
        CustomTokenServices tokenServices = new CustomTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setReuseRefreshToken(false);
        tokenServices.setClientDetailsService(clientDetailsService);
        tokenServices.setAuthenticationManager(createPreAuthProvider());
        return tokenServices;
    }
    
    private ProviderManager createPreAuthProvider() {
        PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
        provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
        return new ProviderManager(Arrays.asList(provider));
    }
    

    UserDetailsService 被自动连接到这个 @Configuration 类中。

    【讨论】:

      【解决方案3】:

      我不能不同意 Jesper 的回答,但就我而言,同样的错误已被修复:

      tokenServices.setAuthenticationManager(authenticationManager) 
      

      来自tokenService()

      【讨论】:

      • 不设置 authenticationManager “修复”错误,但我不推荐它。我已经调试了spring的DefaultTokenServices,如果authenticationManager为null,刷新令牌不会执行重新身份验证,这意味着它永远不会进入UserDetailsService来检查帐户是否仍然处于活动状态,没有被阻止等。
      猜你喜欢
      • 2012-07-21
      • 2015-08-31
      • 2017-12-02
      • 2015-06-02
      • 2017-04-14
      • 2016-03-07
      • 1970-01-01
      • 2017-04-20
      • 1970-01-01
      相关资源
      最近更新 更多