š Spring Security: Simple Authentication with Opaque Tokens
This guide demonstrates opaque token authentication in Spring Boot and compares it with JWT for clarity.
Opaque tokens are random strings issued by an auth server. Only the server can interpret them. They are easy to revoke and secure by default. JWTs are self-contained tokens with encoded claims and a signature.
š§± Key Components
- AuthUser: Represents authenticated user data.
- UserAuthentication: Implements Spring Security's
Authentication
. - TokenAuthenticationFilter: Validates incoming tokens.
- SecurityConfig: Integrates the filter into Spring Security.
Step 1: Define AuthUser
package com.example.security.user;
import java.util.List;
public record AuthUser(String userId, List<String> roles) {}
Step 2: Implement UserAuthentication
package com.example.security.authentication;
import com.example.security.user.AuthUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.Collection;
import java.util.stream.Collectors;
public class UserAuthentication implements Authentication {
private final AuthUser authUser;
public UserAuthentication(AuthUser authUser) {
this.authUser = authUser;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authUser.roles().stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
@Override
public Object getCredentials() { return null; }
@Override
public Object getDetails() { return null; }
@Override
public Object getPrincipal() { return authUser; }
@Override
public boolean isAuthenticated() { return true; }
@Override
public void setAuthenticated(boolean isAuthenticated) {
throw new UnsupportedOperationException();
}
@Override
public String getName() { return authUser.userId(); }
}
Step 3: Token Authentication Filter
package com.example.security.filter;
import com.example.security.authentication.UserAuthentication;
import com.example.security.user.AuthUser;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TokenAuthenticationFilter extends OncePerRequestFilter {
private final TokenService tokenService;
public TokenAuthenticationFilter(TokenService tokenService) {
this.tokenService = tokenService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = extractToken(request);
if (token != null && tokenService.isValid(token)) {
AuthUser authUser = tokenService.getUserFromToken(token);
UserAuthentication authentication = new UserAuthentication(authUser);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
Step 4: Security Configuration
package com.example.security.config;
import com.example.security.filter.TokenAuthenticationFilter;
import com.example.security.service.TokenService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final TokenService tokenService;
public SecurityConfig(TokenService tokenService) {
this.tokenService = tokenService;
}
@Bean
public HttpSecurity configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(new TokenAuthenticationFilter(tokenService), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/login", "/register").permitAll()
.anyRequest().authenticated();
return http;
}
}
āļø Opaque Tokens vs JWT
1. Definition
Opaque Token: Random string, only auth server can interpret.
JWT (JSON Web Token): Self-contained token with encoded claims (user ID, roles, expiration) and a signature.
### 2. Structure
Feature Opaque Token JWT (JSON Web Token)
Readable by client? ā No ā token content is hidden ā
Yes ā can decode base64, but signature prevents tampering
Self-contained? ā No ā server must store and check token ā
Yes ā carries claims like user ID, roles, and expiration
Verification ā
Must validate with authorization server ā
Can verify locally using the signature
Revocation ā
Easy ā simply delete token from server ā ļø Difficult ā requires blacklist or short expiration
Size Small ā just a random string Larger ā includes claims and signature
Flexibility Simple ā no extra data inside High ā can include roles, scopes, and expiration for authorization decisions
3. How They Work
Opaque Token Flow:
-
Client gets token.
-
Client calls API.
-
API validates token with auth server.
-
Server returns user info.
JWT Flow:
-
Client gets JWT.
-
Client calls API.
-
API verifies signature locally.
-
API reads claims for authorization.
4. Pros & Cons
Opaque Tokens
-
ā Secure by default
-
ā Easy to revoke
-
ā Requires server-side check per request
JWT
-
ā Stateless, no server call
-
ā Can include useful info in token
-
ā Harder to revoke
-
ā Larger size, may expose payload if not encrypted
5. When to Use Which
Opaque Tokens: Internal APIs, high security, easy revocation.
JWT: Microservices or distributed systems, stateless authentication, rarely changing claims.
ā Summary
Opaque tokens provide secure and easily revocable authentication, while JWTs allow stateless verification and include useful claims. Choose based on architecture and security needs.