添加JWT依赖
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.7.0version>
dependency>
添加工具类,包含
package com.szx.exam.util;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import java.util.Date;
/**
* @author songzx
* @create 2023-06-25 13:55
*/
public class JwtHelper {
// 随机字符串混淆Token
private static final String tokenSignKey = "uNvs^pML-E";
/**
* 生成Token,并保存userID和userName信息
* @param userId
* @param userName
* @return
*/
public static String createToken(Long userId, String userName) {
long tokenExpiration = 1000L * 60 * 60 * 24 * 30; // 过期时间,设置30天
String token = Jwts.builder()
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.claim("userId", userId)
.claim("userName", userName)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
/**
* 从token中获取userID
* @param token
* @return
*/
public static Long getUserId(String token) {
if (StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer) claims.get("userId");
return userId.longValue();
}
/**
* 从token中获取用户姓名
* @param token
* @return
*/
public static String getUserName(String token) {
if (StringUtils.isEmpty(token)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
return (String) claims.get("userName");
}
/**
* 查看Token是否过期
* @param token
* @return false 未过期,true 已过期
*/
public static Boolean tokenExpired(String token) {
try {
Claims claims = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody();
Date expiration = claims.getExpiration();
Date currentDate = new Date();
return expiration.before(currentDate);
} catch (ExpiredJwtException ex) {
return true;
} catch (Exception ex) {
return true;
}
}
}
添加 handle
package com.szx.exam.handle;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.szx.exam.exception.SsyxException;
import com.szx.exam.util.JwtHelper;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author songzx
* @create 2022-10-12 9:48
*/
public class TokenHandle implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(!(handler instanceof HandlerMethod)){
return true;
}
// 从请求头中获取token
String token = request.getHeader("X-Token");
// 校验token
if(StringUtils.isEmpty(token) || JwtHelper.tokenExpired(token)){
throw new SsyxException("登录过期,请重新登录",401);
}
return true;
}
}
添加 TokenConfig,设置需要过滤的接口信息,不在过滤中的接口都将配拦截校验是否存在token
@Configuration
public class TokenConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenHandle())
.addPathPatterns("/**")
.excludePathPatterns(
"/stu/register",
"/stu/login",
"/swagger-ui.html",
"/webjars/**",
"/swagger-resources/**"
);
}
}
测试
有如下接口:
http://localhost:9000/exam/saveExam
在不加Token的情况下,或者Token已经过期的情况下, 发起请求,返回401
现在生成一个Token,
将得到的Token加在请求头中,再次发起请求
可以看到本次请求正常返回200
补充:
如果项目中使用了Swagger,则在swagger中请求接口时也会被拦截器拦截,出现401提示,我们可以在swagger的配置类中添加请求头
package com.szx.exam.config;
import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* Swagger2配置信息
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket webApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("webApi")
.apiInfo(webApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.szx.exam"))
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build();
}
public ApiInfo webApiInfo(){
return new ApiInfoBuilder()
.title("Api文档")
.description("文本档描述了定义的接口")
.version("1.0")
.contact(new Contact("szx", "https://blog.csdn.net/SongZhengxing_?spm=1010.2135.3001.5343","[email protected]"))
.build();
}
@Bean
public Docket docket() {
// 设置请求头
List<Parameter> parameters = new ArrayList<>();
parameters.add(new ParameterBuilder()
.name("X-Token") // 字段名
.description("添加请求头信息,校验是否登录") // 描述
.modelRef(new ModelRef("string")) // 数据类型
.parameterType("header") // 参数类型
.defaultValue("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAKtWSq0oULIyNLM0MDEyMTc01FEqLU4t8kvMTVWyUno2t-_p7NlGSrUA1d5cpicAAAA.i6QU9I5h0hKHupiAK3JEfribAbQKRIXAYM4qUWR0PFGJenJUXz50aMMuxenUUBCsPI9-79m9JjB3SQLKc8xEOw") // 默认值:可自己设置
.hidden(true) // 是否隐藏
.required(false) // 是否必须
.build());
// 创建一个 swagger 的 bean 实例
return new Docket(DocumentationType.SWAGGER_2)
// 配置接口信息
.select() // 设置扫描接口
// 配置如何扫描接口
.apis(RequestHandlerSelectors
.basePackage("com.szx.exam") // 扫描指定包下的接口,最为常用
)
.paths(PathSelectors
.any() // 满足条件的路径,该断言总为true
)
.build()
.globalOperationParameters(parameters);
}
}