在刷新令牌上找不到AuthenticationProvider - Spring OAuth2 java config

时间:2016-01-11 08:02:52

标签: java spring authentication oauth spring-boot

我有一个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();
        }
    }

我还定义了一些自定义组件,如自定义令牌Granter,自定义身份验证提供程序等。如果需要,我会发布它们。

正如我所说,身份验证流程正常。当我发送到/ oauth / token时,我得到一个令牌和刷新令牌,但是当我尝试将刷新令牌换成新令牌时(通过使用grant_type = refresh_token和refresh_token =我的刷新令牌POST http://localhost:8080/oauth/token)我得到一个例外:

找不到org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken

的AuthenticationProvider

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

3 个答案:

答案 0 :(得分:7)

我通过明确定义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 config:

@Autowired
private AuthenticationProvider authenticationProvider;

@Autowired
@Qualifier("preAuthProvider")
private AuthenticationProvider preAuthProvider;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider).authenticationProvider(preAuthProvider);
}

答案 1 :(得分:1)

我不能不同意Jesper的回答,但就我而言,该错误已得到修复,可以消除:

tokenServices.setAuthenticationManager(authenticationManager) 

来自tokenService()

答案 2 :(得分:0)

作为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类中。