Spring Boot 集成 JWT 简单实现

尽意
2024-12-04 / 0 评论 / 11 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年12月04日,已超过50天没有更新,若内容或图片失效,请留言反馈。

一、JWT 简介

JSON Web Token (JWT) 是一种用于客户端与服务端之间安全传递信息的轻量级协议。它通常用于:

  • 用户认证:登录成功后颁发 Token,后续请求中携带该 Token。
  • 授权管理:通过 Token 判断用户是否有权限。

JWT 的组成部分:

  1. Header:描述令牌的类型和算法。
  2. Payload:包含用户信息及其他声明。
  3. Signature:由 Header、Payload 和密钥生成,用于验证 Token 的完整性。

二、项目依赖

在项目的 pom.xml 中引入以下依赖:

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- JWT -->
    <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>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
    </dependency>
</dependencies>

三、创建 JWT 配置类

1. 定义 JwtProperties

通过 @ConfigurationProperties 注解加载外部配置:

package com.example.jwt.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
    private String secret;         // 秘钥
    private long expiration;       // 过期时间(秒)

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public long getExpiration() {
        return expiration;
    }

    public void setExpiration(long expiration) {
        this.expiration = expiration;
    }
}
2. 激活配置类
package com.example.jwt.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtConfig {
}
3. 在 application.yml 中添加配置
jwt:
  secret: my-secret-key
  expiration: 3600  # 单位:秒

四、编写 JWT 工具类

创建 JwtUtils,用于生成和验证 Token:

package com.example.jwt.utils;

import com.example.jwt.config.JwtProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtils {

    private final JwtProperties jwtProperties;

    public JwtUtils(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    // 生成 JWT
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration() * 1000))
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecret())
                .compact();
    }

    // 验证 JWT
    public Claims validateToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(jwtProperties.getSecret())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            return null; // Token 无效
        }
    }
}

五、实现登录接口

1. 控制器 AuthController
package com.example.jwt.controller;

import com.example.jwt.utils.JwtUtils;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

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

    private final JwtUtils jwtUtils;

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

    // 登录接口
    @PostMapping("/login")
    public Map<String, String> login(@RequestBody Map<String, String> request) {
        String username = request.get("username");

        // 简单验证用户(实际中应使用数据库或其他认证机制)
        if ("admin".equals(username)) {
            String token = jwtUtils.generateToken(username);

            Map<String, String> response = new HashMap<>();
            response.put("token", token);
            return response;
        } else {
            throw new RuntimeException("Invalid username or password");
        }
    }
}
2. 测试接口

使用 curl 测试登录接口:

curl -X POST http://localhost:8080/auth/login \
    -H "Content-Type: application/json" \
    -d '{"username": "admin"}'

返回结果:

{
    "token": "eyJhbGciOiJIUzI1NiIsInR..."
}

六、实现简单的认证拦截器

为了验证 Token 是否有效,我们可以使用拦截器来处理请求。

1. 创建拦截器 JwtInterceptor
package com.example.jwt.interceptor;

import com.example.jwt.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JwtInterceptor implements HandlerInterceptor {

    private final JwtUtils jwtUtils;

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

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");

        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7); // 去掉 "Bearer "
            Claims claims = jwtUtils.validateToken(token);

            if (claims != null) {
                // 将用户名放入请求属性中
                request.setAttribute("username", claims.getSubject());
                return true;
            }
        }

        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.getWriter().write("Unauthorized");
        return false;
    }
}
2. 注册拦截器
package com.example.jwt.config;

import com.example.jwt.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final JwtInterceptor jwtInterceptor;

    public WebConfig(JwtInterceptor jwtInterceptor) {
        this.jwtInterceptor = jwtInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/auth/login"); // 登录接口不拦截
    }
}

七、验证拦截功能

添加受保护接口
package com.example.jwt.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/user/info")
    public String getUserInfo(@RequestAttribute("username") String username) {
        return "Hello, " + username;
    }
}
测试接口
  1. 访问受保护接口(无 Token)
curl http://localhost:8080/user/info

返回结果:

Unauthorized
  1. 携带 Token 访问
curl http://localhost:8080/user/info \
    -H "Authorization: Bearer <your-jwt-token>"

返回结果:

Hello, admin
1

评论 (0)

取消