SpringBoot集成Token
创始人
2025-01-28 07:01:10
0

简介

在项目开发中,Token 是常见且重要的一个功能,目前对于 Token 设计有很多成熟的方案;比如使用 Redis 存储管理 Token,不过这种方式需要而额外集成 Redis 服务,虽然 Redis 查询效率很高,但是对于普通项目来说,还是增加了开发难度;本章将介绍一个简单易用的 Token 插件:jjwt,该插件直接与 SpringBoot 集成即可,其原理是:在服务端加密生成一个三段式加密字符串,前端每次请求都要将该 Token 传递给服务端(建议放在 Header 中),后台通过解析该 Token 字符串,判断其是否合法,超时等

基本原理

从这个架构图中可以看出 JWT 主体分为 3 个部分:user,application server,authentication server;
非常常见的一个架构,首先用户需要 通过登录等手段向 authentication server 发送一个认证请求,authentication会返回给用户一个 JWT (这个JWT 的具体内容格式是啥后面会说,先理解成一个简单的字符串好了) 此后用户向application server发送的所有请求都要捎带上这个 JWT,然后application server 会验证这个 JWT 的合法性,验证通过则说明用户请求时来自合法守信的客户端

JWT 结构

这个 JWT 的格式,就是一个由三部分组成的字符串:header.payload.signatue;其中 header 主要包含了加密算法等信息;payload 则主要包含后端服务器放入的自定义信息,如:登录用户的信息;signature 就是使用算法生成的能够实现身份认证的字符串

实现步骤

1. 在项目的 pom.xml 配置文件中添加如下依赖

io.jsonwebtokenjjwt0.7.0

 2. 添加一个公共类 Token,提供生成 Token,校验等基本方法

/*** @ClassName Token* @Author Andy* @Date 2020/7/14 14:32* @Description 用于生成, 解析 token 的工具类**/
public class Token {// 密钥private static SecretKeySpec key = "";// 对密钥加密static {key = new SecretKeySpec(Constant.SECRET_KEY.getBytes(), SignatureAlgorithm.HS512.getJcaName());}// 生成 tokenpublic static String createToken(String subject, Map < String, Object > claims, Date expireDate) {JwtBuilder builder = Jwts.builder().setClaims(claims) // payload 私有申明,存放一些个人信息,必须放在第一个.setIssuer(Constant.AUTHOR) // token 的签发人.setIssuedAt(new Date()) // token 的签发时间.setSubject(subject) // token 的所有人, 一般放用户的 id 之类的.setExpiration(expireDate) // 过期时间.signWith(SignatureAlgorithm.HS512, key); // token 的签名算法return builder.compact();}// 生成 tokenpublic static String createToken(String subject, Date expireDate) {return createToken(subject, new HashMap < > (), expireDate);}// 解析 tokenpublic static Claims parseToken(String token) {try {return Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody();} catch (Exception e) {throw new CommonException(Exceptions.TOKEN_PARSE_ERROR);}}// 将 token 标记为过期public static void markTokenExpired(String token) {Date expireDate = CommonUtil.currentTimeAddSeconds(1);Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().setExpiration(expireDate);}// 判断 token 是否过期public static boolean checkTokenExpired(String token) {Claims claims = parseToken(token);return !CommonUtil.checkExpired(claims.getExpiration());}// 获取 token 的 subject 信息, 登录成功保存的时用户的主键public static String getSubject(HttpServletRequest request) {return parseToken(getToken(request)).getSubject();}// 根据 token 获取 subject 信息public static String getSubject(String token) {return parseToken(token).getSubject();}// 根据 HttpServletRequest 获取 tokenpublic static String getToken(HttpServletRequest request) {return request.getHeader(Constant.HEADER_TOKEN);}// 获取 token 的签发人public static String getIssuer(String token) {return parseToken(token).getIssuer();}
}

这里使用到其它关联类的方法或属性如下

Constant.class

// token 签发者
public static final String AUTHOR = "duzimei";
// 获取 header 中 token 标识
public static final String HEADER_TOKEN = "token";
// token 密钥加密字符串(取自 《肖申克的救赎》经典台词)
public static final String SECRET_KEY = "Fear can hold you prisoner.Hope can set you free";
// 设置 token 的过期时间为 1 个小时(单位秒)
public static final Integer EXPIRE_DATE = 3600;

CommonUtil.class

// 在当前时间基础上加 seconds 秒
public static Date currentTimeAddSeconds(int seconds) {Calendar now = Calendar.getInstance();now.add(Calendar.SECOND, seconds);return now.getTime();
}// 判断于当前日期是否过期
public static boolean checkExpired(Date expiration) {return expiration.after(new Date());
}

异常定义请忽略,根据自己项目而实现

3. 定义一个 UserController.class,添加登录方法,当用户登录成功后,返回一个 Token

@RestController
@RequestMapping("/user")
public class User {    @Autowiredprivate UserService userService;@PostMapping("/login")public Map login(@RequestBody JSONObject params) {Map result = new HashMap<>();String userAccount = params.getString("account");String password = params.getString("password");User tempUser = userService.getUserByAccount(userAccount);if(null == tempUser) {result.put(-1, "用户不存在");} else if(!password.equals(tempUser.getPassword())) {result.put("-2", "密码不正确");} else {// 用户登录成功, 添加 token 返回给客户端// 1. 设置 token 过期时间Date expireDate = CommonUtil.currentTimeAddSeconds(Contant.EXPIRE_DATE);// 2. 这里我们把用户的 id 信息放到 subject 中String token = Token.createToken(tempUser.getUserId(), expireDate); result.put(0, token);}return result;}
}

4. 前端当用户登录成功后,向后台发送其它请求的时候,将 Token 放到 Header 中一起传送给后台

$.ajax({headers: {"token": 从后台获取到的token},url: "http://xxx:8080/user/info",method: "GET",data: null,dataType: "json",contentType: "application/json",success: function(data) {console.log(data);},error: function(x, s, e) {console.log("异常信息: " + x.responseText);}
})

5.解析Token

在后台对应方法中,就可以通过解析 Token 获取用户 id,判断请求是否合法;像下面这种请求,前端根本不用传递用户的 id 到后台,后台通过解析 Token,获取 Token 的 subject 属性就能拿到用户 id,在一定程度上提高了安全性

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/info")public Map getUserInfo(HttpServletRequest request) {Map result = new HashMap<>();String token = Token.getToken(request);// 对 token 进行校验if(StringUtils.isEmpty(token)) { // 如果没有获取到 Tokenresult.put(-1, "没有获取到 Token, 请求被阻止"); } else if(!Constant.AUTHOR.equals(Token.getIssuer(token))) { // 如果 Token 的签发人不正确result.put(-2, "非法的 Token, 请求被阻止");} else if(Token.checkTokenExpired(token)) { // 如果 Token 已过期result.put(-3, "过期的 Token, 请求被阻止");} else {String userId = Token.getSubject(token);User user = userService.getUserById(userId);result.put(0, user);}return result;}
}

这里只是介绍的关于 JWT 实现 Token 的简单用法,在实际开发过程中,建议使用 Spring 的 AOP 技术实现对 Token 的校验;我们这里只是实现了一个最简单的 Token,这 Token 中还可以放入其它信息,由于是单向加密的,所以数据传输非常安全;更进一步的实现,应根据实际开发具体实现

相关内容

热门资讯

孙杨参赛4项 涵盖短中长距离 转自:天津日报  本报讯(记者 李蓓)昨天,即将在本周末开赛的2025全国游泳冠军赛报项名单出炉。3...
齐鲁品牌文化数智平台共建项目启...   张志恒 王鑫 济南报道  5月13日举行的“新质山东 品筑未来”2025年山东最具影响力品牌暨《...
云湖兰山服务综合体运营 转自:贵州日报 本报讯 5月13日,观山湖区环百花湖旅居推介会暨云湖兰山启幕式在观山湖区朱昌...
守好健康证的“健康关” 人都没到场,证就办好了?近日,有记者走访发现,一些医疗机构的健康证体检项目“缺斤少两”,规章制度形同...
“我的论文不是AI写的”(图) 转自:天津日报  有网友反映,自己原创的论文经过系统检测之后,竟被指出AI生成内容比例过高。据媒体报...
应用机器人与3D打印术行颌骨截...   刘通 通讯员 崔子昂 李鲲济南报道  近日,山东大学齐鲁医院口腔科颌面外科陈安威、王涛、韩亦冰及...
美股周二收盘点评:通货膨胀率下... 来源:宏观对冲陈凯丰Kevin截至4月份的12个月里,消费者物价指数上涨了2.3%,而截至3月份的1...
纽约汇市:美元下跌 美国通胀率...   彭博一项衡量美元强弱的指数兑所有G10货币均走低,此前发布的美国4月份消费者价格涨幅低于预期。投...
要为丘陵山区等研制急需急用的装... □四川日报全媒体记者 阚莹莹  近日,四川省农业农村厅发布2025年“天府良机”薄弱环节关键技术装备...
亮出低空经济发展的未来 □四川日报全媒体记者 高杲 李欣忆7款“四川造”低空产品小鹰-700飞机  ●是目前唯一国产的上单翼...
聆听“沧海龙吟” 共赏民歌之美... 转自:天津日报  本报讯(记者 张帆 摄影 曹彤)昨天,由首都图书馆发起并联合天津图书馆、河北省图书...
彩桥即将合龙   5月12日,宜宾市屏山县岷江二桥工地施工繁忙,建设者对桥梁最后几榀钢拱架进行吊装作业。岷江二桥是...
第二十届西博会5月25日开幕 ●拟邀请匈牙利、老挝为主宾国,浙江、青海为主宾省●拟特邀阿联酋担任大会合作伙伴●据初步统计,西部各地...
各美其美 美美与共 □四川日报全媒体记者 吴晓铃  在古老的历史长河中,留下帕特农神庙、雅典卫城等文明遗产的希腊是西方文...
“公证日记” 转自:天津日报  智慧公证  曾经,办理继承公证需要集齐一沓证明材料,群众要跑好几个部门;如今,滨海...
武清区扎实推进兴业富农 绘就乡... 转自:天津日报  初夏时节,灿烂的阳光夹杂着阵阵微风,深情抚慰着大运河畔的武清区南蔡村镇丁家瞿阝村。...
如皋农商银行:双向奔赴谋发展 ... 4月27日,如皋农商银行举办“跨境人民币赋能外贸企业发展”专题培训会。会议邀请跨境人民币业务优质企业...
泰州农商银行联合海陵区供销总社... 日前,泰州农商银行与泰州市海陵区供销总社在城中街道联合举办“政银携手进社区 惠民助农促消费”系列活动...
将虾苗卖到“小龙虾之乡” □四川日报全媒体记者 陈丽霏  5月,鲜活肥美的小龙虾大量上市,中江县黄鹿镇也迎来了一年中最繁忙的时...
建圈强链 四川农业明确产业“路... 5月11日,四川省大邑县现代农业(粮食产业)园区,工人在试验田中开展小麦新品的测产工作。 李旭 摄(...