JWT简介
JWT的token由三段信息构成的,将这三段信息文本用.连接一起就构成了JWT字符串;
- Header 头部(包含了令牌的元数据,并且包含签名和或加密算法的类型)
- Payload 负载
- Signature 签名/签证
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJwaG9uZSI6IjE1NjgwNzkzNTM5IiwiZXhwIjoxNjE4OTEyNjI1fQ.
RrumX_QyRUBjm92dgHJHuVyiQqg861oDltU1kYjSiVQ
jwt优点
简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,数据量小,传输速度快
自包含(Self-contained):负载中包含了所有用户所需要的信息,避免多次查询数据库
.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言支持;
不需要在服务端保存会话信息,适用于分布式与微服务;
实现代码
- 引入pom.xml 依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
jwt工具类
jwt工具类中有三个方法,分别是生成数字签名用于用户首次登陆时发送jwt给客户端;其次是校验方法,用于拦截器拦截所有规则内的url,每个请求都必须带有服务器发送的jwt,经过验证后才放行请求;最后一个获得用户名的方法用于查询密钥,在验证jwt时作为参数传入;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/* *
* @Author chenlirun
*/
public class JwtUtil {
// Token过期时间30分钟
public static final long EXPIRE_TIME = 30 * 60 * 1000;
/* *
* @Author chenlirun
* <p> 校验token是否正确 </p>
*/
public static boolean verify(String token, String id,String role String secret) {
try {
// 设置加密算法
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("id", id)
.withClaim("role ", role )
.build();
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/* *
* @Author chenlirun
*/
public static String sign(Integer id, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带id信息
return JWT.create()
.withClaim("id", id)
.withExpiresAt(date)
.sign(algorithm);
}
/* *
* @Author lsc
* <p> 获得用户名 </p>
*/
public static String getIdByToken(HttpServletRequest request) {
String token = request.getHeader("token");
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("id")
.asString();
}
/**
* 解析token获取role
*/
public static String getRoleByToken(HttpServletRequest request) {
String token = request.getHeader("token");
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("role")
.asString();
}
}
用户访问限制
- 自定义注解
限制请求接口是否需要登录,接口以什么身份访问。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({
ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginPermission {
//登录认证
boolean login() default true;
//角色认证
String role();
}
- 自定义身份信息
public class RolePermission {
public static final String User_ADMIN="1" ;
public static final String USER_GROUP="1,2,3";
public static final String COMMON="1,2,3";
}
自定义拦截器
拦截请求的接口,认证token携带的信息是否正确。
- yml中添加秘钥
jwt:
secret: "abcd" # 用于生成token的密钥
expiration: 7200000 # token有效期
token: Authorization # http header里token 所在的字段名
自定义拦截器JwtInterceptor,实现HandlerInterceptor接口,每次请求到达之前都会验证token是否有效;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author lsc
* <p>token验证拦截器 </p>
*/
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Value("${jwt.secret}") //调用实现yml中的配置属性
private String secret;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从 http 请求头中取出 token
String token = request.getHeader("token");
// 如果不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod) handler;
Method method = handlerMethod.getMethod();
if(method.isAnnotationPresent(RolePermission.class)){
RolePermission annotation = method.getAnnotation(RolePermission.class);
if(!annotation.login()){
return true;
}
}
if (token != null){
int id = JwtUtil.getIdByToken(request);
String number=JwtUtil.getNumberByToken(request);
String role=JwtUtil.getRoleByToken(request);
if(method.isAnnotationPresent(RolePermission.class)){
RolePermission annotation = method.getAnnotation(RolePermission.class);
String roleRolePermission = annotation.role();
if(StringUtils.isBlank(roleRolePermission)){
throw new BaseException("接口权限获取失败");
}
int i = roleRolePermission.indexOf(role);
if(i==-1){
throw new BaseException("用户权限不足");
}
}
// 解密token信息
boolean result = JwtUtil.verify(token,id,number,role,secret);
if(result){
System.out.println("通过拦截器");
return true;
}
}
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
4.3拦截器配置
拦截器配置中主要定义拦截请求规则,将拦截器注入WebMvcConfigurer;cors跨域处理;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/* *
* @Author chenlirun
* <p>拦截器配置 </p>
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/* *
* @Author chenlirun
* <p> 设置拦截路径 </p>
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/userInfo/login");
// .excludePathPatterns("/**");
}
/* *
* @Author lsc
* <p> 将拦截器注入context </p>
* @Param []
* @Return com.zszxz.jwt.interceptor.JwtInterceptor
*/
@Bean
public JwtInterceptor authenticationInterceptor() {
return new JwtInterceptor();
}
/* *
* @Author chenlirun
* <p>跨域支持 </p>
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD")
.maxAge(3600 * 24);
}
}
测试
@SpringBootTest
class DemoApplicationTests {
@Value("${jwt.secret}") //调用实现yml中的配置属性
private String secret;
@Test
public void test(){
int id=1;
String role="1";
String asdf = JwtUtil.sign(id, role, secret);
System.out.println(asdf);
}
}
输出结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJudW1iZXIiOiIxMjM0NTYiLCJyb2xlIjoiYXNkZiIsImlkIjoxLCJleHAiOjE2MjY4NTc1NTN9.
NWjwBLPky03dUX3GCICKNTGFayqtXPOYJ5GmkE4EEOs
注解使用方式,加到controller 类文件头或者方法体上