桂林旅游几月份去最好seo自媒体培训
JWT
什么是 JWT
JSON Web Token,通过数字签名的方式,以 JSON 对象为载体,在不同的服务终端之间安全地传输信息
- 官网:https://jwt.io/
- SDK: https://jwt.io/libraries (含Java和各种语言)
- Java SDK(上面的SDK链接得到): https://github.com/auth0/java-jwt (java sdk也有很多种,这是其中一种)
- 可以根据自己的喜好选择 SDK,不同的SDK的API设计有差异,那自然会有好坏
- 本项目种使用的 SDK 也是官方推荐的其中一种(选官方的比来路不明的好)
JWT 有什么用?
JWT 最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求之前,都要先进行 JWT 安全校验,通过之后再进行处理。
JWT 的组成
JWT 由 3 部分组成,用 . 拼接
格式是 Header.Payload.Signature
- Header(头信息)
{'typ': 'JWT','alg': 'HS256'
}
包含类型typ和算法alg,类型写死 JWT,算法可以自己决定,这里用HS256举例
- Payload
实际的有效数据,你要传的业务数据,例如:
- Signature
Signature是这么算出来的?var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'your_secret');说明:
1、header和payload先用base64加密用点链接起来,再用header里alg所指定的算法计算摘要
2、your_secret 就是秘钥,加密方和解密方共同知道的,解密的时候要用
补充
当你拿到一段jwt字符串的时候(就是三段式的那串),你能知道什么?
对于 xxx.yyy.zzz,你可以知道
- xxx,可以用base64解密xxx
- yyy,本以为base64不能解密,结果可以,另外,我也遇到过不能解密的,可能是自己额外加密了
- zzz,不能base64解密
注意:不要整段粘贴进去用base64解密,要分段,否则可能会出现解密不了的情况。
例子
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0ZWFjaGVyIjp7Im5hbWUiOiJNaXNzIExlZSJ9LCJzZXgiOnRydWUsIm5hbWUiOiJTdG9uZSIsImNvdXJzZUxpc3QiOlt7Im5hbWUiOiJNYXRoIiwiY3JlZGl0cyI6NH0seyJuYW1lIjoiRW5nbGlzaCIsImNyZWRpdHMiOjN9XSwiZXhwIjoxNjkxNDExMzcyLCJhZ2UiOjIwfQ.C2LJfyeeS2E0tpS12o-MnQWvn3B7ecK_ul3kudpiAPE注意一定要分段粘贴进去进行base64解密,不要一整个整体,本例子没有问题,有些别的例子会出现yyy部分不能解密的情况!!!base64解密第一段:{"typ":"JWT","alg":"HS256"}
base64解密第一段:{"teacher":{"name":"Miss Lee"},"sex":true,"name":"Stone","courseList":[{"name":"Math","credits":4},{"name":"English","credits":3}],"exp":1691411372,"age":20}
base64解密第一段:(乱码,看来是解密不了)
Java 例子
其实实际的使用过程,Java 作为后端仅仅需要写解析jwt的,对于封装,这是前端要干的事情。
package com.wyf.test.jwt;import io.jsonwebtoken.*;import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;public class JWTTest {private static final String MY_SECRET = "xAy23hz23YK";// 加解密用的秘钥public static void main(String[] args) throws InterruptedException {String jwtString = testGetJwtString();//Thread.sleep(4*1000);// 模拟超市的情况是否能检查出来System.out.println("\n-------------------\n");testParseJwtString(jwtString);}// 得到加密的字符串,这个一般来说不是Java写的,肯定是前端人员写的,JS(react/vue等)public static String testGetJwtString() {/*格式:Header.Payload.Signatureheader的格式是:{'typ': 'JWT','alg': 'HS256'}其中类型typ写死,alg可选,这里选HS256算法Payload 是实际的数据Signature的计算方式如下(参考如下用JS写的伪代码):var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);var signature = HMACSHA256(encodedString, 'your_secret');其中 your_secret 就是秘钥,加密方和解密方共同知道的,解密的时候要用*/// 设置 token 有效期long tokenLifespan = 3 * 1000; // 30 秒Date expireDateTime = new Date(System.currentTimeMillis() + tokenLifespan);// 用 Java 中引入的 JWT 包里的 APIJwtBuilder jwtBuilder = Jwts.builder();String jwtString = jwtBuilder// header.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")// payload (claim 其实就是payload的意思,就是你要传的数据).setClaims(prepareMapPayloadData())// 可选,设置 token 有效期.setExpiration(expireDateTime)// signature (解密的时候用的secret不对的话会抛出signature无法匹配的异常:io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature.).signWith(SignatureAlgorithm.HS256, MY_SECRET)// 用点连接起来.compact();System.out.println("jwtString:" + jwtString);System.out.println("token duration(Sec):" + (tokenLifespan / 1000));SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));System.out.println("expireDateTime:" + simpleDateFormat.format(expireDateTime));return jwtString;}// 我在想这个方法根本就不需要返回payload里的值,如果使用JWT,仅仅是validate一下signature是不是正确的、达到鉴权效果就可以了public static Object testParseJwtString(String jwtString) {JwtParser jwtParser = Jwts.parser();// 设置解密用的秘钥(解密的时候用的secret不对的话会抛出signature无法匹配的异常)jwtParser.setSigningKey(MY_SECRET);// parse 的时候就会校验签名,不正确的时候会从这行抛出异常,也会校验token是否超时!如果超时爆出// Exception in thread "main" io.jsonwebtoken.ExpiredJwtException: JWT expired at 2023-08-07T18:59:42Z. Current time: 2023-08-07T18:59:44Z, a difference of 2416 milliseconds. Allowed clock skew: 0 milliseconds.Jwt parse = jwtParser.parse(jwtString);Header header = parse.getHeader();Object body = parse.getBody();// 其API是设计成用Object接收的System.out.println("header: " + (header != null ? header.getClass().toString() + ", " + header : "null"));System.out.println("body: " + (body != null ? body.getClass().toString() + ", " + body : "null"));return body;}/*** method to convert object to a map* @param obj* @return* @throws IllegalAccessException*/public static Map<String, Object> getObjectToMap(Object obj) throws IllegalAccessException {Map<String, Object> map = new HashMap<String, Object>();Class<?> cla = obj.getClass();Field[] fields = cla.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);String keyName = field.getName();Object value = field.get(obj);if (value == null)value = "";map.put(keyName, value);}return map;}public static Map<String, Object> prepareMapPayloadData() {Map<String, Object> objectToMap;try {objectToMap = getObjectToMap(preparePayloadData());} catch (IllegalAccessException e) {throw new RuntimeException(e);}System.out.println("payload map: " + objectToMap);return objectToMap;}public static Student preparePayloadData() {List<Course> courseList = new ArrayList<Course>();Course course1 = new Course();course1.setName("Math");course1.setCredits(4);Course course2 = new Course();course2.setName("English");course2.setCredits(3);courseList.add(course1);courseList.add(course2);Teacher teacher = new Teacher();teacher.setName("Miss Lee");Student student = new Student();student.setName("Stone");student.setAge(20);student.setSex(true);student.setTeacher(teacher);student.setCourseList(courseList);return student;}public static class Student {private String name;private Integer age;private Boolean sex;private Teacher teacher;private List<Course> courseList;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Boolean getSex() {return sex;}public void setSex(Boolean sex) {this.sex = sex;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}public List<Course> getCourseList() {return courseList;}public void setCourseList(List<Course> courseList) {this.courseList = courseList;}}public static class Teacher {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}public static class Course {private String name;private Integer credits;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getCredits() {return credits;}public void setCredits(Integer credits) {this.credits = credits;}}
}