question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SecurityContextHolder with AbstractPreAuthenticatedProcessingFilter

See original GitHub issue

Hi,

I’m trying to rate limit my API using an API key from the URL I have my API secured by an API key using this method:

@Configuration
@EnableWebSecurity
public class APISecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${principal.hashed.key}")
    private String principalHashedKey;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        APIKeyAuthFilter filter = new APIKeyAuthFilter();

        filter.setAuthenticationManager(authentication -> {
            String urlApiKey = (String) authentication.getPrincipal();
            if (!BCrypt.checkpw(urlApiKey, principalHashedKey)) {
                throw new BadCredentialsException("The API key was not found or not the expected value.");
            }
            authentication.setAuthenticated(true);
            return authentication;
        });

        httpSecurity
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/documentation.html")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .addFilter(filter)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

and the filter:

public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        return request.getParameter("api_key");
    }

    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        return "N/A";
    }

}

What is happening is I’m getting the api_key from the url and then check it against the hashed api_key. If they match then the request passes. Everithing works well.

My problem is that when using this: bucket4j.filters[0].rate-limits[0].execute-condition=@securityService.isAuthorized()

@Service
public class SecurityService {

    public boolean isAuthorized() {
        return SecurityContextHolder.getContext().getAuthentication() != null &&
                SecurityContextHolder.getContext().getAuthentication().isAuthenticated();
    }

}

the SecurityContextHolder.getContext().getAuthentication() is always null but if I try to use it in my controller it is not null anymore. I guess it has something to do with the fact that I’m using a filter and not the classic approach with username and pass. Is there something I can do to get the status of auth?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
majoercommented, Mar 10, 2020

Took me a day to figure it out. This post made me realize that Spring security context is only accessible after some security filter has been applied first. Depending on how many filters you have, filter-order must be high enough, so that Spring security context is available when the Bucket4J filter is applied.

bucket4j:
  enabled: true
  filters:
    - cache-name: requests
      filter-order: 1000 # <-- This must be greater that whatever filter initializes Spring Security Context
      url: .*
      rate-limits:
        - expression: "@authorizationService.subClaim()"
          bandwidths:
            - capacity: 5
              time: 10
              unit: seconds
0reactions
cod-rcommented, Mar 20, 2020

I solved my issue by adding this line in spring security config class:

.addFilterAfter(context.getBean(ServletRequestFilter.class), APIKeyAuthFilter.class)

and have the filter order set to a high value. The class looks like this now:

@Configuration
@EnableWebSecurity
public class APISecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${principal.hashed.key}")
    private String principalHashedKey;

    @Autowired
    private ApplicationContext context;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        APIKeyAuthFilter filter = new APIKeyAuthFilter();

        filter.setAuthenticationManager(authentication -> {
            String urlApiKey = (String) authentication.getPrincipal();
            if (!BCrypt.checkpw(urlApiKey, principalHashedKey)) {
                throw new BadCredentialsException("The API key was not found or not the expected value.");
            }
            authentication.setAuthenticated(true);
            return authentication;
        });

        httpSecurity
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html", "/documentation.html")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .addFilter(filter)
                .addFilterAfter(context.getBean(ServletRequestFilter.class), APIKeyAuthFilter.class)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

So now I have the ServletRequestFilter registered in the springSecurityFilterChain and once again after it. This was the only combination I found that worked for my case. The full code is here: https://github.com/cod-r/docs

Read more comments on GitHub >

github_iconTop Results From Across the Web

spring-security/AbstractPreAuthenticatedProcessingFilter.java ...
public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFilterBean ... securityContextHolderStrategy = SecurityContextHolder.
Read more >
AbstractPreAuthenticatedProces...
public abstract class AbstractPreAuthenticatedProcessingFilter extends ... Sets the SecurityContextRepository to save the SecurityContext on authentication ...
Read more >
Spring Security : Set Remember me Service while using ...
How to configure spring security to set remember me service while using a customAuthenticationFilter extending AbstractPreAuthenticatedProcessingFilter and ...
Read more >
org.springframework.security.web ... - Download JAR files
AbstractPreAuthenticatedProcessingFilter maven / gradle build tool code. ... SecurityContextHolder; import org.springframework.security.web.
Read more >
Spring Security Reference - Framework Repositories
PRE_AUTH_FILTER, AbstractPreAuthenticatedProcessingFilter Subclasses, N/A ... The security context is established by calling SecurityContextHolder.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found