为什么我收到AuthenticationCredentialsNotFoundException?

时间:2017-03-24 12:29:58

标签: spring spring-security spring-security-oauth2

我想为我的应用程序配置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 个答案:

答案 0 :(得分:1)

好的,我配置中的主要问题是在SecurityConfiguration类中。我已根据此帖See here添加了注释@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;
        }
    }
}

我也改变了一些配置:

@strTablename

现在一切都按预期工作了。