Spring Security中LDAP认证返回Cookie/Token实现求助
Hey there! Since you're new to Spring Security, let's walk through how you can adjust your existing LDAP authentication setup to return either a session cookie (which Spring Security handles by default) or a token like JWT instead of the raw Principal object.
Option 1: Cookie-Based Session Authentication (Simpler, Default Approach)
Spring Security already uses session cookies out of the box once a user authenticates successfully. The SESSIONID cookie is automatically set in the response, and subsequent requests use this cookie to validate the user's session.
To customize what you return after login and ensure your cookie setup is configured properly:
Customize the Login Success Response
Create a customAuthenticationSuccessHandlerto replace the default behavior, so you can return user details along with confirmation that the session cookie is active:import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import java.util.stream.Collectors; public class CustomLoginSuccessHandler implements AuthenticationSuccessHandler { private final ObjectMapper objectMapper = new ObjectMapper(); @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { // Build a custom response object with user info var userResponse = new UserResponse( authentication.getName(), authentication.getAuthorities().stream() .map(grantedAuthority -> grantedAuthority.getAuthority()) .collect(Collectors.toList()) ); // Return JSON response response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); objectMapper.writeValue(response.getWriter(), userResponse); } // Simple record to hold user response data public record UserResponse(String username, List<String> roles) {} }Update Your Security Configuration
Modify yourSecurityConfigurationclass to use this success handler and adjust session/cookie settings if needed:@Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().and() .logout().logoutSuccessHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_OK)).and() .authorizeRequests() .antMatchers("/index.html", "/", "/home", "/login", "/assets/**").permitAll() .anyRequest().authenticated() .and() .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() // Add custom success handler .formLogin().successHandler(new CustomLoginSuccessHandler()) // Optional: Customize session cookie properties .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .rememberMe() // Optional: Add "remember me" cookie if needed .key("your-secure-secret-key") .tokenValiditySeconds(86400); // 1 day validity }Now when you hit
/login(or your/custom/user-loginendpoint after authentication), you'll get a JSON response with user details, and theSESSIONIDcookie will be automatically set in the browser for subsequent authenticated requests.
Option 2: Token-Based Authentication (JWT)
If you want stateless authentication using JWT tokens (no server-side session), here's how to implement it:
Step 1: Add Dependencies
Add these to your pom.xml (Maven):
<!-- JJWT for JWT handling --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency>
Step 2: Create a JWT Utility Class
This class will handle generating and validating JWT tokens:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Component public class JwtUtil { // Use a secure secret key in production (store in environment variables!) private final String SECRET_KEY = "your-strong-secret-key-here"; public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); } private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, userDetails.getUsername()); } private String createToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours expiration .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }
Step 3: Custom Authentication Success Handler for JWT
Modify the success handler to generate a JWT and return it in the response:
import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import com.fasterxml.jackson.databind.ObjectMapper; @Component public class JwtLoginSuccessHandler implements AuthenticationSuccessHandler { private final JwtUtil jwtUtil; private final ObjectMapper objectMapper = new ObjectMapper(); public JwtLoginSuccessHandler(JwtUtil jwtUtil) { this.jwtUtil = jwtUtil; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { // Convert Authentication to UserDetails UserDetails userDetails = (UserDetails) authentication.getPrincipal(); String jwt = jwtUtil.generateToken(userDetails); // Return JWT in response body (you can also set it in a cookie if preferred) var tokenResponse = new TokenResponse("Bearer", jwt); response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); objectMapper.writeValue(response.getWriter(), tokenResponse); } public record TokenResponse(String tokenType, String token) {} }
Step 4: Add JWT Authentication Filter
This filter will validate the JWT in incoming requests:
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; 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; @Component public class JwtRequestFilter extends OncePerRequestFilter { private final UserDetailsService userDetailsService; private final JwtUtil jwtUtil; public JwtRequestFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) { this.userDetailsService = userDetailsService; this.jwtUtil = jwtUtil; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); username = jwtUtil.extractUsername(jwt); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } chain.doFilter(request, response); } }
Step 5: Update Security Configuration for JWT
Modify your SecurityConfiguration to use stateless sessions, the JWT filter, and the custom success handler:
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER) protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter { private final JwtLoginSuccessHandler jwtLoginSuccessHandler; private final JwtRequestFilter jwtRequestFilter; public SecurityConfiguration(JwtLoginSuccessHandler jwtLoginSuccessHandler, JwtRequestFilter jwtRequestFilter) { this.jwtLoginSuccessHandler = jwtLoginSuccessHandler; this.jwtRequestFilter = jwtRequestFilter; } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // CSRF is typically disabled for stateless JWT auth .authorizeRequests() .antMatchers("/index.html", "/", "/home", "/login", "/assets/**").permitAll() .anyRequest().authenticated() .and() .httpBasic() .and() .logout().logoutSuccessHandler((req, res, auth) -> res.setStatus(HttpServletResponse.SC_OK)) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No server-side sessions .and() .formLogin().successHandler(jwtLoginSuccessHandler); // Add JWT filter before UsernamePasswordAuthenticationFilter http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } @Bean public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() { ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(LDAPAuthController.domain, LDAPAuthController.URL); // Optional: Set user details mapper to populate authorities properly provider.setUserDetailsContextMapper(new ActiveDirectoryUserDetailsMapper()); return provider; } @Override @Bean public UserDetailsService userDetailsService() { return super.userDetailsService(); } }
Key Notes for Your Setup
- LDAP Integration: Your existing
ActiveDirectoryLdapAuthenticationProvideris already handling the core LDAP auth logic, so we're just adding layers on top to manage session cookies or tokens. - Security Best Practices: In production, use a strong secret key for JWT (store it in environment variables, not hardcoded), enforce HTTPS to prevent token/cookie interception, and adjust token expiration times based on your application's needs.
- Testing: For cookie auth, your browser will automatically send the
SESSIONIDcookie with each request after login. For JWT, you'll need to include theAuthorization: Bearer <your-token>header in all authenticated requests.
内容的提问来源于stack exchange,提问作者Ramstack

