当前位置: 首页 > news >正文

Spring Boot集成JWT:打造安全的RESTful API


Spring Boot集成JWT:打造安全的RESTful API


1. JWT简介

JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全传输信息。它广泛应用于身份认证和授权场景,尤其适合无状态的RESTful API开发。

JWT的组成

  1. Header:声明令牌类型(如JWT)和签名算法(如HS256)。
    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
  2. Payload:存储用户信息(如用户ID、角色)和其他元数据(如过期时间)。
    {
      "sub": "admin",
      "iat": 1625145600,
      "exp": 1625232000
    }
    
  3. Signature:通过密钥对Header和Payload进行加密生成的签名,确保数据未被篡改。

JWT的优势

  • 无状态:服务端无需存储会话信息,减轻服务器压力。
  • 跨语言支持:几乎所有编程语言都有JWT实现库。
  • 易于扩展:Payload可灵活添加自定义字段。

2. 环境准备

步骤1:创建Spring Boot项目
使用 Spring Initializr 生成项目,勾选以下依赖:

  • Spring Web
  • Spring Security

步骤2:添加JJWT依赖(Maven)

<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>

步骤3:配置文件

# application.properties
# JWT配置
jwt.secret=MySuperSecretKey123!@#  # 密钥(需强密码)
jwt.expiration=86400000            # 有效期24小时(单位:毫秒)

3. 核心实现

3.1 JWT工具类

@Component
public class JwtUtils {
    @Value("${jwt.secret}")
    private String secretKey;
    
    @Value("${jwt.expiration}")
    private long expiration;

    // 生成令牌
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    // 验证令牌
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            return false;
        }
    }

    // 解析用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }
}

3.2 Spring Security配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private final JwtUtils jwtUtils;

    public SecurityConfig(JwtUtils jwtUtils) {
        this.jwtUtils = jwtUtils;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
            .and()
            .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    // JWT验证过滤器
    private Filter jwtFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(
                HttpServletRequest request,
                HttpServletResponse response,
                FilterChain filterChain
            ) throws ServletException, IOException {
                String header = request.getHeader("Authorization");
                
                if (header != null && header.startsWith("Bearer ")) {
                    String token = header.substring(7);
                    if (jwtUtils.validateToken(token)) {
                        String username = jwtUtils.getUsernameFromToken(token);
                        Authentication auth = new UsernamePasswordAuthenticationToken(
                            username, null, Collections.emptyList()
                        );
                        SecurityContextHolder.getContext().setAuthentication(auth);
                    }
                }
                filterChain.doFilter(request, response);
            }
        };
    }
}

3.3 登录接口

@RestController
@RequestMapping("/api")
public class AuthController {
    @Autowired
    private JwtUtils jwtUtils;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // 实际项目中应查询数据库验证用户
        if ("admin".equals(request.getUsername()) 
            && "admin123".equals(request.getPassword())) {
            
            String token = jwtUtils.generateToken(request.getUsername());
            return ResponseEntity.ok(new JwtResponse(token));
        }
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
}

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

@Data
class JwtResponse {
    private String token;

    public JwtResponse(String token) {
        this.token = token;
    }
}

4. 高级配置

4.1 自定义异常处理

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(value = {
        ExpiredJwtException.class,
        MalformedJwtException.class,
        SignatureException.class
    })
    public ResponseEntity<?> handleJwtException(Exception ex) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
            .body(Map.of("error", ex.getMessage()));
    }
}

4.2 基于角色的权限控制

@GetMapping("/admin/dashboard")
@PreAuthorize("hasRole('ADMIN')")
public String adminDashboard() {
    return "Admin Access Granted";
}

4.3 跨域配置

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

5. 常见问题与解决方案

问题1:令牌解析失败

  • 可能原因
    • 密钥不匹配
    • 令牌过期
    • 令牌被篡改
  • 排查步骤
    1. 检查jwt.secret配置是否一致
    2. 使用 jwt.io 调试令牌

问题2:令牌刷新机制

  • 实现方案
    1. 客户端在令牌过期前调用/api/refresh接口
    2. 服务端验证旧令牌有效性后生成新令牌

问题3:Spring Security兼容性

  • 典型错误Cannot convert access token to JSON
  • 解决方案:确保依赖版本一致,检查jjwt-impl是否包含在运行时依赖中

6. 总结与扩展

应用场景

  • 微服务间的安全通信
  • 移动应用用户认证
  • 前后端分离架构的权限管理

扩展学习

  • JWT官方文档
  • Spring Security官方指南
  • 实战案例:GitHub电商项目集成JWT

流程图:JWT认证流程

Client Server Database POST /api/login (username/password) 验证用户凭证 返回用户数据 生成JWT 返回JWT令牌 GET /protected (Header: Bearer <token>) 验证JWT签名和有效期 返回受保护资源 Client Server Database

避坑指南

  1. 密钥安全:切勿硬编码密钥,应通过环境变量注入
  2. 令牌存储:客户端使用HttpOnly Cookie或SecureLocalStorage
  3. 时间同步:确保服务器间时钟同步,避免令牌有效期计算错误

通过本指南,您已掌握在Spring Boot中集成JWT的核心技术。立即动手实践,为您的API穿上安全盔甲! 🔐

相关文章:

  • Linux上离线安装PyTorch教程:No module named ‘_bz2:No module named ‘_lzma‘
  • 单元测试mock
  • 蓝桥杯备考:特殊01背包问题——》集合subset
  • 两款软件助力图片视频去水印及图像编辑
  • PHP转GO Go语言环境搭建(Day1) 常见问题及解决方案指南
  • Node.js系列(3)--集群部署指南
  • K8S-etcd服务无法启动问题排查
  • Android audio(8)-native音频服务的启动与协作(audiopolicyservice和audioflinger)
  • 网络华为HCIA+HCIP VLAN间通信
  • ubuntu下TFTP服务器搭建
  • [GHCTF 2025]Goph3rrr [127.0.0.1绕过][env命令查找flag]
  • 如何让焦虑为城市供能 | 杂谈
  • windows上LISTENER监听器中显示“监听程序不支持服务”
  • hackmyvm-Smol
  • C++ 语法之函数和函数指针
  • 百度OCR调用记录
  • 荣耀手机怎么录制屏幕?屏幕录制后为视频加水印更有“安全感”
  • 复习JVM
  • STM32之快乐的Event Recorder功能
  • Docker 部署RabbitMQ
  • 5月12日至13日北京禁飞“低慢小”航空器
  • 美联储如期按兵不动,强调“失业率和通胀上升的风险均已上升”(声明全文)
  • 保利发展前4个月销售额约876亿元,单月斥资128亿元获4个项目
  • 上海:5月8日起5年以上首套个人住房公积金贷款利率下调至2.6%
  • 个人住房公积金贷款利率下调,100万元30年期贷款总利息将减少近5万元
  • 中国电信财务部总经理周响华调任华润集团总会计师