问题
I am using Spring Security OAuth2 for authorizations. When trying to refresh the token I get an error: UserDetailsService is required
(interestingly I get this error only on unix machines and not on windows). I am using Spring OAuth2 version 2.0.7.
For some reason the AuthenticationManager
in the DefaultTokenService
is not empty and it tries to authenticate the user to check if he still exists. I think it gets initialized because of some spring security vs. spring oauth2 configuration problems.
I am not using any custom UserDetailsService
, hence it should not authenticate the users at this point. However, when I debug it I see that it tries to use one from the WebSecurityConfigurerAdapter
and gets to this error. Even if I provide my custom dummy UserDetailsService
, it is not using that one, but tries to use the other one, which is null. Am I missing here something? I can not find out why is this happening?
Here is my Oauth2 configuration
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private MySpringTokenStore tokenStore;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private MyClientDetailsServiceImpl clientDetailsService;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
endpoints.authenticationManager(authenticationManager)
.approvalStoreDisabled();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
Here is my Spring security configuration
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.antMatchers("/myRest/events/**", "/events/**", "/events", "/myRest/events").permitAll()
.antMatchers("/login.jsp", "/login").permitAll()
.and()
.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/myRest/events")).disable()
.sessionManagement().sessionFixation().none();
// @formatter:on
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/index*", "/myRest/events/**", "/events/**", "/myRest/events", "/events", "/swagger/**", "/kibana/**",
"/elastic/**", "/version/**", "/api-docs/**", "/js/**", "/oauth/uncache_approvals", "/oauth/cache_approvals");
}
}
回答1:
Authorization server endpoint needs UserDetailsService
. In your OAuth2Config
class configure user details service like the following:
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore);
endpoints.userDetailsService(userDetailsService);
endpoints.authenticationManager(authenticationManager)
.approvalStoreDisabled();
}
You can also configure it in WebSecurityConfigurerAdapter
:
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
@Override
protected void configure(HttpSecurity http) throws Exception {
if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
// @formatter:off
http
.authorizeRequests()
.antMatchers("/myRest/events/**", "/events/**", "/events", "/myRest/events").permitAll()
.antMatchers("/login.jsp", "/login").permitAll()
.and()
.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize")).disable()
.csrf().requireCsrfProtectionMatcher(new AntPathRequestMatcher("/myRest/events")).disable()
.sessionManagement().sessionFixation().none();
// @formatter:on
}
回答2:
If implementing custom DefaultTokenServices
, we don't need UserDetailsService
.
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// ...
.tokenServices(tokenServices(endpoints));
}
public AuthorizationServerTokenServices tokenServices(final AuthorizationServerEndpointsConfigurer endpoints) {
final DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
// ...
tokenServices.setAuthenticationManager(
new ProviderManager(List.of(new MyCustomAuthProvider())));
return tokenServices;
}
}
The commit message says:
Add AuthenticationManager to default token services
So that it can be used to check user account changes in a refresh token grant. If a global UserDetailsService is available it will be used as a default (e.g. if user has a GlobalAuthenticationConfigurer). It works by constructing a PreAuthenticationAuthenticationProvider and using that the authenticate the user in DefaultTokenServices. To customize that process, users can create their own DefaultTokenServices and inject an AuthenticationManager.
Fixes gh-401
来源:https://stackoverflow.com/questions/43842659/refresh-token-call-fails-using-spring-security-an-oauth2-with-error-userdetails