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

Posted

技术标签:

【中文标题】面对拒绝访问 (403) - spring security oauth2 中的禁止错误【英文标题】:Facing Access Denied (403) - Forbidden error in spring security oauth2 【发布时间】:2017-12-20 21:45:24 【问题描述】:
.antMatchers("/secure2/**").authenticated();

我已将我的一个 api 配置为受保护,当我尝试访问它时,它给了我访问被拒绝的错误消息,我不知道可能是什么原因。请注意,我正在传递有效的访问令牌。

我的场景: 基本上我已经在授权服务器中创建了注销休息 api,我希望这样,允许使用有效令牌的请求访问这个 api。

请求:

GET /auth/secure2 HTTP/1.1
Host: localhost:9191
Authorization: Bearer 33984141-1249-4465-a3aa-0b95a053fc63
Cache-Control: no-cache
Postman-Token: f4661790-a8e1-90ea-f6db-79cb37958cdf

回复:


    "timestamp": 1500186837033,
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/auth/secure2"

我发现下面的方法返回 false 并因此引发访问被拒绝错误。

public final class ExpressionUtils 

    public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx) 
        try 
            return ((Boolean) expr.getValue(ctx, Boolean.class)).booleanValue();
        
        catch (EvaluationException e) 
            throw new IllegalArgumentException("Failed to evaluate expression '"
                    + expr.getExpressionString() + "'", e);
        
    

以下是我在框架中调试时捕获的屏幕截图。还请检查评论中提到的图像。

代码:

SecurityConfiguration.java:

import org.springframework.beans.factory.annotation.Autowired;


@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Autowired
    private CustomUserDetailService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new StandardPasswordEncoder();
    

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

    @Bean
    public DaoAuthenticationProvider authenticationProvider() 
        DaoAuthenticationProvider authProvider
          = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(encoder());
        return authProvider;
    

    @Bean
    public ShaPasswordEncoder encoder() 
        return new ShaPasswordEncoder(256);
    

    @Override
    public void configure(WebSecurity web) throws Exception 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
         http
         .exceptionHandling()
         .accessDeniedHandler(accessDeniedHandler())
         .and()
         .csrf()
         .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
         .disable()
         .headers()
         .frameOptions().disable().disable()
         .sessionManagement()
         .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
         .and()
         .authorizeRequests()
         .antMatchers("/hello/").permitAll()
         .antMatchers("/secure3/").permitAll()
         .antMatchers("/oauth/token/revoke/**").authenticated()
         .antMatchers("/secure2/**").authenticated();

    

    @Bean
    public AccessDeniedHandler accessDeniedHandler()
        return new CustomAccessDeniedHandler();
    

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

    @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
    private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration 

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

    


Oauth2Configuration.java

@Configuration
public class OAuth2Configuration 


    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware 

        private static final String ENV_OAUTH = "authentication.oauth.";
        //private static final String PROP_CLIENTID = "clientid";
        //private static final String PROP_SECRET = "secret";
        private static final String PROP_ACCESS_TOKEN_VALIDITY_SECONDS = "accessTokenValidityInSeconds";
        private static final String PROP_REFRESH_TOKEN_VALIDITY_SECONDS = "refreshTokenValidityInSeconds";

        private RelaxedPropertyResolver propertyResolver;

        @Autowired
        private DataSource dataSource;

        @Autowired
        private CustomUserDetailService userDetailsService;

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

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

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

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

        @Bean
        public DefaultAccessTokenConverter accessTokenConverter() 
            return new DefaultAccessTokenConverter();
        

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


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
            clients
                    .inMemory()

                    .withClient("clientId")
                    .scopes("read", "write")
                    .authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
                    .authorizedGrantTypes("password", "refresh_token")
                    .accessTokenValiditySeconds(propertyResolver.getProperty(PROP_ACCESS_TOKEN_VALIDITY_SECONDS, Integer.class, 80))
                    .refreshTokenValiditySeconds(propertyResolver.getProperty(PROP_REFRESH_TOKEN_VALIDITY_SECONDS, Integer.class, 180))

                    .and().inMemory()
                    .withClient("clientid")
                    .scopes("read", "write")
                    .authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
                    .authorizedGrantTypes("client_credentials")
                    .secret("secret");

        

        @Override
        public void setEnvironment(Environment environment) 
            this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
        

    

控制器:

@Controller
@RequestMapping("/secure2")
public class SecureController1 

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public String sayHello() 

        return "Secure Hello secure2!";
    


在哪些情况下会引发拒绝访问错误消息?如果需要任何其他信息,请告诉我。

【问题讨论】:

i.stack.imgur.com/vmOff.png i.stack.imgur.com/CwslH.png 这是授权服务器,我想保护授权服务器中的一些资源。例如,我有要保护的令牌撤销/注销休息 api。允许使用有效访问令牌的请求进行注销。 我在 SecurityConfiguration.configure(httpsecurity) 方法中有相关配置。 授权服务器中有哪些资源?应该只有授权和令牌端点。所有其他端点都是资源服务器的一部分。但是,如果您调用授权服务器,则不会应用 OAth2 令牌并且您是匿名的。只有资源服务器检查令牌。这就是你的 403 的原因。 【参考方案1】:

我使用了这些代码,它们运行良好。

OAuth2AuthorizationServerConfig.java:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 

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

    // Configure the token store and authentication manager
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        //@formatter:off
        endpoints
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter()) // added for JWT
                .authenticationManager(authenticationManager);
        //@formatter:on
    

    // Configure a client store. In-memory for simplicity, but consider other
    // options for real apps.


    //It is not necessary.works even without this func:)
//    @Override
//    public void configure(AuthorizationServerSecurityConfigurer oauthServer)
//            throws Exception 
//        oauthServer
//                .tokenKeyAccess("permitAll()")
//                .checkTokenAccess("isAuthenticated()");
//    


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        //@formatter:off
        clients
                .inMemory()
                .withClient("myclient")//username in basic auth header
                .secret ("noop123")//password in basic auth header;
                .authorizedGrantTypes("authorization_code", "implicit", "password", "client_credentials", "refresh_token")
                .scopes("read")
                //.redirectUris("http://localhost:9191/x")
                .accessTokenValiditySeconds(86400); // 24 hours
        //@formatter:on
    

    // A token store bean. JWT token store
    @Bean
    public TokenStore tokenStore() 
        return new JwtTokenStore(accessTokenConverter()); // For JWT. Use in-memory, jdbc, or other if not JWT
    

    // Token converter. Needed for JWT
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() 
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123"); // symmetric key
        return converter;
    

    // Token services. Needed for JWT
    @Bean
    @Primary
    public DefaultTokenServices tokenServices() 
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    


//    @Bean
//
//    public PasswordEncoder passwordEncoder () 
//
//        return new BCryptPasswordEncoder();
//
//    

WebSecurityConfig:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private UserDetailsService userDetailsService;


    //It is not important to use this func. /oauth/token is the default path of spring security to use oauth2. she is so clever!! :)
//    @Override
//    protected void configure(HttpSecurity http) throws Exception 
//
//
//        http.authorizeRequests()
//                .antMatchers(HttpMethod.POST, "/oauth/token").permitAll()
//                .anyRequest().authenticated();
//    

    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception 
        auth.inMemoryAuthentication()
                .withUser("user").password("noopuser").roles("ROLE");
    

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


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

//
//    @Bean
//    public PasswordEncoder passwordEncoder() 
//        return new BCryptPasswordEncoder();
//    

输出: 请注意,code 和 postman 中使用的用户名和密码的值必须相同。

enter image description here enter image description here

希望对你有所帮助:)

【讨论】:

以上是关于面对拒绝访问 (403) - spring security oauth2 中的禁止错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring Security 中启用 CSRF 时,访问被拒绝 403

Spring Security 4.1 升级 - 错误 403 访问被拒绝

Spring Security 总是返回 403 被禁止,访问被拒绝

Spring security hasRole() 给出错误 403 - 访问被拒绝

Spring Security 总是返回 HTTP 403 访问被拒绝 [关闭]

使用 MySQL 和 JPA 提供 403 访问被拒绝的 Spring 自定义安全性