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

JWT应用样例

一、简述

JWT由三部分组成:Header、Payload(负载)、Signature(签名)。

  1. Header部分记录使用的加密算法和Token类型。
  2. Payload部分可以放一些用户信息。如账号、姓名等非敏感信息。
  3. 签名是将Header部分和Payload部分做加密签名,使用Header中指定的加密算法。

二、JWT工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

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

/**
 * JWT生成、解析、校验工具类
 *
 * @author neo
 * @since 2025-02-27
 */
public class JWTUtil {
    // 加密密钥。在程序启动后初始化
    private String secret = "";

    private JWTUtil(){

    }

    public static JWTUtil instance(){
        return JWTUtilBuilder.instance;
    }

    /**
     * 设置加密密钥
     *
     * @param secret 密钥
     * @author neo
     * @since 2025/3/25
     */
    public void setSecret(String secret){
        this.secret = secret;
    }

    /**
     * 创建Token
     *
     * @param parameterMap 参数表
     * @return 生成的Token
     * @author neo
     * @since 2025/3/25
     */
    public String createToken(Map<String, String> parameterMap){
        Calendar calendar = Calendar.getInstance();

        // 设置三天后过期
        calendar.add(Calendar.DATE, 3);

        JWTCreator.Builder builder = JWT.create();

        // 设置header
        Map<String, Object> headerMap = new HashMap<>();
        headerMap.put("alg", "HS256");
        headerMap.put("typ", "JWT");
        builder.withHeader(headerMap);

        // 填充负载
        for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
            builder.withClaim(entry.getKey(), entry.getValue());
        }

        // 创建token
        String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(secret));

        return token;
    }

    /**
     * 校验Token是否有效
     *
     * @param token 令牌
     * @return 校验结果
     */
    public DecodedJWT verifyJWT(String token){
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
        return verifier.verify(token);
    }

    /**
     * 解析Token获取负载中的参数
     *
     * @param token 令牌
     * @return 参数
     */
    public Map<String, String> decodeToken(String token){
        DecodedJWT decodedJWT = verifyJWT(token);

        // 提取参数
        Map<String, String> parameterMap = new HashMap<>();
        Map<String, Claim> claimMap = decodedJWT.getClaims();
        claimMap.forEach((k, v)->{
            parameterMap.put(k, v.asString());
        });

        return parameterMap;
    }

    private static final class JWTUtilBuilder {
        private static JWTUtil instance = new JWTUtil();
    }
}

JWTUtil 工具类实现了JWT的生成,校验、参数解析功能。使用的密钥secret可以在程序启动时从配置文件中读取,然后通过setSecret方法保存到内存。

三、提供登录接口

@RestController
@RequestMapping("/v1/login")
@Api(tags = "用户登录服务")
public class LoginController extends BaseController {
    private static final Logger LOGGER = LogManager.getLogger(LoginController.class);

    @Resource
    private LoginService loginService;

    /**
     * 用户登录
     *
     * @param loginUser 用户信息
     * @return 生成的token
     */
    @PostMapping("/login-in")
    @ApiOperation(value = "用户登录")
    public ResultModel<String> login(@RequestBody UserLogin loginUser){
        return loginService.login(loginUser);
    }
 }

前端在用户登录时调用后台的登录接口,传入用户名,密码等信息(封装在UserLogin中)。后端服务调用JWTUtil的接口生成Token信息并返回给前端。前端可以将Token信息保存在本地存储或者session中。

用户登录后,前端后续调用其它业务接口时将Token放到http的请求头中传递到后台,后台拦截器提取Token数据做权限认证。

四、通过拦截器拦截http请求并认证

import com.elon.base.util.JWTUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 拦截rest接口请求,做用户合法性认证.
 *
 * @author neo
 * @since 2025-02-28
 */
public class JWTInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LogManager.getLogger(JWTInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 提起请求头中的jwt信息
        String jwtToken = request.getHeader("jwt-token");

        try {
            JWTUtil.instance().verifyJWT(jwtToken);
            response.setHeader("Access-Control-Allow-Origin", "*");
            return true;
        } catch (Exception e){
            LOGGER.error("Verify token fail.", e);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println("Token is valid");

            // 设置为没有权限,前端拿到响应码需跳转到登录页面
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
    }
}

认证不通过的请求返回401。

五、添加拦截器及拦截规则

import com.neo.lesson.manage.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 注册JWT Token拦截器
 *
 * @author neo
 * @since 2025-02-28
 */
@Configuration
public class JWTConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/v1/lesson/**", "/v1/student/**")
                .excludePathPatterns("/v1/login/**");
    }
}

仅有用户登录的接口不拦截,其它业务接口均需要拦截。

六、扩展考虑

  1. JWT在生成时设置了过期时间,但在实际项目中可能会根据业务需要设置不同的过期时间。例如:如果用户登录后30分钟没有做任何操作,那么系统将自动登出。这种情况可以考虑将token数据放到redis中存储,利用redis的失效机制。

  2. 系统不仅需要认证用户身份合法性,还需要根据用户的不同角色对接口做鉴权。此时需要在拦截http请求后获取用户的权限信息,结合当前请求的接口地址做校验。

相关文章:

  • 【Android】Activity 生命周期(详细介绍)
  • Mac 常用命令
  • 《Git江湖录·分支篇》
  • 二叉树进阶
  • 【leetcode刷题日记】lc.560-和为 K 的子数组
  • 深入解析 JVM 内存区域及核心概念
  • 掌握Linux项目自动化构建:从零入门make与Makefile
  • 基于LLM的Agent框架全面比较分析:MGX(MetaGPT X)、AutoGen、OpenHands与秒哒(MiaoDa)
  • [C++面试] span<char>和string_view的差别
  • MySQL数据库入门
  • 【JavaScript】金丹期功法
  • LLM动态Shape实现原理与核心技术
  • 【银河麒麟系统常识】命令:dotnet run(运行)
  • 远程医疗的现状如何?
  • C++ 之 SOCKET 通信详解
  • “自动驾驶背后的数学” 专栏导读
  • 推陈换新系列————java8新特性(编程语言的文艺复兴)
  • 【现代深度学习技术】现代卷积神经网络04:含并行连接的网络(GoogLeNet)
  • 每日总结3.26
  • 算法题(108):
  • 中国军网:带你揭开3所新调整组建军队院校的神秘面纱
  • 中日东三省问题的源起——《1905年东三省事宜谈判笔记》解题
  • 盛和资源海外找稀土矿提速:拟超7亿元收购匹克,加快推动坦桑尼亚项目
  • “中国神湖”加快放大资源规模!3亿美元换海外年产380万吨钾盐项目
  • 山东市监局回应“盒马一批次‘无抗’鸡蛋抽检不合格后复检合格”:系生产商自行送检
  • 体坛联播|C罗儿子完成国家队首秀,德约结束与穆雷合作