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

Spring Boot控制器中如何在登录响应时向客户端发送Bearer JWT授权头?

在Spring Security+JWT应用中把Bearer令牌放入登录响应头

要实现登录成功后将Bearer格式的JWT令牌写入响应头,主要有两种方案,根据你的登录实现方式选择:

方案一:自定义登录接口(推荐,灵活可控)

如果是自己编写登录接口,验证用户身份后生成JWT,直接往响应头添加即可:

1. 编写登录Controller

@RestController
@RequestMapping("/auth")
public class AuthController {

    private final JwtTokenProvider jwtTokenProvider;
    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    public AuthController(JwtTokenProvider jwtTokenProvider,
                          UserDetailsService userDetailsService,
                          PasswordEncoder passwordEncoder) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @PostMapping("/login")
    public ResponseEntity<Void> login(@RequestBody LoginRequest loginRequest) {
        // 验证用户名和密码
        UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
        if (!passwordEncoder.matches(loginRequest.getPassword(), userDetails.getPassword())) {
            throw new BadCredentialsException("用户名或密码错误");
        }

        // 生成JWT令牌
        String token = jwtTokenProvider.generateToken(userDetails);

        // 构建响应头,添加Bearer令牌
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        // 返回响应,无响应体(也可根据需求返回用户信息)
        return ResponseEntity.ok().headers(headers).build();
    }
}

// 登录请求DTO
record LoginRequest(String username, String password) {}

2. JWT生成工具类

@Component
public class JwtTokenProvider {

    @Value("${jwt.secret}")
    private String secretKey;

    @Value("${jwt.expiration-ms}")
    private long expirationMs;

    public String generateToken(UserDetails userDetails) {
        // 设置JWT声明信息
        Claims claims = Jwts.claims().setSubject(userDetails.getUsername());
        claims.put("roles", userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .toList());

        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expirationMs);

        // 生成并返回JWT
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }
}

方案二:自定义Spring Security登录成功处理器(适配默认表单登录)

如果使用Spring Security默认的表单登录流程,需要自定义AuthenticationSuccessHandler来改写响应头:

1. 自定义成功处理器

@Component
public class JwtLoginSuccessHandler implements AuthenticationSuccessHandler {

    private final JwtTokenProvider jwtTokenProvider;
    private final ObjectMapper objectMapper;

    public JwtLoginSuccessHandler(JwtTokenProvider jwtTokenProvider, ObjectMapper objectMapper) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.objectMapper = objectMapper;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication) throws IOException {
        // 获取登录用户信息
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        // 生成JWT
        String token = jwtTokenProvider.generateToken(userDetails);
        // 添加Bearer令牌到响应头
        response.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        // 设置响应类型并返回登录成功信息
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        objectMapper.writeValue(response.getWriter(), Map.of("message", "登录成功"));
    }
}

2. 配置Spring Security

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtLoginSuccessHandler loginSuccessHandler;
    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    public SecurityConfig(JwtLoginSuccessHandler loginSuccessHandler,
                          UserDetailsService userDetailsService,
                          PasswordEncoder passwordEncoder) {
        this.loginSuccessHandler = loginSuccessHandler;
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/auth/login").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(form -> form
                        .loginProcessingUrl("/auth/login") // 表单提交的URL
                        .successHandler(loginSuccessHandler) // 绑定自定义成功处理器
                );

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

关键配置与注意事项

  • 在application.properties中配置JWT参数:
    jwt.secret=your-strong-secret-key-keep-it-safe
    jwt.expiration-ms=86400000 # 24小时有效期
    
  • 客户端需要从响应头的Authorization字段提取令牌,后续请求时将该令牌放入请求头的Authorization字段(格式同样为Bearer {token})。
  • 生产环境中,JWT密钥要使用高复杂度的随机字符串,避免硬编码,建议通过环境变量注入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 14:27:03