Spring security OAuth2 Refresh Token - IllegalStateException,需要 UserDetailsS​​ervice

Posted

技术标签:

【中文标题】Spring security OAuth2 Refresh Token - IllegalStateException,需要 UserDetailsS​​ervice【英文标题】:Spring security OAuth2 Refresh Token - IllegalStateException, UserDetailsService is required 【发布时间】:2018-02-13 00:45:49 【问题描述】:

我是 Spring 安全性的新手,我正在开发带有 OAuth2 身份验证和授权的 spring REST API。一切正常,但是当我请求刷新令牌时,我得到的错误是 -

org.springframework.security.oauth2.provider.endpoint.TokenEndpoint handleException IllegalStateException,需要 UserDetailsS​​ervice。

注意:我正在使用自定义身份验证提供程序来验证来自数据库的用户凭据。

这是我的代码:

网络安全配置适配器:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter 

@Autowired
private ClientDetailsService clientDetailsService;

@Autowired
private CustomAuthenticationProvider authProvider;

@Override
protected void configure(
  AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(authProvider);


@Override
protected void configure(HttpSecurity http) throws Exception 
    http
    .csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers("/oauth/token").permitAll();


@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception 
    return super.authenticationManagerBean();



@Bean
public TokenStore tokenStore() 
    return new InMemoryTokenStore();


@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore)
    TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
    handler.setTokenStore(tokenStore);
    handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
    handler.setClientDetailsService(clientDetailsService);
    return handler;


@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception 
    TokenApprovalStore store = new TokenApprovalStore();
    store.setTokenStore(tokenStore);
    return store;



自定义身份验证提供程序

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider 

@Override
public Authentication authenticate(Authentication authentication) 
  throws AuthenticationException 

    String username = authentication.getName();
    String password = authentication.getCredentials().toString();
    Ss_admin_service ss_admin_service = new Ss_admin_service();
    Ss_admin ss_admin = new Ss_admin();
    ss_admin.setA_password(password);
    ss_admin.setA_username(username);
    ss_admin_service.doLogin(ss_admin);
    if(!ss_admin_service.doLogin(ss_admin)) 
        throw new BadCredentialsException("Invalid username/password");
    
    return new UsernamePasswordAuthenticationToken(username, password, (Collection<? extends GrantedAuthority>) new ArrayList<>());


@Override
public boolean supports(Class<?> authentication) 
    return authentication.equals(UsernamePasswordAuthenticationToken.class);



授权服务器配置

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter 

private static String REALM="MY_OAUTH_REALM";

@Autowired
private TokenStore tokenStore;

@Autowired
private UserApprovalHandler userApprovalHandler;

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

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception 

    clients.inMemory()
        .withClient("my-trusted-client")
        .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
        .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
        .scopes("read", "write", "trust")
        .secret("secret")
        .accessTokenValiditySeconds(10).//Access token is only valid for 10 sec for testing.
        refreshTokenValiditySeconds(10);//Refresh token is only valid for 10 sec for testing.


@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
    endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
            .authenticationManager(authenticationManager);


@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception 
    oauthServer.realm(REALM+"/client");



资源服务器配置

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter 

private static final String RESOURCE_ID = "SPRING_REST_API";

@Override
public void configure(ResourceServerSecurityConfigurer resources) 
    resources.resourceId(RESOURCE_ID).stateless(false);


@Override
public void configure(HttpSecurity http) throws Exception 
    http
    .anonymous().disable()
    .requestMatchers().antMatchers("/admin/**")
    .and().authorizeRequests()
    .antMatchers("/admin/**").access("hasRole('ADMIN')")
    .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());



【问题讨论】:

以上问题现在解决了吗?? 【参考方案1】:

在我的例子中,Nelio Alves' answer 几乎成功了,但它开始为每个请求返回“访问此资源需要完全身份验证”。相反,我不得不在WebSecurityConfigurerAdapter 上进行注入,大致基于Vijay Nandwana's answer to another similar question。

这是我的代码,供参考:

public class MyWebSecurityConfigurer extends WebSecurityConfigurerAdapter 

    @Autowired
    private AtsUserDetailsService userDetailsService;

    (...)

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        this.endpoints.getEndpointsConfigurer().userDetailsService(this.userDetailsService);
        
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/oauth/token").permitAll()
            .anyRequest().fullyAuthenticated();
    

    (...)


【讨论】:

【参考方案2】:

在您的 AuthorizationServerConfigurerAdapter 中注入 UserDetailsS​​ervice:

@Autowired
private UserDetailsService userDetailsService;

然后在这个configure方法上进行配置:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
    
    endpoints
    ...

    .userDetailsService(userDetailsService);

【讨论】:

如果我创建了一个AuthorizationServerConfigurerAdapter,API 会突然开始返回“访问此资源需要完全身份验证”,即使没有其他任何更改。【参考方案3】:

我已经解决了这个问题,可以使用endpoints.tokenServices()

另请参阅:Refresh token call fails using spring security an oauth2 with error: UserDetailsService is required

【讨论】:

【参考方案4】:

您可以尝试将 ClientDetailsS​​ervice 注入到授权服务器配置中并进行如下配置;

授权服务器配置

.....

@Autowired
private ClientDetailsService clientDetailsService;

......
......

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

【讨论】:

以上是关于Spring security OAuth2 Refresh Token - IllegalStateException,需要 UserDetailsS​​ervice的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security OAuth2 设置 - 无法找到 oauth2 命名空间处理程序

Spring Security OAuth2 v5:NoSuchBeanDefinitionException:'org.springframework.security.oauth2.jwt.Jwt

如何使用spring-security,oauth2调整实体的正确登录?

针对授权标头的Spring Security OAuth2 CORS问题

Spring Security---Oauth2详解

OAuth2.0学习(4-1)Spring Security OAuth2.0 - 代码分析