为啥我收到 AuthenticationCredentialsNotFoundException?

Posted

技术标签:

【中文标题】为啥我收到 AuthenticationCredentialsNotFoundException?【英文标题】:Why I'm getting AuthenticationCredentialsNotFoundException?为什么我收到 AuthenticationCredentialsNotFoundException? 【发布时间】:2017-08-17 09:28:07 【问题描述】:

我想为我的应用程序配置 OAuth2 身份验证。 我有下一个配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration 

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() 
        return new OAuth2MethodSecurityExpressionHandler();
    



@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter 

    private static final String[] GRANT_TYPES = "password", "refresh_token";
    private static final String[] SCOPES = "read", "write";

    private final SecurityConfigurationProperties securityConfigurationProperties;

    private final AuthenticationProvider authenticationProvider;

    private final OAuth2AccessTokenRepository oAuth2AccessTokenRepository;
    private final OAuth2RefreshTokenRepository oAuth2RefreshTokenRepository;

    @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(securityConfigurationProperties.getClientId())
                .authorizedGrantTypes(GRANT_TYPES)
                .authorities(UserRole.USER.getName())
                .scopes(SCOPES)
                .secret(securityConfigurationProperties.getClientSecret())
                .accessTokenValiditySeconds(securityConfigurationProperties.getAccessTokenTime())
                .refreshTokenValiditySeconds(securityConfigurationProperties.getRefreshTokenTime());
    

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

    @Bean
    public AuthenticationManager authenticationManager() 
        return new ProviderManager(Collections.singletonList(authenticationProvider));
    

    @Bean
    public TokenStore tokenStore() 
        return new MongoTokenStore(oAuth2AccessTokenRepository, oAuth2RefreshTokenRepository);
    

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() 
        final DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setAuthenticationManager(authenticationManager());

        return tokenServices;
    



@Configuration
@EnableResourceServer
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter 

    private static final String RESOURCE_ID = "api";

    private final TokenStore tokenStore;

    @Override
    public void configure(final ResourceServerSecurityConfigurer resources) 
        resources.resourceId(RESOURCE_ID)
                .tokenStore(tokenStore);
    

    @Override
    public void configure(final HttpSecurity http) throws Exception 
        http.anonymous().disable()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
    


@Configuration
@EnableWebSecurity
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    private final ApiUserDetailsService apiUserDetailsService;
    private final AuthenticationProvider authenticationProvider;

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

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

我也有我的自定义AuthenticationProvider

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserAuthenticationProvider implements AuthenticationProvider 

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(final Authentication authentication)
            throws AuthenticationException 
        final String email = authentication.getName();
        final String password = authentication.getCredentials().toString();

        return userRepository.findByEmail(email)
                .filter(user -> passwordEncoder.matches(password, user.getPassword()))
                .map(this::signInUser)
                .orElseThrow(() -> new BadCredentialsException("Failed to authenticate"));
    

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

    private Authentication signInUser(final User user) 
        final ApiUser springSecurityUser =
                new ApiUser(user.getEmail(), user.getPassword(), user.getRoles());
        final Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser,
                        user.getId(), springSecurityUser.getAuthorities());

        SecurityContextHolder.getContext().setAuthentication(authentication);

        return authentication;
    

一切都适用于令牌我从 /oauth/token 端点获取访问和刷新令牌,但是当我尝试使用 @PreAuthorize 注释访问资源时出现错误。

链接http://localhost:8080/users/me?access_token=8450e2f3-2ecb-4e88-b304-685b22c2ad65 我也尝试将"Authorization: Bearer 8450e2f3-2ecb-4e88-b304-685b22c2ad65" 添加到标题


  "timestamp": 1490358162182,
  "status": 403,
  "error": "Forbidden",
  "exception": "org.springframework.security.authentication.AuthenticationCredentialsNotFoundException",
  "message": "Access Denied",
  "path": "/users/me"

我的端点:

@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(value = RestPath.Users.ME, method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity userInfo() 
    return ResponseEntity.noContent().build();

也许有人已经在相同的配置下遇到过这样的异常。

【问题讨论】:

【参考方案1】:

好的,所以我的配置中的主要问题是在 SecurityConfiguration 类中。根据这篇文章https://***.com/a/42836521/2055854,我添加了注释@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)。 现在看起来:

@Configuration
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private AuthenticationProvider authenticationProvider;

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

我也改变了一些配置:

@Configuration
public class OAuth2Config 

    @Configuration
    @EnableResourceServer
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public static class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter 

        public static final String RESOURCE_ID = "api";

        private static final String AUTHORIZATION = "Authorization";
        private static final String BEARER = "Bearer";
        private static final String ACCESS_TOKEN = "access_token";

        private final TokenStore tokenStore;

        @Override
        public void configure(final ResourceServerSecurityConfigurer resources) 
            resources.resourceId(RESOURCE_ID)
                    .tokenStore(tokenStore);
        

        @Override
        public void configure(final HttpSecurity http) throws Exception 
            http.csrf().disable()
                    .authorizeRequests().anyRequest().permitAll()
                    .and()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
        
    


    @Configuration
    @EnableAuthorizationServer
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public static class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter 

        private static final String[] GRANT_TYPES = "password", "refresh_token";
        private static final String[] SCOPES = "read", "write";

        private final SecurityConfigurationProperties securityConfigurationProperties;

        private final AccessTokenRepository oAuth2AccessTokenRepository;
        private final RefreshTokenRepository oAuth2RefreshTokenRepository;

        private final AuthenticationProvider authenticationProvider;
        private final UserDetailsService userDetailsService;

        @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(securityConfigurationProperties.getClientId())
                    .authorizedGrantTypes(GRANT_TYPES)
                    .authorities(UserRole.USER.getName())
                    .secret(securityConfigurationProperties.getClientSecret())
                    .scopes(SCOPES)
                    .resourceIds(OAuth2ResourceServerConfig.RESOURCE_ID)
                    .accessTokenValiditySeconds(securityConfigurationProperties.getAccessTokenTime())
                    .refreshTokenValiditySeconds(securityConfigurationProperties.getRefreshTokenTime());
        

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

        public AuthenticationManager authenticationManager() 
            return new ProviderManager(Collections.singletonList(authenticationProvider));
        

        @Bean
        public TokenStore tokenStore() 
            return new MongoTokenStore(oAuth2AccessTokenRepository, oAuth2RefreshTokenRepository);
        

        @Bean
        @Primary
        public DefaultTokenServices tokenServices() 
            final DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setTokenStore(tokenStore());
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setAuthenticationManager(authenticationManager());

            return tokenServices;
        
    

现在一切正常。

【讨论】:

以上是关于为啥我收到 AuthenticationCredentialsNotFoundException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我收到 NullPointerException 错误

为啥我收到 AuthenticationCredentialsNotFoundException?

为啥我收到的 jwt 格式错误?

为啥我收到“无法解析 NSPredicate”错误

为啥我收到 AbstractDynamicObject$CustomMessageMissingMethodException 错误?

无效的字典 0 )。为啥我收到此错误?