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.
✅ 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.
- 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 💻☕