介绍
JSON Web Token(JWT)是一种开放标准,用于作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输中完成数据加密、签名等相关处理。
实现前后端分离就用JWT
Jwt的核心是什么:一种信息交换,一种是用来做javaweb中的安全验证
流程
-
用户使用用户名密码请求服务器
-
服务器验证用户信息
-
服务器通过验证发送给用户一个token
-
客户端存储token,并每次请求时附带一个token值
-
服务器验证token并返回数据
实现案例
1,在pom.xml里导入依赖配置
<!-- 导入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.18.3</version>
</dependency>
2,新建一个po文件夹里面存放下面两个Java文件
info.java
package com.xxgc.helloworld.po;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
//Lombok自动生成set get方法
@Data
//全参构造
@AllArgsConstructor
//无参构造
@NoArgsConstructor
public class Info {
//返回状态码
private Integer code;
//返回信息
private String msg;
//返回的数据
private Object data;
//版本号
private String version;
//请求响应时间
private Date ResponseDate;
public Info(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Info(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
}
TokenInfo.java
package com.xxgc.helloworld.po;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @program: helloworld
* @description: token返回
* @author: liutao
* @create: 2022-03-07 17:25
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TokenInfo {
/**
* 返回状态码
* 401 登录过期 或未登录
* -202 无效签名
* -203 算法不一致
* -204 token无效
*/
private Integer code;
//返回信息
private String message;
}
3,新建一个config文件夹并新建以下文件
InterceptorConfig.java
package com.xxgc.helloworld.config;
import com.xxgc.helloworld.interceptor.JWTInterceptors;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
/**
* @program: helloword
* @description: 拦截器配置
* @author: liutao
* @create: 2022-03-07 16:11
**/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
//配置拦截器规则
public void addInterceptors(InterceptorRegistry registry){
//swagger相关
ArrayList<String> swagger = new ArrayList<>();
swagger.add("/*.html");
swagger.add("/swagger-resources/**");
swagger.add("/webjars/**");
swagger.add("/v2/**");
swagger.add("/swagger-ui.html/**");
//jwt 相关
registry.addInterceptor(new JWTInterceptors())
.addPathPatterns("/**")//需要进行token验证的
.excludePathPatterns("/login/namePassLogin")//放行的
.excludePathPatterns(swagger);//放行swagger相关
}
}
4,新建一个interceptors文件夹
JWTInterceptors.java
package com.xxgc.helloworld.interceptor;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxgc.helloworld.po.TokenInfo;
import com.xxgc.helloworld.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @program: helloworld
* @description: JWT拦截器
* @author: liutao
* @create: 2022-03-07 15:55
**/
public class JWTInterceptors implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
TokenInfo info = new TokenInfo();
// 获取请求头中令牌
String token = request.getHeader("token");
try {
JWTUtils.verify(token);// 验证令牌
return true; // 放行请求
} catch (SignatureVerificationException e) {
info.setCode(-202);
info.setMessage("无效签名");
}catch (TokenExpiredException e){
info.setCode(401);
info.setMessage("登录过期");
}catch (AlgorithmMismatchException e){
info.setCode(-203);
info.setMessage("算法不一致");
}catch (Exception e){
info.setCode(-204);
info.setMessage("token无效");
}
// 将map以json的形式响应到前台 map --> json (jackson)
String json = new ObjectMapper().writeValueAsString(info);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
5,新建utils文件夹
JWTUtils.java
package com.xxgc.helloworld.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
/**
* @program: helloword
* @description: JWT工具类
* @author: liutao
* @create: 2022-03-07 15:20
**/
public class JWTUtils {
/**
* 加密和解密使用的密钥
* */
private static final String SING = "NIUYEYE";
//用于获取token
public static String getToken(Map<String,String> map){
Calendar instance = Calendar.getInstance();
//配置令牌失效时间 20秒后失效 (项目当中一般30分钟)
instance.add(Calendar.DATE,1);
//创建jwt
JWTCreator.Builder builder = JWT.create();
map.forEach((k,v) ->{
builder.withClaim(k,v);
});
String token = builder
.withExpiresAt(instance.getTime())//过期时间
.sign(Algorithm.HMAC256(SING));//签名
return token;
}
//合法性 校验
public static DecodedJWT verify(String token){
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
6,调用
在用户登录成功以后生成token
package com.xxgc.helloworld.service.Impl;
import com.xxgc.helloworld.bean.Users;
import com.xxgc.helloworld.bean.UsersExample;
import com.xxgc.helloworld.dao.UsersMapper;
import com.xxgc.helloworld.po.Info;
import com.xxgc.helloworld.service.ILoginService;
import com.xxgc.helloworld.utils.JWTUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
@Service
public class LoginService implements ILoginService {
//业务逻辑层和dao层关联
@Autowired
private UsersMapper um;
//ctrl + q
@Override
public Info namePassLogin(Users u) {
UsersExample example = new UsersExample();
example.createCriteria()
.andUsernameEqualTo(u.getUsername())
.andPasswordEqualTo(u.getPassword());
//alt+shift+l ctrl+alt+v 生成返回结果
List<Users> users = um.selectByExample(example);
//判断是否为空
if (CollectionUtils.isEmpty(users)) {
return new Info(-200, "用户名或密码错误");
} else {
HashMap<String, String> map = new HashMap<>();
map.put("username",users.get(0).getUsername());
String token= JWTUtils.getToken(map);
map.put("token",token);
return new Info(200, "登录成功", map);
}
}
}
7,登录返回token
没有token时访问,因为被拦截器拦截不能被放行
当携带token时正常访问