Spring Boot 和 Spring Security 实现 JWT 认证

一、 JWT(JSON Web Token)

1.JWT 基本概念

JWT 是一种开放标准(RFC 7519),用于在网络应用间安全传递 JSON 格式的声明信息。其核心特点包括:

  1. 紧凑性:通过 Base64URL 编码生成字符串,可通过 URL、HTTP Header 或 POST 参数传输。
  2. 自包含:负载(Payload)直接携带用户信息(如 ID、角色),减少服务端查询数据库的开销。
  3. 数字签名:使用密钥(HMAC)或公钥/私钥(RSA)签名,确保信息未被篡改。
2.JWT 结构

JWT 由三部分组成,以点号 . 分隔:Header.Payload.Signature

  1. Header(头部)

    • 描述令牌类型(typ: "JWT")和签名算法(如 alg: HS256),Base64URL 编码后形成第一部分。
    json 复制代码
    { "alg": "HS256", "typ": "JWT" }
  2. Payload(负载)

    • 存储声明(Claims),包含用户数据及标准声明(如 sub 用户 ID、exp 过期时间)。
    • 声明类型
      • 注册声明 (可选):预定义字段(iss 签发者、aud 受众)。
      • 公共声明 :自定义字段(如 name: "John")。
      • 私有声明:系统内部使用的自定义字段。

    注意 :Payload 仅 Base64URL 编码,未加密,避免存储敏感信息(如密码)。

  3. Signature(签名)

    • 对编码后的 Header 和 Payload 用指定算法(如 HMAC-SHA256)签名,密钥由服务端保管:
    js 复制代码
    HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
    • 签名验证数据完整性和来源真实性。
3.JWT 工作原理
  1. 认证阶段
    • 用户提交凭证(用户名/密码)→ 服务端验证通过 → 生成 JWT 返回客户端。
  2. 客户端存储
    • 客户端将 JWT 存入 localStoragesessionStorage 或 Cookie(建议 HttpOnly 防 XSS)。
  3. 请求携带
    • 后续请求在 HTTP Header 中添加:Authorization: Bearer <JWT>
  4. 服务端验证
    • 校验签名有效性、过期时间(exp)及受众(aud)→ 通过则允许访问资源。
4.JWT vs Session 认证
特性 JWT Session
服务端状态 无状态(信息在 Token 中) 需存储会话信息(内存/数据库)
扩展性 天然支持分布式系统 需 Session 共享机制(如 Redis)
安全性 签名防篡改;但需防 XSS 盗取 Token 依赖 Cookie;易受 CSRF 攻击
性能 减少数据库查询;但 Token 体积较大 需频繁查询会话数据

JWT 在微服务、跨域单点登录(SSO)场景中优势显著。


5.应用场景
  1. 身份认证:用户登录后,JWT 作为无状态凭证。
  2. 单点登录(SSO):一次登录生成 JWT,多系统共享认证状态。

二、Spring Security 集成 JWT

  1. 认证流程

    • 用户登录:客户端提交凭证 → 服务端验证并生成 JWT
    • 请求携带 :客户端在 Authorization: Bearer <token> 头中附加 JWT
    • 服务端验证:Spring Security 过滤器链解析并验证 JWT 有效性
  2. 技术优势

    • 无状态架构:服务端无需存储会话,适合微服务分布式系统
    • 细粒度授权:通过 JWT Claims 实现角色/权限动态控制
1.JWT 配置参数
yaml 复制代码
jwt:
  secret: "base64-encoded-secret"  # Base64 编码密钥
  expiration: 86400000             # Token 有效期(24小时)
  header: Authorization             # 请求头字段
2.Spring Security 配置
java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()          // 关闭 CSRF
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()  // 放行登录接口
            .anyRequest().authenticated()             // 其他接口需认证
            .and()
            .addFilterBefore(jwtAuthFilter(), UsernamePasswordAuthenticationFilter.class); // 添加 JWT 过滤器
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 密码加密器
    }
}
3.JWT 工具类实现
java 复制代码
@Component
public class JwtUtils {
    @Value("${jwt.secret}")
    private String secret;

    public String generateToken(UserDetails userDetails) {
        return Jwts.builder()
            .setSubject(userDetails.getUsername())   // 用户名作为主体
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expiration))
            .signWith(SignatureAlgorithm.HS512, secret) // HS512 算法签名
            .compact();
    }

    public Claims parseToken(String token) {
        return Jwts.parser()
            .setSigningKey(secret)
            .parseClaimsJws(token)
            .getBody();
    }
}

签名算法选择

算法类型 适用场景
HS256/HS512 单应用场景(对称加密)
RS256/RS512 多服务调用(非对称加密)

4.认证流程实现
(1)登录接口生成 Token
java 复制代码
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestBody LoginRequest request) {
        // 1. 验证用户名密码
        UserDetails user = userDetailsService.loadUserByUsername(request.getUsername());
        // 调用passwordEncoder验证密码是否一致
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            return ResponseEntity.status(401).build();
        }
        // 2. 生成 JWT
        String token = jwtUtils.generateToken(user);
        return ResponseEntity.ok(token);
    }
}

新增用户时使用PasswordEncoder加密密码。

(2)JWT 认证过滤器
java 复制代码
public class JwtAuthFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) {
            chain.doFilter(request, response); // 放行未认证请求
            return;
        }
        String token = header.substring(7);
        Claims claims = jwtUtils.parseToken(token); // 解析 Token
        // 构建 Authentication 对象并存入 SecurityContext
        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
            claims.getSubject(), null, AuthorityUtils.createAuthorityList("ROLE_USER")
        );
        SecurityContextHolder.getContext().setAuthentication(auth);
        chain.doFilter(request, response);
    }
}

在 Spring Security 中,UsernamePasswordAuthenticationToken 的 authorities 参数用于注入当前用户的​​权限集合​​(Collection<? extends GrantedAuthority>)。

6.常见问题与解决方案
(1)Token 失效场景
  • 签名不匹配:检查密钥一致性(确保 Base64 编码正确)
  • 过期时间(exp)未生效:服务器时间不同步 → 使用 NTP 同步
(2)跨域问题(CORS)
java 复制代码
@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.addAllowedOrigin("https://your-frontend.com");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
相关推荐
deeper_wind3 小时前
keeplived双击热备配置
linux·运维·网络
allenXer4 小时前
Spring Boot测试全景指南:JUnit 5 + Testcontainers实现单元与集成测试
spring boot·微服务·log4j
熟悉的新风景5 小时前
springboot项目或其他项目使用@Test测试项目接口配置-spring-boot-starter-test
java·spring boot·后端
技术猿188702783517 小时前
实现“micro 关键字搜索全覆盖商品”并通过 API 接口提供实时数据(一个方法)
开发语言·网络·python·深度学习·测试工具
zyhomepage7 小时前
科技的成就(六十九)
开发语言·网络·人工智能·科技·内容运营
Arva .8 小时前
HTTP常见误区
网络·网络协议·http
Synfuture阳途10 小时前
终端安全管理系统为什么需要使用,企业需要的桌面管理软件
网络·安全
LUCIAZZZ10 小时前
高性能网络模式-Reactor和Preactor
java·服务器·开发语言·网络·操作系统·计算机系统
k *10 小时前
网络编程-tcp连接:服务器与客户端
服务器·网络·tcp/ip
云云32111 小时前
亚矩阵云手机:破解 Yandex 广告平台多账号风控难题的利器
网络·科技·线性代数·智能手机·矩阵