Spring Boot应用基于自定义Token的身份认证实现方案咨询
Alright, let's break down how to build this custom token-based authentication flow for your Spring Boot REST API. I’ve implemented this exact pattern a few times, so here’s a hands-on, step-by-step guide that fits your requirements:
1. Define Token Entity & Repository
First, create an entity to represent your custom auth token in the database, along with a repository to handle database operations:
@Entity @Table(name = "auth_tokens") public class AuthToken { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String token; @Column(nullable = false) private Long userId; // Link to your existing user entity @Column(nullable = false) private LocalDateTime expiryDate; // Getters, setters, and constructors }
public interface AuthTokenRepository extends JpaRepository<AuthToken, Long> { // Find token by its string value for validation Optional<AuthToken> findByToken(String token); }
2. Build a Token Validation Service
Create a service class to encapsulate token validation logic—this is where you'll check if the token exists in the DB and hasn't expired:
@Service public class TokenService { private final AuthTokenRepository tokenRepository; // Constructor injection (preferred over @Autowired) public TokenService(AuthTokenRepository tokenRepository) { this.tokenRepository = tokenRepository; } public AuthToken validateToken(String token) { return tokenRepository.findByToken(token) // Check if token is still valid (not expired) .filter(authToken -> authToken.getExpiryDate().isAfter(LocalDateTime.now())) .orElseThrow(() -> new AuthenticationCredentialsNotFoundException("Invalid or expired auth token")); } }
3. Custom Authentication Filter
Spring Security uses filters to handle auth, so we'll create a custom filter that extracts the token from incoming requests, validates it, and sets the authenticated user in the security context:
@Component public class CustomTokenAuthenticationFilter extends OncePerRequestFilter { private final TokenService tokenService; public CustomTokenAuthenticationFilter(TokenService tokenService) { this.tokenService = tokenService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // Extract token from request header (adjust the header name to match what your frontend uses) String authToken = request.getHeader("X-Auth-Token"); if (authToken != null && !authToken.isBlank()) { try { // Validate the token against the database AuthToken validToken = tokenService.validateToken(authToken); // Create a UserDetails object (customize this to match your actual user model) UserDetails userDetails = User.withUsername(validToken.getUserId().toString()) .password("") // Password isn't needed here since we're using token-based auth .authorities("ROLE_USER") // Add your user's roles/permissions here .build(); // Create an authentication token and set it in the security context UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (AuthenticationException e) { // Clear context and return 401 if token is invalid SecurityContextHolder.clearContext(); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); return; } } // Continue with the rest of the filter chain filterChain.doFilter(request, response); } }
4. Configure Spring Security
Wire up your custom filter into the Spring Security chain, define public/private endpoints, and handle auth exceptions:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomTokenAuthenticationFilter customTokenFilter; public SecurityConfig(CustomTokenAuthenticationFilter customTokenFilter) { this.customTokenFilter = customTokenFilter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // Disable CSRF since we're building a REST API (not form-based) .csrf(csrf -> csrf.disable()) // Define authorization rules .authorizeHttpRequests(auth -> auth .requestMatchers("/api/public/**").permitAll() // Allow public access to these endpoints (e.g., login, register) .anyRequest().authenticated() // All other endpoints require valid auth ) // Add our custom filter BEFORE the default username/password filter .addFilterBefore(customTokenFilter, UsernamePasswordAuthenticationFilter.class) // Handle auth errors (return 401 for missing/invalid tokens) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Missing or invalid auth token"); }) ); return http.build(); } }
- Token Refresh Mechanism: Add a refresh token flow so users don't have to re-login frequently. Store refresh tokens in the DB with longer expiry, and let users exchange a valid refresh token for a new access token.
- Database Optimization: Add a unique index on the
tokencolumn in yourauth_tokenstable to speed up lookup queries—this is crucial since every authenticated request will hit this query. - Security Hardening:
- Use HTTPS to prevent token interception during transit.
- Store tokens in
HttpOnlycookies on the frontend instead of local storage to reduce XSS attack risks. - Generate long, random tokens (use
UUID.randomUUID().toString()or a secure token generator) to make brute-force attacks harder. - Set short expiry times for access tokens (e.g., 15-30 minutes) to limit the impact of a leaked token.
内容的提问来源于stack exchange,提问作者Shubham Rana

