Spring Security: Using JWT and Basic Auth for Different URL Patterns

Hello, fellow developers!
If you’ve ever scratched your head wondering, “How can I use both JWT and Basic Auth in the same Spring Boot application for different URL patterns?” — you’re not alone. This blog post is for developers like you who want to secure different parts of a Spring Boot app using different authentication mechanisms.
By the end, you’ll be able to:
Secure some APIs with Basic Auth
Secure others with JWT
Configure everything cleanly with Spring Security
Let’s dive right in!
Why Would You Need Multiple Authentication Types?
There are plenty of real-world use cases:
- Public APIs secured via Basic Auth (for clients using tools like Postman or curl)
- Mobile/web clients using JWT tokens for stateless authentication
- Admin endpoints that are locked down with Basic Auth
- User endpoints that use JWT-based login flow
Instead of choosing either JWT or Basic Auth, Spring Security gives us the power to use both, tailored to specific URL patterns.
High-Level Approach
Here’s how we’re going to do it:
- Create two
SecurityFilterChain
beans:- One for Basic Auth endpoints (
/api/admin/**
) - One for JWT-secured endpoints (
/api/user/**
)
- One for Basic Auth endpoints (
- Define an authentication provider for each type.
- Register both configurations with proper
@Order
.
Dependencies (Spring Boot 3.x)
Add the following to your pom.xml
:
org.springframework.boot
spring-boot-starter-security
io.jsonwebtoken
jjwt-api
0.11.5
io.jsonwebtoken
jjwt-impl
0.11.5
runtime
io.jsonwebtoken
jjwt-jackson
0.11.5
runtime
1. Basic Authentication for /api/admin/**
Let’s start with securing admin APIs using Basic Auth.
@Configuration
@Order(1)
public class BasicAuthSecurityConfig {
@Bean
public SecurityFilterChain basicAuthFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/admin/**")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public UserDetailsService basicUserDetailsService() {
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Try accessing:
curl -u admin:admin123 http://localhost:8080/api/admin/dashboard
2. JWT Authentication for /api/user/**
We’ll now secure user APIs using JWTs. First, create a JWT utility class.
JWT Utility Class
@Component
public class JwtUtils {
private final String secret = "mysecretkey";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(Keys.hmacShaKeyFor(secret.getBytes()))
.compact();
}
public String extractUsername(String token) {
return Jwts.parserBuilder()
.setSigningKey(secret.getBytes())
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
extractUsername(token);
return true;
} catch (Exception e) {
return false;
}
}
}
JWT Filter
@Component
public class JwtAuthFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String jwt = authHeader.substring(7);
String username = jwtUtils.extractUsername(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
if (jwtUtils.validateToken(jwt)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
filterChain.doFilter(request, response);
}
}
JWT Security Configuration
@Configuration
@Order(2)
public class JwtSecurityConfig {
@Autowired
private JwtAuthFilter jwtAuthFilter;
@Bean
public SecurityFilterChain jwtFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/user/**")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public UserDetailsService jwtUserDetailsService() {
UserDetails user = User.withUsername("john")
.password(passwordEncoder().encode("john123"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Testing It Out
Basic Auth
curl -u admin:admin123 http://localhost:8080/api/admin/dashboard
JWT Flow
- Simulate login and generate token (you can hardcode for now).
String jwt = jwtUtils.generateToken("john");
- Access secure API:
curl -H "Authorization: Bearer " http://localhost:8080/api/user/profile
Final Thoughts
This dual-authentication strategy is incredibly useful when:
- You need backward compatibility for legacy clients (Basic Auth)
- You want modern stateless security for SPAs/mobile apps (JWT)
- You’re building multi-tenant or role-separated services
Spring Security’s filter chains and @Order
mechanism make it super easy to support both.
SEO Keywords (for Google’s love
)
- Spring Security multiple authentication providers
- Spring Boot Basic Auth and JWT together
- Spring Security filter chain for different URL patterns
- Secure REST API with JWT and Basic Auth
- Spring Security custom SecurityFilterChain
Need Help?
Got stuck? Want the full source code or a GitHub repo? Drop a comment below or message me on LinkedIn!
Until next time, happy coding!
– Your friendly Spring Coach