2

I'm a Spring's beginner and I'm trying to build a REST API, connected to a React frontend in order to learn these technologies. In order to secure this API, I added an apiKey mechanism with Spring Security, by creating a filter that checks a specific header key (API-KEY in this case), and that only allows requests that match the correct api key value.

I added this filter in my security config, which extends WebSecurityConfigurerAdapter. However, I'd like to add another authentication mechanism just to authenticate my users, in a traditional username/password way. I'm a bit lost, I read a lot of articles but all of these are using the same mechanism (filter + configure the security component). But I really don't know how to gather these two mechanisms.

I would like all requests are intercepted to check the API-KEY value, but I also would like to have an anonymous and authenticated parts in my app. How could I achieve this ? I found some elements like interceptors but it seems to be only available for spring-mvc app.

Here's the filter I'm using :

public class ApiKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {

    /**
     * The request header we want to check with our apiKey
     */
    private String principalRequestHeader;


    public ApiKeyAuthFilter(String principalRequestHeader) {
        this.principalRequestHeader = principalRequestHeader;
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        return request.getHeader(principalRequestHeader);
    }

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

And here's my security config :

@Configuration
@EnableWebSecurity
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * The header corresponding to our apiKey
     */
    @Value("${application.security.requestKey}")
    private String apiKeyHeader;

    /**
     * The api key value we want to test with the header value
     */
    @Value("${application.security.apiKey}")
    private String apiKeyValue;


    Logger logger = LoggerFactory.getLogger(ApiSecurityConfig.class);


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ApiKeyAuthFilter filter = new ApiKeyAuthFilter(this.apiKeyHeader);

        filter.setAuthenticationManager(new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                final String principal = (String) authentication.getPrincipal();

                if (!apiKeyValue.equals(principal)) {
                    throw new BadCredentialsException("The API key was not found or doesn't match the correct value");
                }

                logger.info("Connexion autorisée");
                authentication.setAuthenticated(true);
                return authentication;
            }
        });

        http.cors().and().
                antMatcher("/api/**").
                csrf().disable().
                sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
                and().
                addFilter(filter).
                authorizeRequests().anyRequest().authenticated();
    }
}

Do you have any clue to setup this kind of authentication ? I saw that we could define an order in our filter with methods like addFilterAfter() or addFilterBefore(), but I don't know how to setup this with my usecase. I also found this post : How to config multiple level authentication for spring boot RESTful web service? which seems to have the same requirements, I tried the solution provided but the authentication isn't dynamic (it's only using a string "valid-user" for its authentication filter, and I need to authenticate through my User entity stored in an in-memory h2 database. How to achieve this ?

Thank's a lot for your answers and have a nice day !

Elrendil
  • 45
  • 7

0 Answers0