JWT(JSON Web Token)

1.什么是JWT?

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全传递声明的紧凑且自包含的方式。它由三部分组成:

Header(头部) – 说明令牌类型和签名算法
Payload(负载) – 包含实际的用户数据和其他声明
Signature(签名) – 用于验证消息的完整性

典型JWT格式:xxxxx.yyyyy.zzzzz(Base64Url编码的三部分用点连接)

2.JWT能解决什么问题?

认证问题:替代传统的session-cookie机制

服务端不再需要存储session状态
天然支持无状态分布式架构

跨域/跨服务认证

适合微服务架构下的单点登录(SSO)
适合前后端分离项目

信息自包含

包含用户基本信息和权限声明
减少数据库查询次数

安全传输

通过签名防止数据篡改

可选择加密保护敏感信息

3.使用步骤(以Spring Boot + Java JWT为例)

1. 添加依赖(pom.xml)
<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>

2. 创建JWT工具类
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;

public class JwtUtil {
            
    // 密钥(应存储在安全配置中)
    private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    // 有效期(毫秒)
    private static final long EXPIRATION_TIME = 86400000; // 24小时
    
    // 生成JWT
    public static String generateToken(String username, String role) {
            
        return Jwts.builder()
                .setSubject(username)
                .claim("role", role) // 自定义声明
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SECRET_KEY)
                .compact();
    }
    
    // 解析JWT
    public static Claims parseToken(String token) {
            
        return Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
    
    // 验证JWT
    public static boolean validateToken(String token) {
            
        try {
            
            Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            
            return false;
        }
    }
}

3. 实现登录接口(颁发token)
@RestController
@RequestMapping("/auth")
public class AuthController {
            
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
            
        // 1. 验证用户名密码(伪代码)
        User user = userService.authenticate(request.getUsername(), request.getPassword());
        
        // 2. 生成JWT
        String token = JwtUtil.generateToken(user.getUsername(), user.getRole());
        
        // 3. 返回token
        return ResponseEntity.ok(new JwtResponse(token));
    }
}

@Data
class LoginRequest {
            
    private String username;
    private String password;
}

@Data
class JwtResponse {
            
    private String token;
    
    public JwtResponse(String token) {
            
        this.token = token;
    }
}

4. 创建JWT验证过滤器
public class JwtAuthenticationFilter extends OncePerRequestFilter {
            
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain)
        throws ServletException, IOException {
            
        
        // 1. 从请求头获取token
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) {
            
            filterChain.doFilter(request, response);
            return;
        }
        
        String token = header.substring(7);
        
        // 2. 验证token
        try {
            
            Claims claims = JwtUtil.parseToken(token);
            
            // 3. 创建认证对象
            UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(
                    claims.getSubject(), 
                    null, 
                    Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + claims.get("role")))
                );
            
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            
            // 4. 设置安全上下文
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } catch (Exception e) {
            
            // token无效
            SecurityContextHolder.clearContext();
        }
        
        filterChain.doFilter(request, response);
    }
}

5. 配置Spring Security
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
            
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
            
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            .antMatchers("/auth/login").permitAll()
            .anyRequest().authenticated();
    }
}

4.JWT的安全实践

密钥保护

使用足够强度的密钥(HS256至少32字节)
不要硬编码密钥,使用安全配置管理

有效期控制

设置合理的expiration时间
实现refresh token机制更新token

防止盗用

使用HTTPS传输
将token存储在HttpOnly cookie中(防XSS)

敏感数据

Payload中不要存储密码等敏感信息
必要时可以使用JWE(JSON Web Encryption)加密

5.常见问题解决方案

问题1:Token失效问题

场景:用户修改密码后旧token仍有效
解决方案

维护token黑名单
使用JWT的jti(唯一标识)配合redis记录有效token

问题2:Token被盗

防护措施

实现IP绑定检查
添加指纹识别(browser fingerprint)

问题3:服务端注销

方案对比

方案 优点 缺点
短期令牌 实现简单 频繁重新认证
令牌黑名单 即时生效 需要额外存储
双令牌机制 平衡安全与体验 实现较复杂
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容