Spring Security
API Authentication
Multiple Authentication Methods
API Endpoints
Software Development

Spring multiple authentication methods for different api endpoints

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Many Spring applications need different authentication methods for different endpoint groups. Public API consumers may use JWT bearer tokens, internal tools may use HTTP Basic, and admin pages may use form or OAuth login. Spring Security supports this cleanly with multiple filter chains matched by request patterns.

Use Multiple SecurityFilterChain Beans

In Spring Security 6 style configuration, define separate SecurityFilterChain beans with explicit matchers and order. The first matching chain handles the request.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.core.annotation.Order;
4import org.springframework.http.HttpMethod;
5import org.springframework.security.config.Customizer;
6import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7import org.springframework.security.web.SecurityFilterChain;
8
9@Configuration
10public class SecurityConfig {
11
12    @Bean
13    @Order(1)
14    SecurityFilterChain internalChain(HttpSecurity http) throws Exception {
15        http
16            .securityMatcher("/internal/**")
17            .authorizeHttpRequests(auth -> auth
18                .requestMatchers(HttpMethod.GET, "/internal/health").permitAll()
19                .anyRequest().hasRole("OPS")
20            )
21            .httpBasic(Customizer.withDefaults())
22            .csrf(csrf -> csrf.disable());
23
24        return http.build();
25    }
26
27    @Bean
28    @Order(2)
29    SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
30        http
31            .securityMatcher("/api/**")
32            .authorizeHttpRequests(auth -> auth
33                .requestMatchers("/api/public/**").permitAll()
34                .anyRequest().authenticated()
35            )
36            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
37
38        return http.build();
39    }
40
41    @Bean
42    @Order(3)
43    SecurityFilterChain fallbackChain(HttpSecurity http) throws Exception {
44        http
45            .authorizeHttpRequests(auth -> auth.anyRequest().denyAll());
46        return http.build();
47    }
48}

This pattern keeps endpoint intent explicit and avoids one oversized security rule block.

Authentication Providers and User Sources

Different chains can still share providers where appropriate. For example, Basic auth endpoints may use in-memory or LDAP users, while JWT endpoints rely on token signature validation and claim mapping.

If your JWT uses custom claim names for roles, configure a converter so authorities map consistently.

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.core.convert.converter.Converter;
3import org.springframework.security.authentication.AbstractAuthenticationToken;
4import org.springframework.security.oauth2.jwt.Jwt;
5import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
6import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
7
8@Bean
9Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthConverter() {
10    JwtGrantedAuthoritiesConverter granted = new JwtGrantedAuthoritiesConverter();
11    granted.setAuthorityPrefix("ROLE_");
12    granted.setAuthoritiesClaimName("roles");
13
14    JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
15    converter.setJwtGrantedAuthoritiesConverter(granted);
16    return converter;
17}

Consistent authority mapping prevents authorization surprises across endpoint groups.

Endpoint Design and Documentation

Security complexity drops when routes are clearly segmented. Keep URL namespaces clean:

  • /api/public for anonymous endpoints.
  • /api/private for JWT-protected business APIs.
  • /internal for operational tools.

Document expected authentication type per namespace in API docs. Client integration issues often come from unclear auth expectations rather than broken security code.

Add integration tests that verify both success and failure for each endpoint family. Tests should assert status codes, not only controller logic.

Migration Notes

If your codebase still uses WebSecurityConfigurerAdapter, migrate gradually to bean-based filter chains. The new style is easier to reason about and aligns with current Spring Security recommendations.

During migration, keep one explicit fallback chain that denies unmatched traffic. This protects against accidentally exposed routes when path matchers are incomplete.

For larger systems, create a security architecture matrix that maps each endpoint group to authentication type, token issuer, required authorities, and expected client type. This simple artifact reduces onboarding time and helps reviewers catch mismatched matcher rules before they reach production safely. It also supports audit conversations by making endpoint security intent visible outside source code.

Common Pitfalls

A common pitfall is overlapping request matchers with incorrect @Order. If a broad matcher is evaluated first, specific chains may never run.

Another issue is mixing stateful and stateless auth defaults. JWT endpoints are usually stateless, while form or session flows are stateful. Configure session policy deliberately per chain.

Developers also forget CORS and CSRF differences by endpoint type. Browser clients and machine clients often need different settings, so apply them intentionally per route group.

Finally, teams skip security integration tests and rely on manual checks. This makes regressions likely when endpoints are added or reorganized.

Summary

  • Define multiple SecurityFilterChain beans with explicit matchers and order.
  • Map each endpoint namespace to one clear authentication mechanism.
  • Configure authority conversion consistently for token-based auth.
  • Keep a deny-all fallback chain to prevent accidental exposure.
  • Add integration tests that validate auth behavior per endpoint family.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.