How to Configure Spring Security – Secure Different URLs Differently

In the previous Part 1 & Part 2 tutorial series, we developed REST APIs, secured them with OAuth2 JWT authentication, and documented them using OpenAPI 3 spec. Now, we are gonna configure multiple HttpSecurity instances in Spring Security to secure one API with basic authentication and all other APIs with OAuth2 JWT authentication.

Introduction

Sometimes we might need different authentication for different APIs. For instance, in our previous tutorial, we disabled basic auth and enabled only JWT auth. This is because, when the basic auth is enabled, the secured endpoints can then be accessed with either basic auth or JWT auth which is not what we want.

Since we have disabled the basic auth, In the /signin API, we had to accept the user credentials in plain text format in the request body and perform the authentication manually with the help of AuthenticationManager. After successful authentication, we had to set the authentication object in the Spring SecurityContextHolder.

We can avoid this type of manual authentication if we configure basic auth for the token generation API alone and JWT auth for all other APIs.

What you’ll do?

In a nutshell, we are going to perform the following steps:

  • Expose a POST API with mapping /token. On passing the username and password in the authorization header, it will generate a JSON Web Token (JWT).
  • Configure Spring Security to enable basic auth only for /token API.

What you’ll need?

  • IntelliJ or any other IDE of your choice
  • JDK 17
  • MySQL Server 8

Developing Token API

Modifying Auth Controller

Let’s add a new /token POST API in AuthController that returns the access token when the user email/password is passed in the Authorization header.

AuthController.java

    @PostMapping("/token")
    public ResponseEntity<?> getToken(Authentication authentication) {
        LocalUser localUser = (LocalUser) authentication.getPrincipal();
        String jwt = getToken(localUser);
        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }

Configuring Spring Security

WebConfig.java

Let’s add one more HttpSecurity instance for enabling basic authentication only for the new /token API. Also, we will annotate the new method with @Order(1) annotation to let Spring Security know that it has the highest precedence over others. Therefore, it will be executed first.

    @Bean
    @Order(1)
    public SecurityFilterChain basicAuthSecurityFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http
                .securityMatcher("/api/auth/token")
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated()
                )
                .csrf().disable()
                .httpBasic(withDefaults());
        // @formatter:on
        return http.build();
    }

Testing

Swagger UI Basic Auth

We need to provide the credentials in the basicAuth and authorize it.

Swagger UI Basic Auth

Accessing Token API with Basic Auth

Accessing Token API with Basic Auth

Accessing Secured API with Basic Auth

If we try to execute a secured API other than /token API with basic auth, we will get HTTP status 401 Unauthorized.

Accessing Secured API with Basic Auth

Accessing Secured API with JWT Auth

If we try to execute a secured API other than /token API with JWT auth, we will get HTTP 200 response as expected.

Accessing Secured API with JWT Auth

Accessing Token API with JWT Auth

If we try to execute a token API with JWT auth, then it will ask for the credentials since JWT will be ignored for this request.

Accessing Token API with JWT Auth

Source Code

https://github.com/JavaChinna/spring-boot-oauth2-jwt

Conclusion

That’s all folks. In this article, we have configured Spring Security to Secure different URLs with different authentication. In the next article, we will see how we can initialize the database with data from a CSV file on application startup.

Thank you for reading.

Leave a Reply