Spring Boot:Oauth2:访问被拒绝(用户是匿名的);重定向到身份验证入口点

Posted

技术标签:

【中文标题】Spring Boot:Oauth2:访问被拒绝(用户是匿名的);重定向到身份验证入口点【英文标题】:Spring Boot: Oauth2: Access is denied (user is anonymous); redirecting to authentication entry point 【发布时间】:2017-10-25 13:55:36 【问题描述】:

我正在尝试使用 spring boot oauth2 来完成无状态身份验证和授权。但是,我正在努力让它工作。

这是我的代码:

@EnableAutoConfiguration
@ComponentScan
//@EnableEurekaClient
//@EnableZuulProxy
@Configuration
public class AuthServiceApp 

  public static void main(String[] args) 
    SpringApplication.run(AuthServiceApp.class, args);
  

授权配置:

@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter 

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

  @Autowired
  private DataSource dataSource;

  @Autowired
  private CustomUserDetailsService userDetailService;

  @Autowired
  private ClientDetailsService clientDetailsService;


  @Bean
  public JdbcTokenStore tokenStore() 
    return new JdbcTokenStore(dataSource);
  

  @Bean
  protected AuthorizationCodeServices authorizationCodeServices() 
    return new JdbcAuthorizationCodeServices(dataSource);
  

  @Override
  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception 
    security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
  

  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
    // @OFF
    endpoints
          .authorizationCodeServices(authorizationCodeServices())
          .authenticationManager(auth)
          .userDetailsService(userDetailService)
          .tokenStore(tokenStore());
    // @ON
  


  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception 

    // @OFF
    clients.jdbc(dataSource)
           .withClient("client")
           .secret("secret")
           .authorizedGrantTypes("password","refresh_token", "client_credentials")
           .authorities("USER")
           .scopes("read", "write")
           .autoApprove(true)
           .accessTokenValiditySeconds(60)
           .refreshTokenValiditySeconds(300);
    // @ON
  

资源服务器配置:

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
class ResourceServerConfig extends ResourceServerConfigurerAdapter 


  @Autowired
  private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

  @Autowired
  private CustomLogoutSuccessHandler customLogoutSuccessHandler;


  @Override
  public void configure(HttpSecurity http) throws Exception 
    // @OFF
          http
              .sessionManagement()
              .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
              .and()
              .exceptionHandling()
              .authenticationEntryPoint(customAuthenticationEntryPoint)
              .and()
              .logout()
              .logoutUrl("/oauth/logout")
              .logoutSuccessHandler(customLogoutSuccessHandler)
              .and()
              .csrf()
//            .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
              .disable()
              .headers()
              .frameOptions().disable()
              .and()
              .authorizeRequests()
              .antMatchers("/identity/**").authenticated();
   // @ON
  



@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 

  @Autowired
  private CustomUserDetailsService userDetailsService;

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

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

  @Override
  protected void configure(HttpSecurity http) throws Exception 
    // @OFF
    http
        .csrf()
        .disable()
        .authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin().permitAll();
   // @ON
  

控制器:

@RestController
@RequestMapping("/")
public class AuthController 

  @PreAuthorize("#oauth2.hasScope('read')")
  @GetMapping("/user")
  public Principal getUser(Principal user) 
     return user;
  

我可以使用 POSTMAN 获取访问令牌。我在标头中使用相同的访问令牌来获取用户详细信息作为http://localhost:8082/identity/user 在它过期之前。但是,我通过以下登录控制台获得登录页面 html 响应:

2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.AnonymousAuthenticationFilter  : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 301C6EDD36372CF9C553FCFCD4AA47E3; Granted Authorities: ROLE_ANONYMOUS'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy        : /user at position 10 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy        : /user at position 11 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.security.web.FilterChainProxy        : /user at position 12 of 12 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/user'; against '/login'
2017-05-24 22:55:16.070 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /user; Attributes: [authenticated]
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 301C6EDD36372CF9C553FCFCD4AA47E3; Granted Authorities: ROLE_ANONYMOUS
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@55b4f25d, returned: -1
2017-05-24 22:55:16.071 DEBUG 16899 --- [nio-8082-exec-9] o.s.s.w.a.ExceptionTranslationFilter     : Access is denied (user is anonymous); redirecting to authentication entry point

org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]

但在第一次调用以获取oauth/token 的访问令牌时,我似乎已通过身份验证:

2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /oauth/token; Attributes: [fullyAuthenticated]
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@50c8f5e8: Principal: org.springframework.security.core.userdetails.User@af12f3cb: Username: client; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 2F070B741A55BD1E47933621D9127780; Granted Authorities: USER
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@61f8721f, returned: 1
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2017-05-24 22:54:35.966 DEBUG 16899 --- [nio-8082-exec-6] o.s.security.web.FilterChainProxy        : /oauth/token reached end of additional filter chain; proceeding with original chain
2017-05-24 22:54:35.967 DEBUG 16899 --- [nio-8082-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Looking up handler method for path /oauth/token
2017-05-24 22:54:35.968 DEBUG 16899 --- [nio-8082-exec-6] .s.o.p.e.FrameworkEndpointHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException]
2017-05-24 22:54:35.975 DEBUG 16899 --- [nio-8082-exec-6] .o.p.p.ResourceOwnerPasswordTokenGranter : Getting access token for: client
2017-05-24 22:54:35.975 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
Hibernate: select user0_.id as id1_1_, user0_.enabled as enabled2_1_, user0_.name as name3_1_, user0_.password as password4_1_, user0_.username as username5_1_ from user user0_ where user0_.username=?
Hibernate: select roles0_.user_id as user_id1_2_0_, roles0_.role_id as role_id2_2_0_, role1_.id as id1_0_1_, role1_.role as role2_0_1_ from user_role roles0_ inner join role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
2017-05-24 22:54:36.125  INFO 16899 --- [nio-8082-exec-6] o.s.s.o.p.token.store.JdbcTokenStore     : Failed to find access token for token 180c2528-b712-4088-9cce-71e9cc7ccb94
2017-05-24 22:54:36.232 DEBUG 16899 --- [nio-8082-exec-6] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2017-05-24 22:54:36.232 DEBUG 16899 --- [nio-8082-exec-6] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

可能是我配置错误。我在这里错过了什么?

【问题讨论】:

您是否正确执行 HTTP 身份验证标头? Authorization: Bearer $token 【参考方案1】:

我遇到了类似的问题,发现 OAuth2AuthenticationProcessingFilter 没有被过滤器链调用,因此,用户没有通过身份验证,因此被视为匿名用户。

我使用的是 Spring-boot 1.5.3 版本,我在 application.yml 中添加了以下行来修复排序。

security.oauth2.resource.filter-order=3

必须有一个日志语句显示它正在被调用

DEBUG 34386 --- [nio-8082-exec-1] o.s.security.web.FilterChainProxy        : /foo at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'

参考 - https://github.com/spring-projects/spring-security-oauth/issues/993

【讨论】:

这个技巧真的解决了这个问题!..过滤器调用顺序上发生了奇怪的事情。非常感谢! 经过几天的噩梦,这是解决我类似问题的唯一方法!谢谢! @Rites 应该升级到最新版本。 在 application.yml 中添加这样的行也解决了 Spring 版本 1.5.2.RELEASE 中的问题。 Tnx【参考方案2】:

使用

.authorizedGrantTypes("password","refresh_token", "client_credentials")

你需要打开访问权限

/auth/token

在您的授权服务器的安全配置中

@Override
protected void configure(HttpSecurity http) throws Exception 
    code...
    http
        .antMatcher("/**")
        .authorizeRequests()
            .antMatchers("/oauth/token").permitAll()
        .anyRequest().authenticated()
    code...     
 

【讨论】:

【参考方案3】:

我遇到了同样的问题,并设法通过显式禁用一些弹簧自动配置来解决它:

@EnableAutoConfiguration(exclude = [OAuth2AutoConfiguration::class,
   SecurityAutoConfiguration::class, SecurityFilterAutoConfiguration::class])

【讨论】:

【参考方案4】:

还要检查您是否使用来自多个与身份验证相关的依赖项/库的多个 OAuth2 过滤器。

就我而言,我正在使用

    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.2.4.RELEASE</version>
    </dependency>

与我的组织中使用的另一个自定义 OAuth2 库一起,这导致 spring 库尝试在自定义(正确)库之前进行身份验证,并且没有向其提供凭据当然会抛出这个确切的异常。

一旦我删除了 Spring OAuth2 依赖项,我的服务调用就可以工作了。

【讨论】:

以上是关于Spring Boot:Oauth2:访问被拒绝(用户是匿名的);重定向到身份验证入口点的主要内容,如果未能解决你的问题,请参考以下文章

任何用户的 jdbc-authentication 访问被拒绝 [OAUTH2]

Spring OAuth2 授权:访问被拒绝

Spring Security OAuth2 在 REST 服务上被拒绝访问

在 Spring OAuth2 中配置安全性:身份验证请求的访问被拒绝

用户''@'localhost'的访问被拒绝(使用密码:NO)spring-boot

面对拒绝访问 (403) - spring security oauth2 中的禁止错误