JWT=JSON Web Token
1. JWT的组成
JWT==Header,Payload,Signature==>abc.def.xyz
地址:JSON Web Tokens - jwt.er
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加密*/@Testpublic 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封装为一个claimsClaims 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 {@Overridepublic 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=thisaxios.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=thisaxios.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,所以我们要是有getHeaderString 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对象//定义一个工具类,将生成JWTuser.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-->tokenreturn JwtUtil.checkToken(token);}
解决跨域问题
参考:SpringBoot跨域请求的问题_m0_63077733的博客-CSDN博客
@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 所有接口.allowCredentials(true) // 是否发送 Cookie.allowedOriginPatterns("*") // 支持域.allowedMethods("GET", "POST", "PUT", "DELETE") // 支持方法.allowedHeaders("*").exposedHeaders("*");}
}
拦截器实现Token验证
@Configuration
public class TokenInterceptor implements HandlerInterceptor {//在请求处理方法被调用之前被调用@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//前提:token放在请求头中String token = request.getHeader("token");if (!JwtUtil.checkToken(token)) {return false;//验证失败}return true;}
}