41

I have configured a custom Filter that grants a spring authority for every URL other than /login :

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);
      }
}

and a spring configuration that protects all requests (but /login) with that authority :

@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");
    }

}

But every request except /login gets a HTTP 403 Forbidden.

I have debugged and made sure the code from the filter is really triggered.

What could be the problem?

EDIT - when putting spring security logs in debug I get the following stack trace :

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]
kgautron
  • 7,915
  • 9
  • 39
  • 60
  • Can you please put some stacktrace? Thanks. – We are Borg Jul 31 '15 at 12:44
  • There is no exception thrown – kgautron Jul 31 '15 at 12:46
  • please enable debug logging for org.springframework.security. Then when you get the 403, copy the last 10-20 lines in the debug log from your IDE console. – We are Borg Jul 31 '15 at 12:47
  • Ok thanks, just added it to the question. – kgautron Jul 31 '15 at 12:56
  • As the log says, you are not logged in. Your configuration seems wrong. I have not that much idea about Java based configurations as I am using XML based. You can check out some examples. Secondly, instead of adding authority as 'myAuthority', please add something relevant like ROLE_USER. – We are Borg Jul 31 '15 at 12:59
  • 3
    You've declared a custom filter, but you have not added it to your config `http.addFilter(new TokenFilter())`. Also check out [`addFilterBefore(...)`](http://docs.spring.io/autorepo/docs/spring-security/4.0.1.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilterBefore%28javax.servlet.Filter,%20java.lang.Class%29) or [`addFilterAfter(...)`](http://docs.spring.io/autorepo/docs/spring-security/4.0.1.RELEASE/apidocs/org/springframework/security/config/annotation/web/HttpSecurityBuilder.html#addFilterAfter%28javax.servlet.Filter,%20java.lang.Class%29) – Roman Vottner Jul 31 '15 at 13:00
  • 1
    Thanks Roman Vottner it worked! But before doing that it was already declared as a @component and effectively executed. Did it not work because of filter order? Or does 'http.addFilter' do something special more that just registering the filter? – kgautron Jul 31 '15 at 13:30
  • 2
    I'm not aware of that filters are auto-discoverable at all. Pre Java-based web-configuration you needed to declare filters in XML via `customFiltercom.yourcompany.CustomFilter` also – Roman Vottner Jul 31 '15 at 13:39

11 Answers11

80

I have the same issue to you, every request is blocked by 403 error, except the [/] request. After a lot of time in crazy, I found the root cause, that is the [csrf].
Then my security config is like as following:

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

This configuration says that: only [delete/**] should be authorized.
And I mark the [delete] action as following:

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

Hope to help someone.

lbenedetto
  • 2,022
  • 1
  • 21
  • 39
doannx
  • 1,250
  • 13
  • 19
  • i think you can add the role in same configuration: authorizeRequests() .antMatchers("/delete/**") .hasRole("ROLE_ADMIN") – user666 Jan 04 '19 at 06:30
  • is it secure to disable csrf? – user666 Jan 04 '19 at 07:36
  • @user666 Simple answer: no, it is not secure. However when your website does not allow user to modify any data (save/update their data in database etc) then it could be secure (basically when you only allow GET requests) since the purpose of `csrf` protection is to prevent malicious websites from doing requests on user's behalf. [Good post about csrf](https://stackoverflow.com/questions/5207160/what-is-a-csrf-token-what-is-its-importance-and-how-does-it-work). – Cray Jun 12 '19 at 12:25
7

The only (obvious) thing that might result to 403 is the users role is not set to ROLE_myAuthority.

Link
  • 311
  • 1
  • 6
7

As others have said 403 means that user is logged in but doesn't have the right permission to view the resource; I would check the following:

  1. Your control has the correct role permission @Secured( {"ROLE_myAuthority"} )
  2. You had actually granted the correct permission new SimpleGrantedAuthority("ROLE_myAuthority");
  3. Actual granted authority from the UsernamePasswordAuthenticationToken object
  4. Filter is been injected correctly

    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();
        // ... 
    }
    
Ithar
  • 4,865
  • 4
  • 39
  • 40
2

I know that this is a very old question but I just had the same error and I didn't find any solution on the internet.
It is correct that 403 means that the user is authenticated but not authorized to get a resource. This is related to the claims part in your JWT.
Your JWT builder needs to set proper claims for the user :

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();

My WebSecurity configuration :

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);
    }
Dharman
  • 30,962
  • 25
  • 85
  • 135
Rayen Rejeb
  • 311
  • 2
  • 8
  • Hey, can you provide me with your source code or the code for the filter? I'm kinda stuck here. – Piazzi Mar 28 '23 at 13:54
2

Confusingly Spring Security also returns 403 instead of 404 for all undefined endpoints if any restricting auth rule is set in the SecurityFilterChain.

So if you mistype a test url or (like me) forget to scan the the base package of the controller, this might set you on a wild-goose chase.

eg. this correctly returns 404 if I call an undefined endpoint:

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
        .anyRequest().permitAll();
    return http.build();
}

While this returns 403 for the same call:

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
        .requestMatchers(GET, "/health/**").permitAll();
    return http.build();
}
Péter Veres
  • 955
  • 1
  • 10
  • 25
1

Getting a 403 instead of a 401 usually means that you were logged in but you are not permitted (via authority) to see a resource.

Debug and confirm that the user you are logging in has that authority (I know your code sets it, but maybe you are setting something else wrong).

Zerkz
  • 686
  • 1
  • 6
  • 25
  • 2
    Ok but where can I check for it? The code doing that is somewhere down in spring security, i don't understand the framework well enough to know where to look. – kgautron Jul 31 '15 at 12:49
  • 1
    On your debugger, use "Step Into" instead of "Step Over". – Zerkz Jul 31 '15 at 13:18
1

IDK is it meet topic question or not, but I didn't find any similar subject so I'll leave it here.

I have pretty same situation: fully custom filter based on headers token authorization and always receiving 403

That's what worked for me:

http.csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .logout().disable()
                .anonymous().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

                .addFilterBefore(XAuthTokenFilter, BasicAuthenticationFilter::class.java)

Also a good idea to use in your application config:

logging:
  level:
    org.springframework.security: TRACE

to see what's going on inside filter chain.

Hope it will save somebody's time

E.Big
  • 723
  • 6
  • 15
0

UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken, AbstractAuthenticationToken implements Authentication.

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

So you should call setAuthenticated of UsernamePasswordAuthenticationToken instance and set the argument true.

Like this:

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);
  }
}
Beary
  • 19
  • 4
0

Pretty old question but just in case someone stumble upon this post, an application had the same problem and it turns out to be an issue with @ControllerAdvice.

Basically, the setup was like this:

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

And for a strange reason, any controller extending from MainController would trigger the @PreAuthorize of the AdminController class even though there were no relationships between this controller and the latter.

In my case, it was an easy fix as removing the @ControllerAdvice was enough but if you need @ControllerAdvice, you might move the annotation to a class that is never used as a superclass.

Robin
  • 1,438
  • 2
  • 19
  • 29
0

You might have a similar problem that I had by having the server.servlet.context-path configured in your application.yml

In my case, the server.servlet.context-path is configured to be /api and I expose the following endpoints below like so:

.requestMatchers("/api/health", "/api/ping").permitAll()

However, this won't work as spring security doesn't put the context-path into consideration so you have to omit that when specifying the endpoints.

.requestMatchers("/health", "/ping").permitAll()
Melvin Sy
  • 1,743
  • 2
  • 11
  • 17
-1

Add "/styles/*" for CSS files

http.authorizeHttpRequests(requests->requests .requestMatchers("/login","/register","/styles/*").permitAll().anyRequest().authenticated()

Shubham Jain
  • 9
  • 1
  • 1