Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

After adding custom filters, permitAll() does not work #4368

Closed
redbrickss opened this issue Jun 4, 2017 · 16 comments
Closed

After adding custom filters, permitAll() does not work #4368

redbrickss opened this issue Jun 4, 2017 · 16 comments

Comments

@redbrickss
Copy link

Summary

After adding custom JWT filter for authentication, all paths with permitAll() are still passing through the custom JWT filter.
So, for example if I have these URI's

/api/users/{id}/topic/{name} (This has to be available for all users. permitAll())
and
/api/users/** (other multiple ant matchers URI which are authenticated())

So, If I have a custom JWT filter with pattern matching URI as (/api/users/**) then why is this filter enabled for permitAll (/api/users/{id}/topic/{name} URI)? It doesn't have jwt token.

Is this a bug?

Do I have to keep authenticated URI's with /a/api/user/** and use the pattern for the filter?
and /api/users/** with permitAll()? Looks like a bad design to me.

Actual Behavior

permitAll() URI's are passing through custom filter.

Expected Behavior

permitAll() URI's shouldn't pass through custom filter.

Configuration

Version

Latest version

Sample

@mmouterde
Copy link

mmouterde commented Mar 12, 2018

@redbrickss I had a similar issue with :

String authorization = servletRequest.getHeader("Authorization");
        if (authorization != null) {
            SecurityContextHolder.getContext().setAuthentication(new JWTAuthentication(authorization.replaceAll("Bearer ", "")));
        } /* no else case */

I fixed it with :

String authorization = servletRequest.getHeader("Authorization");
        if (authorization != null) {
            SecurityContextHolder.getContext().setAuthentication(new JWTAuthentication(authorization.replaceAll("Bearer ", "")));
        } else {
            SecurityContextHolder.getContext().setAuthentication(null);
        }

Not sure why the context is not refreshed between requests.

@siddhiparkar151992
Copy link

I am facing the same issue :| tried all possible combinations but it seems like after custom filter it doesnt work at all.

@siddhiparkar151992
Copy link

siddhiparkar151992 commented Sep 14, 2018

#Update:
Solved it using following config
added restricted path to filter others will be permitted

http.csrf()
                .disable().antMatcher("<secure patth>/**").
                authorizeRequests().
                anyRequest().
                authenticated().and()
                .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authProvider)

@rwinch
Copy link
Member

rwinch commented Sep 14, 2018

@siddhiparkar151992 I'm glad you were able to solve this. As an FYI, there is no need for .antMatchers("/**") as this is the default. So you can replace what you have with the following:

http
   .csrf().disable()
   .authorizeRequests()
      .anyRequest().authenticated()
      .and()
   .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
   .authenticationProvider(authProvider)

PS: You can use fenced code blocks for formatting code. For example:

```java
System.out.println("hi");
System.out.println("there");
```

will render as

System.out.println("hi");
System.out.println("there");

@rwinch
Copy link
Member

rwinch commented Sep 14, 2018

To anyone still having an issue, it is difficult to reproduce the issue if you have custom code (i.e. a custom authentication filter) without posting it. Please provide a complete sample to reproduce the issue.

@rwinch rwinch added the status: waiting-for-feedback We need additional information before we can continue label Sep 14, 2018
@KevinWingi
Copy link

KevinWingi commented Jun 8, 2019

Another way to solve the problem is ignoring the URL and if possible with the respective HTTP method overriding the method void configure (WebSecurity web) {} as bellow
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers(HttpMethod.POST, "/agents/login"); }

@davi004
Copy link

davi004 commented Sep 5, 2019

I am facing similar issues where i have custom filter for JWT validation and also have configured the web.ignoring in place but still the request goes into filter

http.csrf().disable().authorizeRequests().antMatchers(AUTH_WHITELIST).permitAll().and().authorizeRequests().anyRequest().authenticated().and().requestMatchers().antMatchers(gateConfig.getResource()).and().addFilterAfter(new JWTRequestFilter(), UsernamePasswordAuthenticationFilter.class);

@Kevin2iBi
Copy link

Kevin2iBi commented Sep 5, 2019

@davi004 i think you need to remove the authorizeRequests().antMatchers(AUTH_WHITELIST).permitAll() code and just let the web.ignoring configuration for you AUTH_WHITELIST.

Another think you need to add your JWTRequestFiler filter before the UsernamePasswordAuthenticationFilter and not after like thisaddFilterBefore(new JWTRequestFilter(), UsernamePasswordAuthenticationFilter.class)

@jzheaux
Copy link
Contributor

jzheaux commented Sep 6, 2019

permitAll() has no effect on authentication filters. Spring Security processes authentication first and then authorization, and permitAll() is an authorization matter.

Things essentially happen in this order:

  1. Write Secure Headers, like X-XSS-Protection
  2. Create an Authentication statement (that's what the authentication filters are for)
  3. Decide if that Authentication is enough to allow the request (permitAll() always says "yes")

Step 2 is where a JWT filter would go since that's how you'd figure out what privileges (scopes for example) that token has. That question has to be asked before an authorization decision (permitAll()) can be made.

Note that just because a request is public, that doesn't mean the response won't be rendered differently if there is a user in context. Thus, the separation of these two concerns is important.

It's already been stated that WebSecurity is how to completely ignore certain endpoints. Note, though, that this means that Spring Security won't secure that request in any way (no Step 1, 2, or 3).

@jzheaux jzheaux closed this as completed Sep 6, 2019
@carlosmontoyargz
Copy link

#Update:
Solved it using following config
added restricted path to filter others will be permitted

http.csrf()
                .disable().antMatcher("<secure patth>/**").
                authorizeRequests().
                anyRequest().
                authenticated().and()
                .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authProvider)

Wow that worked like magic, thank you!

@sangonzal
Copy link

@jzheaux Thanks for explaining, your comment was helpful in understanding the steps involved.

I have not been able to find an example for what a WebSecurityConfigurerAdapter would look like in the case where:

  1. I have a custom filter (for example a JWT filter)
  2. Would like to exclude a URL for steps 2 and 3 in your comment, but not step 1

In other words, I have a URL where I'd like to avoid having authentication or authorization checks be performed (including not running my custom filter, which is an authentication filter), but I'd like to have the Step 1 checks (secure headers, etc) and therefore I can't just use WebSecurity to ignore the URL.

Is there any documentation or examples that you know off?

@jzheaux
Copy link
Contributor

jzheaux commented Oct 15, 2020

Hi, @sangonzal. Usually, the best way is to ensure that your custom filter simply skips any request it doesn't care about. This is what several of the Spring Security filters do, like LogoutFilter.

You can achieve that using the RequestMatcher API. Your custom filter would do something like:

public class MyCustomFilter extends OncePerRequestFilter {
    private final RequestMatcher requestMatcher = new AntPathRequestMatcher("/relevant/paths/**");

    // ...
    public void doFilterInternal(...) {
        if (this.requestMatcher.matches(request)) {
            // ... this filter should run
        }
        chain.doFilter(request, response);
    }
}

There are several implementations of RequestMatcher that can help your code describe what requests your custom filter should run for. Or, you can implement your own.

However, if you have a use case where you have endpoints that you don't want Spring Security to authenticate or authorize, but still want the security headers, you can register two WebSecurityConfigurerAdapters, one for these special endpoints, and one for the rest:

@Configuration
@Order(1)
public class OnlyHeadersConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .antMatchers("/special/endpoints/**")
                .authorizeRequests((authz) -> authz.anyRequest().permitAll())
                .anonymous();
    }
}

@Configuration
@Order(2)
public class MainSecurityConfig extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) {
        http
            .authorizeRequests(authz -> authz.anyRequest().authenticated())
            .addFilterAt(new MyCustomFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

The first config is only applied to the /special/endpoints/** set of requests.

@sangonzal
Copy link

sangonzal commented Oct 16, 2020

@jzheaux Thanks, breaking up the configuration solves the issue.

Follow up question creation of a RequestMatcher for a custom filter. I'm working on a Spring boot starter, and it would be nice if MyCustomFilter was able to tap into RequestMatcher set in HttpSecurity as opposed to having the user have to set it on the custom filter separately. In other words, I'd like Spring Security to apply the same configurations to MyCustomFilter as it does to the other authentication filters, including the URLs it will be matched on.

Do you if there are anyways of having MyCustomFilter inherit the RequestMatcher from HttpSecurity ? Would extending AbstractAuthenticationProcessingFilter instead of OncePerRequestFilter be the right approach?

@jzheaux
Copy link
Contributor

jzheaux commented Oct 26, 2020

I'm glad that worked for you, @sangonzal.

At this point, I think that we are getting a bit off the topic of the ticket here, which will make it trickier for community members who came for the specific issue found in this ticket.

I think your question is a good one and would be better answered via the StackOverflow community. Please consider posting the question there, and I'm happy to take a look and discuss it further.

@rwinch rwinch removed the status: waiting-for-feedback We need additional information before we can continue label Dec 8, 2020
@kiranmadanwad
Copy link

kiranmadanwad commented Feb 17, 2022

#Update: Solved it using following config added restricted path to filter others will be permitted

http.csrf()
                .disable().antMatcher("<secure patth>/**").
                authorizeRequests().
                anyRequest().
                authenticated().and()
                .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authProvider)

For me it worked only with following code as I just wanted secured API to go through the custom filter and follow the logic there.
But thank you for your solution.

 http
                .csrf()
                .disable().antMatcher("<Secured API>/**").
                authorizeRequests()
                .anyRequest().permitAll()
                .and()
                .addFilterAfter(new WellnessAuthFilter(), BasicAuthenticationFilter.class);```

@ArkaBando
Copy link

http.csrf()
                .disable().antMatcher("<secure patth>/**").
                authorizeRequests().
                anyRequest().
                authenticated().and()
                .addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .authenticationProvider(authProvider)

not solving

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests