JWT=JSON Web Token
1. JWT的组成
JWT==Header,Payload,Signature==>abc.def.xyz
1.1 Header
Header:标头。
两个组成部分:令牌的类型(JWT)和所使用的签名算法,经过Base64 Url编码后形成的JWT的第一部分。
{
"type":"JWT",
"alg":"HS256"
}
1.2 Payload
Payload---有效负载。存放用户自定义消息
注意点:是以明文的形式进行展示,以Base64进行编码。
{
“sub":"123",
"name":"jon",
"admin":true
}
1.3 Signature
signature--签名。使用标头的算法和私钥对第一部分和第二部分进行加密,通过Base64 Url编码后形成JWT的第三部分。
var encodeString =base64UrlEncode(header)+"."+base64UrlEncode(payload);
var signature=HMACSHA356(encodeString,secret);
2.JWT的基本使用
1.导入pom JWT依赖
JDK1.8以下的加入JWT依赖即可
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JDK1.8以上的加上其他
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
2.创建JWT
//毫秒
private long time=1000*60*60*24;
//签名
private String signature="admin";
/*JWT加密*/
@Test
public void jwt(){
//构建JWT对象:Jwts.builder()
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken = jwtBuilder
//header:头部
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload:载荷
.claim("username", "tom")
.claim("role", "admin")
.setSubject("admin-test")
//有效时间===>一天=当前时间+24小时
//获得当前的系统时间:System.currentTimeMillis()
.setExpiration(new Date(System.currentTimeMillis() + time))
//设置id字段
.setId(UUID.randomUUID().toString())
//signature:签名
//注意点:这个签名算法要与前面的算法一致
//设置加密算法和签名
.signWith(SignatureAlgorithm.HS256, signature)
//将前面3个重要信息拼接起来,使用”."来连接
.compact();
System.out.println(jwtToken);
}
3.验证JWT
@Test
/*验证JWT*/
public void checkJwt(){
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InRvbSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2OTM2MzgzMDMsImp0aSI6ImU5ZjY3ZjBhLWE0NTAtNGYzYy1hYTY3LTU3ZGMwOWRjMDM4ZCJ9.FxA5c91E2bk2KW1rdMo8jlmClXgn7r2mVSaXs19Qzzk";
//Jwts.parser():解析
JwtParser jwtParser = Jwts.parser();
//验证该token是否符合JWT规则
boolean result = Jwts.parser().isSigned(token);
System.out.println(result);//true
}
4.解析JWT
@Test
/*解析JWT*/
public void parseJwt(){
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InRvbSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2OTM2MzgzMDMsImp0aSI6ImU5ZjY3ZjBhLWE0NTAtNGYzYy1hYTY3LTU3ZGMwOWRjMDM4ZCJ9.FxA5c91E2bk2KW1rdMo8jlmClXgn7r2mVSaXs19Qzzk";
//获取JWT的解析对象
JwtParser jwtParser = Jwts.parser();
//通过signature对token进行签名,解开
//parseClaimsJws:将JWT转换为key-value的形式,通过key来获取对应的value
//Jws<Claims>:类似于Map集合
Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);
//获取Jws对象中的数据:get(key)表示根据key来获取value
//相当于将header和payload封装为一个claims
Claims claims = claimsJws.getBody();//存储的是用户保存的数据
Object username = claims.get("username");
System.out.println(username);
Object role = claims.get("role");
System.out.println(role);
String id = claims.getId();
System.out.println(id);
//签名
String subject = claims.getSubject();
System.out.println(subject);
//有效期
Date time = claims.getExpiration();
System.out.println(time);
}
3.SpringBoot + vue +JWT
1.导入pom JWT依赖
JDK1.8以下的加入JWT依赖即可
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JDK1.8以上的还要加上以下依赖
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
2.导入工具类
public class JwtUtil {
//毫秒
private static long time=1000*60*60*24;
//签名
private static String signature="admin";
public static String createToken(){
//构建JWT对象:Jwts.builder()
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken=jwtBuilder
//header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload
.claim("username", "admin")
.claim("role", "admin")
.setSubject("admin-test")
//有效时间===>一天=当前时间+24小时
//获得当前的系统时间:System.currentTimeMillis()
.setExpiration(new Date(System.currentTimeMillis()+time))
.setId(UUID.randomUUID().toString())
//signature
//注意点:这个签名算法要与前面的算法一致
.signWith(SignatureAlgorithm.HS256,signature)
//将前面3个重要信息拼接起来
.compact();
return jwtToken;
}
}
3.在后台解决跨域问题
@Configuration//配置类
public class CoreConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//所有接口
.allowCredentials(true)//是否发送Cookie
.allowedOriginPatterns("*")//支持域
.allowedMethods("GET","POST","PUT","DELETE")//支持方法
.allowedHeaders("*")
.exposedHeaders("*");
}
}
4.前台登录界面
5.前端代码
methods:{
handleSubmit(){
this.$refs.ruleForm.validate((valid)=>{
if(valid){
let _this=this
axios.get("http://localhost:8080/login",
{param:_this.ruleForm}).then(function (response){
console.log(response.data)
})
}
})
}
}
6.进行路由跳转,并且将用户信息存到本地存储中
JSON.stringify-->将JSON转换为字符串
handleSubmit(){
this.$refs.ruleForm.validate((valid)=>{
if(valid){
let _this=this
axios.get("http://localhost:8080/login",
{param:_this.ruleForm})./*带着用户输入的用户名和密码*/
then(function (response){
console.log(response.data)
//如果data不为空,则表示登录成功
if(response.data!=null){
//使用localStorage
//将JSON转换为字符串
localStorage.setItem('access-admin',JSON.stringify(response.data))
//跳转到首页
_this.$router.replace({path:"/"})
}
})
}
})
}
7.将用户信息展示到页面中
JSON.parse--->将字符串转换为JSON
<template>
<div>
欢迎回来:{{admin.username}}
</div>
</template>
data(){
return{
admin:''
}
},
created() {
//获取数据
this.admin=JSON.parse(window.localStorage.getItem("access-admin"));
},
8.添加验证用户信息是否存在【在路由中写】
/router/index.js
router.beforeEach((to,from,next)=>{
if(to.path.startsWith('/login')){
window.localStorage.removeItem('access-admin')
next()
}else {
let admin=JSON.parse(window.localStorage.getItem('access-admin'))
if(!admin){
next({path:'/login'})
}else {
//校验token合法性
axios({
url:'http://localhost:8080/checkeToken',
method:'get',
headers:{
token:admin.token
}
}).then((response)=>{
if(!response.data){
console.log("校验失败");
next({path:'/error'})
}
})
}
}
})
9.后端验证Token方法
UserController
/**
* 验证token方法
*/
@GetMapping("/checkToken")
public Boolean checkToken(HttpServletRequest request){
//因为前端是将数据存储在header,所以我们要是有getHeader
String token = request.getHeader("token");
return JwtUtil.checkToken(token);
}
JwtUtil
/**
* 校验token是否正确
*/
public static boolean checkToken(String token){
if (token==null){
return false;
}
try {
//解析
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
4.Springboot+JWT
User
@Data
public class User {
private String username;
private String password;
private String token;
}
UserController:生成JWT令牌
public class UserController {
private final String USERNAME = "admin";
private final String PASSWORD = "123123";
@GetMapping("/login")
public User login(User user){
//判断是否正确
if(USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())){
//添加token;token保存到user对象
//定义一个工具类,将生成JWT
user.setToken(JwtUtil.createToken());
return user;
}
return null;
}
}
JwtUtil:生成JWT令牌
public class JwtUtil {
//毫秒
private static long time=1000*60*60*24;
//签名
private static String signature="admin";
public static String createToken(){
//构建JWT对象:Jwts.builder()
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken=jwtBuilder
//header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload
.claim("username", "admin")
.claim("role", "admin")
.setSubject("admin-test")
//有效时间===>一天=当前时间+24小时
//获得当前的系统时间:System.currentTimeMillis()
.setExpiration(new Date(System.currentTimeMillis()+time))
.setId(UUID.randomUUID().toString())
//signature
//注意点:这个签名算法要与前面的算法一致
.signWith(SignatureAlgorithm.HS256,signature)
//将前面3个重要信息拼接起来
.compact();
return jwtToken;
}
}
【前端直接传递token】
JwtUtil:验证JWT是否过期
/**
* 校验token是否过期
*/
public static boolean checkToken(String token){
if (token==null || token==""){
return false;//false表示令牌过期
}
try {
//拿到JWT对象
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
UserController:验证JWT是否过期
/**
* 验证token是否过期:前端传递token
*/
@GetMapping("/checkToken")
public Boolean checkToken(String token){
return JwtUtil.checkToken(token);
}
【将Token放入请求头中,使用request】
JwtUtil:验证JWT是否过期
/**
* 校验token是否过期
*/
public static boolean checkToken(String token){
if (token==null || token==""){
return false;//false表示令牌过期
}
try {
//拿到JWT对象
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
UserController:验证JWT是否过期
/**
* 验证token是否过期:前端将数据封装在请求头中
* @param request
* @return
*/
public boolean checkToken(HttpServletRequest request){
//getHeader中的key要与前端通过一致
String token = request.getHeader("token");//key-->token
return JwtUtil.checkToken(token);
}
解决跨域问题
参考:SpringBoot跨域请求的问题_m0_63077733的博客-CSDN博客
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有接口
.allowCredentials(true) // 是否发送 Cookie
.allowedOriginPatterns("*") // 支持域
.allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法
.allowedHeaders("*")
.exposedHeaders("*");
}
}
拦截器实现Token验证
@Configuration
public class TokenInterceptor implements HandlerInterceptor {
//在请求处理方法被调用之前被调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//前提:token放在请求头中
String token = request.getHeader("token");
if (!JwtUtil.checkToken(token)) {
return false;//验证失败
}
return true;
}
}