You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.


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:

  1. Customize the Login Success Response
    Create a custom AuthenticationSuccessHandler to 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) {}
    }
    
  2. Update Your Security Configuration
    Modify your SecurityConfiguration class 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-login endpoint after authentication), you'll get a JSON response with user details, and the SESSIONID cookie 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 ActiveDirectoryLdapAuthenticationProvider is 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 SESSIONID cookie with each request after login. For JWT, you'll need to include the Authorization: Bearer <your-token> header in all authenticated requests.

内容的提问来源于stack exchange,提问作者Ramstack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:46:18