SSM框架中(springboot springmvc mybatis)中,集成JWT的超级详细的步骤,且代码注释的很好,整理文档不易。
我是一个15年的DBA,但也会写不少软件。
第一步:添加JWT及JSON的依赖
如下,在pom文件中在呢个价jwt及json的依赖。
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<!-- 增加json模块,用于支持返回的数据是json格式-->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
第二步:编写JwtUtilsYcust基础类
该类用于生成token、验证token、获取token中信息,注解写的很详细。
package com.example.ycust.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @description : JWT工具类,用于生成、验证和解析JWT(JSON Web Token)
*/
public class JwtUtilsYcust {
/**
* 签名密钥:非常关键!只有服务器知道,不应泄露给其他人。
* 知道签名密钥和加密算法后,理论上可以解析出JWT的内容。
*/
private static final String SIGN_KEY = "ycust_sign";
/**
* 生成JWT Token
*
* @param map 自定义的载荷信息,通常包含用户身份信息等
* @return 生成的JWT Token字符串
*/
public static String getJwtToken(Map<String, Object> map){
// 定义JWT头信息,指定签名算法和类型
Map<String, Object> header = new HashMap<>();
header.put("alg", "HS256"); // 签名算法为HMAC SHA-256
header.put("typ", "JWT"); // 类型声明为JWT
// 定义颁发时间
Date issuedAt = new Date();
// 定义过期时间,这里设置的是当前时间的7200秒(2小时)后
Calendar expireCalendar = Calendar.getInstance();
expireCalendar.add(Calendar.SECOND, 7200);
Date expiresAt = expireCalendar.getTime();
// 使用Auth0 JWT库生成JWT Token
return JWT.create()
.withHeader(header) // 设置头信息
.withIssuedAt(issuedAt) // 设置颁发时间
.withExpiresAt(expiresAt) // 设置过期时间
.withClaim("loginInfo", map) // 添加自定义载荷信息
.sign(Algorithm.HMAC256(SIGN_KEY)); // 使用HMAC256算法和签名密钥进行签名
}
/**
* 验证JWT Token是否有效
*
* @param token 待验证的JWT Token字符串
* @return 如果Token有效,返回true;否则返回false
*/
public static boolean verifyToken(String token){
// 创建一个JWTVerifier对象,指定签名算法和签名密钥
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SIGN_KEY)).build();
try {
// 调用校验功能,如果Token无效会抛出JWTVerificationException异常
verifier.verify(token);
return true; // Token有效
} catch (JWTVerificationException e) {
// 捕获异常,表示Token无效
System.out.println("Token无效");
return false; // Token无效
}
}
/**
* 从JWT Token中获取自定义存储信息,例如存储的用户名等信息
*
* @param token JWT Token字符串
* @return 载荷内容,以Map形式返回
*/
public static Map<String, Object> getTokenClaim(String token){
// 创建一个JWTVerifier对象,指定签名算法和签名密钥
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SIGN_KEY)).build();
// 调用校验功能,并获取DecodedJWT对象,该对象包含了Token的详细信息
DecodedJWT decodedJWT = verifier.verify(token);
// 从DecodedJWT对象中获取自定义的载荷信息
Claim claim = decodedJWT.getClaim("loginInfo");
// 将载荷信息转换为Map并返回
return claim.asMap();
}
}
第三步:生成token的生成、验证、读取的服务
我们将第二步JwtUtilsYcust类中的功能注册成服务,便于服务集中管理及调用。
服务接口代码:
package com.example.ycust.service;
import java.util.Map;
public interface IGetToken {
// 生成tocken
public String get_token();
// 验证token是否合法
public boolean verifyToken(String token);
// 下面这个函数校验Token里带的用户自定义信息
public Map<String, Object> getTokenChaim(String token);
}
服务接口实现类:
package com.example.ycust.service.impl;
import com.example.ycust.jwt.JwtUtilsYcust;
import com.example.ycust.service.IGetToken;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class GetTokenImpl implements IGetToken {
public String get_token(){
Map<String, Object> map = new HashMap<>();
map.put("username","yugang");
map.put("Major","银川科技学院软件工程专业");
return JwtUtilsYcust.getJwtToken(map);
}
public boolean verifyToken(String token){
if(JwtUtilsYcust.verifyToken(token))
return true;
else
return false;
}
public Map<String, Object> getTokenChaim(String token){
//获取载荷信息
Map<String, Object> tokenChaim = JwtUtilsYcust.getTokenClaim(token);
return tokenChaim;
}
}
注:map.put计划放置的信息由功能决定,一般方式用户名、用户角色等信息。
第四步:创建TokenInterceptor拦截器,用于定义拦截逻辑
注意:这个类只是拦截逻辑,至于什么拦截,什么不拦截,第五步中的类实现,直接给代码,,注释很详细。
package com.example.ycust.filter;
import com.example.ycust.service.IGetToken; // 导入自定义的Token服务接口
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.json.JSONObject; // 导入JSON处理库
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; // 导入日志处理库
import org.springframework.beans.factory.annotation.Autowired; // 导入Spring的依赖注入注解
import org.springframework.stereotype.Component; // 导入Spring的组件注解
import org.springframework.web.servlet.HandlerInterceptor; // 导入Spring的拦截器接口
import java.io.PrintWriter;
/**
* @author 银川科技学院-于刚
* @Date 2024-11-15
* 该类用作拦截器,用于对请求中的Token进行验证。
*/
@Component // 声明为Spring组件,让Spring容器管理
public class TokenInterceptor implements HandlerInterceptor {
// 注入Token服务接口的实现类
@Autowired
private IGetToken getTokenService;
// 创建日志记录器
private final Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
/**
* 在请求处理之前调用此方法。
*
* @param request 当前请求对象
* @param response 当前响应对象
* @param handler 被请求的处理器对象(可能是Controller中的方法)
* @return 如果返回true,则继续处理请求;如果返回false,则中断请求处理
* @throws Exception 如果发生异常,则抛出
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 设置响应的字符编码和内容类型
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
// 从请求头中获取名为"Authorization"的Token
String token = request.getHeader("Authorization");
// 如果Token存在,则验证其有效性
if (token != null) {
boolean isValid = getTokenService.verifyToken(token);
if (isValid) {
// Token有效,继续处理请求
return true;
}
}
// Token无效或不存在,构造并返回JSON格式的错误信息
try (PrintWriter out = response.getWriter()) { // 使用try-with-resources确保PrintWriter被正确关闭
JSONObject jsonResponse = new JSONObject();
jsonResponse.put("status", "false");
jsonResponse.put("message", "认证失败,token无效");
jsonResponse.put("code", "500"); // 注意:这里应该使用401或403状态码对应的错误信息,但这里保留了原代码的逻辑
out.append(jsonResponse.toString());
// 记录日志信息
logger.info("认证失败,未通过拦截器");
} catch (Exception e) {
// 捕获并处理异常,这里只是简单地打印堆栈跟踪,实际应用中可能需要更复杂的错误处理逻辑
e.printStackTrace();
// 返回false表示请求被拦截
return false;
}
// 如果没有通过Token验证,也返回false表示请求被拦截
return false;
}
}
第五步:设定拦截逻辑。即可利用第四步的拦截类,指定拦截什么不拦截什么
代码很简单,并且加了很好的注释,如下:
package com.example.ycust.filter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
/**
* @Date 2024-11-15
* TokenInterceptor 为拦截器,该类是拦截器的配置类,用于配置哪些路径不需要被拦截器拦截。
*/
@Component // 声明为Spring组件,让Spring容器管理
public class IntercepterConfig implements WebMvcConfigurer {
private final TokenInterceptor tokenInterceptor; // 声明TokenInterceptor类型的成员变量,用于注入拦截器实例
// 构造方法,通过构造方法注入TokenInterceptor实例
public IntercepterConfig(TokenInterceptor tokenInterceptor){
this.tokenInterceptor = tokenInterceptor;
}
/**
* 重写addInterceptors方法,用于注册拦截器并配置其拦截路径和排除路径。
*
* @param registry 拦截器注册表,用于添加拦截器及其配置。
*/
@Override
public void addInterceptors(InterceptorRegistry registry){
// 创建一个列表,用于存储不需要被拦截的路径
List<String> excludePath = new ArrayList<>();
// 将登录接口添加到排除路径列表中,表示该接口不需要进行token验证
excludePath.add("/get_token");
// 注册拦截器,并配置其拦截所有路径(/**),但排除上面指定的路径
registry.addInterceptor(tokenInterceptor) // 添加拦截器实例
.addPathPatterns("/**") // 配置拦截所有路径
.excludePathPatterns(excludePath); // 配置排除路径
// 注意:WebMvcConfigurer.super.addInterceptors(registry); 这行代码是不必要的,
// 因为我们已经重写了addInterceptors方法,并且在这个方法内部进行了拦截器的配置。
// 调用父类的addInterceptors方法会导致重复配置,因此应该删除这行代码。
}
// 注意:原代码中的 WebMvcConfigurer.super.addInterceptors(registry); 已被注释说明为不必要,实际使用时应该删除。
}
第六步:控制器调用测试
控制器的方法:get_token肯定是要结合用户名及密码来获取的,这知识进行演示。
/ token的生成及验证
@RequestMapping("/get_token")
@ResponseBody
public String get_token(){
// 先生成token
String token=gettoken.get_token();
logger.info("token是:"+token);
// 验证token
boolean check_token =gettoken.verifyToken(token);
logger.info("token验证结果是:"+check_token);
// 开始读取token存储的用户自定义数据
Map<String, Object> tokenChaim = gettoken.getTokenChaim(token);
logger.info("token中自带的用户名:"+tokenChaim.get("username").toString());
logger.info("token中专业名称:"+tokenChaim.get("Major").toString());
return token;
}
需要验证的接口:
@RequestMapping("/maoshan")
@ResponseBody
public List<MaoShan2> maoshan(){
return getmaoshan.maoshanlist(null);
}带token进行访问

如果没有token:

整个系统的工程目录:

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




