【问题标题】:Oauth2 - java.lang.IllegalStateException: UserDetailsService is required. with refresh tokenOauth2 - java.lang.IllegalStateException: UserDetailsS​​ervice 是必需的。带刷新令牌
【发布时间】:2020-06-08 15:53:15
【问题描述】:

我正在尝试在 Spring Boot 中使用 jwt 实现 oauth2,并且身份验证有效,但是当我想要获取 refresh_token 时发生错误,指示以下内容...

java.lang.IllegalStateException: UserDetailsS​​ervice 是必需的。 在 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsS​​erviceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:464) 在 org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:68) 在 org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:103) 在 org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175) 在 org.springframework.security.oauth2.provider.token.DefaultTokenServices.refreshAccessToken(DefaultTokenServices.java:150)

我做错了什么?

这些是我的文件

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

@Value("${token.secret}")
private String secret;
@Value("${server.servlet.context-path}")
private String contextPath;
@Value("${oauth2.client.id}")
public String  CLIENT_ID;
@Value("${oauth2.client.secret}")
public String  CLIENT_SECRET;
@Value("${oauth2.scope.read}")
public String  SCOPE_READ;
@Value("${oauth2.grant.types}")
public String  GRANT_TYPES;
@Value("${oauth2.scopes}")
public String  SCOPES;
@Value("${oauth2.access.token.validity}")
public Integer  TOKEN_VALID_SECONDS;
@Value("${oauth2.refresh.token.validity}")
public Integer  REFRESH_TOKEN_VALID_SECONDS;


@Autowired
private DataSource dataSource;

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

@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}

@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception { 
    clients.inMemory()
            .withClient(CLIENT_ID)
            .secret(passwordEncoder().encode(CLIENT_SECRET))
            .authorizedGrantTypes("password", "refresh_token", "client_credentials","authorization_code")
            .scopes("read", "write", "trust")
            .accessTokenValiditySeconds(TOKEN_VALID_SECONDS)
            .refreshTokenValiditySeconds(REFRESH_TOKEN_VALID_SECONDS);
} 

@Bean
@Primary
public DefaultTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setTokenStore(tokenStore());
    defaultTokenServices.setSupportRefreshToken(true);
    return defaultTokenServices;
}

@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
    endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);

}

@Bean
public TokenStore tokenStore() {
    return new JdbcTokenStore(dataSource);
}


@Bean
public TokenEnhancer tokenEnhancer() {
    return new CustomTokenEnhancer();
}

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
}

这是我的 SecurityConfig 类

@EnableGlobalMethodSecurity(securedEnabled=true)
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private final CustomUserDetailsService customUserDetailsService;
@Autowired
public BCryptPasswordEncoder passwordEncoder;

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

@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(customUserDetailsService)
    .passwordEncoder(passwordEncoder);
}

@Override
public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/oauth/**").permitAll()
    .antMatchers("/oauth/token/**").permitAll()
    .antMatchers("/api/**" ).authenticated()
    .anyRequest().authenticated()
    .and().formLogin().permitAll()
    .and().csrf().disable();

}

}

public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UserService userService;
//login web service url describe on properties file
@Value("${loginServiceUri}")
public String loginServiceUri;
//login web service enabled 
@Value("${loginWebServiceEnabled}")
public Boolean loginWebServiceEnabled;


@Override
public UserDetails loadUserByUsername(String username) {

    log.debug("Trying to authenticate user with details....");

    if (loginWebServiceEnabled && loginServiceUri != null){
        //its necessary to call external Web Service to find the user and then look for 
        //into database
        UserDto userDto = this.loginWebService(username);

        if (userDto != null) {
            // look for the user in data base
            log.debug("User found at external login web service trying to look for at data base");
            return lookUserDataBase(userDto.getUsername());
        } else {
            log.error("User not found at external login web service", username);
            throw new UsernameNotFoundException(username);
        }

    } else {
        // look for the user in data base
        return lookUserDataBase(username);
    }


}

/**
 * Look for use in data base by user name

 * @return
 */
private UserDetails lookUserDataBase(String userName) {
    UserEntity user = userService.findEntityByUsername(userName);
    if (user == null) {
        log.error("User not found in data base", userName);
        throw new UsernameNotFoundException(userName);
    }
    log.debug("User found in data base with userName: " + userName + " and authorities: " + user.getUserAuthority().toString());
    return new UserAuthenticatedDetails(user);
}


/**
 *  Example Login Web Service call login 
 * @param name
 * @return
 */
private UserDto loginWebService (String name){
    xxxxxxxxxxx
}

}

【问题讨论】:

  • 能否添加CustomUserDetailsS​​ervice类
  • 我附上了文件

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


【解决方案1】:

我在 SecurityConfig 中用这行解决了;

 public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
    endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);

    //this line solved the problem
    endpoints.userDetailsService(customUserDetailsService);**


}

【讨论】:

    【解决方案2】:

    将这些添加到您的 WebSecurityConfigurerAdapter 类中

    @Autowired
    public void setApplicationContext(ApplicationContext context) {
        super.setApplicationContext(context);
        AuthenticationManagerBuilder globalAuthBuilder = context
                .getBean(AuthenticationManagerBuilder.class);
        try {
            globalAuthBuilder.userDetailsService(userDetailsService);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    有一个“本地”AuthenticationManagerBuilder 和一个“全局”AuthenticationManagerBuilder,我们需要在全局版本上设置它,以便将此信息传递给这些其他构建器上下文。

    阅读更多关于它的信息here

    【讨论】:

    • 谢谢!有用。我用其他解决方案解决了它,但这也是有效的! (我把我的解决方案放在帖子里)+
    【解决方案3】:

    要么你配置一些 inMemory 用户如下:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user")
                    .password("password")
                    .roles("USER")
            .and()
                .withUser("manager")
                    .password("password")
                    .credentialsExpired(true)
                    .accountExpired(true)
                    .accountLocked(true)
                    .authorities("WRITE_PRIVILEGES", "READ_PRIVILEGES")
                    .roles("MANAGER");
    }
    

    或者你实现一个 UserDetailService 例如它在数据库中查找用户。

    【讨论】:

    • 我在数据库中搜索用户的自定义 userdetailservice --> securityConfig 类¿是否正确? protected void configure(final AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsS​​ervice(customUserDetailsS​​ervice) .passwordEncoder(passwordEncoder); }
    • True...嗯...而且customUserDetailsS​​ervice不为空?
    • 不,它不为空
    猜你喜欢
    • 2015-08-07
    • 1970-01-01
    • 2017-10-06
    • 2017-09-17
    • 2018-02-13
    • 2017-01-31
    • 2017-10-27
    • 2017-04-20
    • 1970-01-01
    相关资源
    最近更新 更多