【问题标题】:Spring security always returns HTTP 403Spring Security 总是返回 HTTP 403
【发布时间】:2015-10-23 03:36:24
【问题描述】:

我已经配置了一个自定义的Filter,它为除/login 之外的每个 URL 授予 spring 权限:

public class TokenFilter implements Filter {
     @Override
     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
          GrantedAuthority authority = new SimpleGrantedAuthority("myAuthority");
          UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, token, Arrays.asList(authority));
          SecurityContextHolder.getContext().setAuthentication(auth);
      }
}

以及使用该权限保护所有请求(但 /login)的弹簧配置:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().hasAuthority("myAuthority");
    }

}

但是除了/login 之外的每个请求都会得到一个HTTP 403 Forbidden。

我已经调试并确保过滤器中的代码确实被触发了。

可能是什么问题?

编辑 - 将 spring 安全日志放入调试时,我得到以下堆栈跟踪:

2015-07-31 14:52:42 [http-nio-8002-exec-2] DEBUG o.s.s.w.a.ExceptionTranslationFilter - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Accès refusé
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115) ~[spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) ~[spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) ~[spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) [spring-security-web-3.2.4.RELEASE.jar:3.2.4.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at com.kgwebapps.tonpronostic.security.TokenFilter.doFilter(TokenFilter.java:55) [classes/:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1720) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1679) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_40]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_40]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-7.0.54.jar:7.0.54]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]

【问题讨论】:

  • 你能放一些stacktrace吗?谢谢。
  • 没有抛出异常
  • 你已经声明了一个自定义过滤器,但你还没有将它添加到你的配置http.addFilter(new TokenFilter())。另请查看addFilterBefore(...)addFilterAfter(...)
  • 感谢 Roman Vottner 成功了!但在此之前,它已经被声明为 @component 并有效执行。由于过滤顺序,它不起作用吗?或者“http.addFilter”除了注册过滤器之外还有什么特别的功能吗?
  • 我不知道过滤器是可以自动发现的。您还需要通过<filter><filter-name>customFilter</filter-name><filter-class>com.yourcompany.CustomFilter</filter-class></filter> 在 XML 中声明过滤器

标签: java spring spring-security


【解决方案1】:

我知道这是一个非常古老的问题,但我遇到了同样的错误,我在互联网上没有找到任何解决方案。
403是正确的,表示用户认证未授权获取资源。这与您的 JWT 中的声明部分有关。
您的 JWT 构建器需要为用户设置正确的声明

List<GrantedAuthority> grantedAuthorities = AuthorityUtils
                .commaSeparatedStringToAuthorityList("ROLE_USER");

Jwts.builder()//
                .setIssuer(...)//
                .setSubject(...)//
                .setAudience(...)

                // This is the part that you missed

                .claim("authorities",
                        grantedAuthorities.stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList()))

                // Ends here

                .setIssuedAt(date)//
                .setExpiration(new Date(date.getTime() + jwtExpirationMs))
                .signWith(SignatureAlgorithm.HS512, signingKey)//
                .compact();

我的网络安全配置:

public class WebSecurity extends WebSecurityConfigurerAdapter {

...

@Override
    protected void configure(HttpSecurity http) throws Exception {

        http.cors().and().csrf().disable()//
                .authorizeRequests()//
                .antMatchers(...).permitAll()//
                .anyRequest().authenticated()
                .and()
                .sessionManagement()//
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterAfter(authenticationJwtTokenFilter(), BasicAuthenticationFilter.class);
    }

【讨论】:

    【解决方案2】:

    很老的问题,但万一有人偶然发现这篇文章,应用程序也有同样的问题,结果证明是@ControllerAdvice 的问题。

    基本上,设置是这样的:

    @ControllerAdvice
    class MainController {
    
    @PreAuthorize("...")
    class AdminController extends MainController {
    

    出于一个奇怪的原因,任何从MainController 扩展的控制器都会触发AdminController 类的@PreAuthorize,即使此控制器与后者之间没有任何关系。

    在我的情况下,这是一个简单的修复,因为删除 @ControllerAdvice 就足够了,但如果您需要 @ControllerAdvice,您可以将注释移动到一个从不用作超类的类。

    【讨论】:

      【解决方案3】:

      UsernamePasswordAuthenticationToken 扩展 AbstractAuthenticationTokenAbstractAuthenticationToken 实现 Authentication

      Spring security call Authentication's method isAuthenticated() to check whether it should be pass.

      所以你应该调用UsernamePasswordAuthenticationToken 实例的setAuthenticated 并设置参数true

      像这样:

      public class TokenFilter implements Filter {
          @Override
          public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
              GrantedAuthority authority = new SimpleGrantedAuthority("myAuthority");
              UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, token, Arrays.asList(authority));
      
              auth.setAuthenticated(true);
      
              SecurityContextHolder.getContext().setAuthentication(auth);
        }
      }
      

      【讨论】:

        【解决方案4】:

        正如其他人所说,403表示用户已登录但没有查看资源的权限;我会检查以下内容:

        1. 您的控件具有正确的角色权限 @Secured( {"ROLE_myAuthority"} )
        2. 您实际上授予了正确的权限new SimpleGrantedAuthority("ROLE_myAuthority");
        3. 来自 UsernamePasswordAuthenticationToken 对象的实际授予权限
        4. 过滤器被正确注入

          Authentication auth = new UsernamePasswordAuthenticationToken(username, authentication.getCredentials(), authorities);  
          Collection<? extends GrantedAuthority> auths = auth.getAuthorities();`
          
          Iterator authsIterator = auths.iterator();
          
          while (authsIterator.hasNext()) {
               SimpleGrantedAuthority sga =  (SimpleGrantedAuthority) authsIterator.next();
                  sga.getAuthority();
              // ... 
          }
          

        【讨论】:

          【解决方案5】:

          我有同样的问题,每个请求都被 403 错误阻止,除了 [/] 请求。在疯了很久之后,我找到了根本原因,那就是[csrf]。
          然后我的安全配置如下:

          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http.authorizeRequests().antMatchers("/delete/**").authenticated().and().httpBasic().and().csrf().disable();
          }
          

          这个配置说:只有 [delete/**] 应该被授权。
          我将 [delete] 操作标记如下:

          @PreAuthorize("hasRole('ROLE_ADMIN')")
          void delete(String id);
          

          希望能帮助到别人。

          【讨论】:

          • 我认为您可以在相同的配置中添加角色:authorizeRequests() .antMatchers("/delete/**") .hasRole("ROLE_ADMIN")
          • 禁用 csrf 是否安全?
          • @user666 简单的回答:不,它不安全。但是,当您的网站不允许用户修改任何数据(在数据库中保存/更新他们的数据等)时,它可能是安全的(基本上当您只允许 GET 请求时),因为csrf 保护的目的是防止恶意网站代表用户提出请求。 Good post about csrf.
          • 你拯救了我的一天!!
          【解决方案6】:

          可能导致 403 的唯一(明显)事情是用户角色未设置为 ROLE_myAuthority

          【讨论】:

          • 我已将我的角色名称从数据库中的 USER 更改为 ROLE_USER 并且它可以工作。谢谢@Link
          • 它也适用于我,DB 中的 ROLE_USER 和 java 代码中的 USER 作为角色
          【解决方案7】:

          得到 403 而不是 401 通常意味着您已登录,但不允许(通过权限)查看资源。

          调试并确认您登录的用户具有该权限(我知道您的代码设置了它,但也许您设置了其他错误)。

          【讨论】:

          • 好的,但是我在哪里可以检查呢?这样做的代码在 Spring Security 中的某个地方,我不太了解框架,不知道在哪里看。
          • 在您的调试器上,使用“Step Into”而不是“Step Over”。
          猜你喜欢
          • 2018-08-15
          • 2018-08-28
          • 2022-01-16
          • 1970-01-01
          • 2017-06-30
          • 2019-05-24
          • 2018-04-23
          • 2015-11-12
          • 1970-01-01
          相关资源
          最近更新 更多