JWT的全称为Json Web Token,它可以帮助消息在Web层安全传递。
抛弃以前的cookie和session,jwt最重要的一个特点是无状态
以前是cookie存在客户端本地,sessionid存储在服务器,同时寄生于cookie。
前者存在不安全性,可能存在cookie欺骗工具,后者若是小数据还好说,若是大数据,则加大了服务器的压力。
至此,token它诞生了。它比较与cookie有如下一些优点
- 支持跨域访问,将token置于请求头中,而cookie是不支持跨域访问的;
- 无状态化,服务端无需存储token,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;
- 无需绑定到一个特殊的身份验证方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;
- 避免CSRF跨站伪造攻击,是因为它不依赖于cookie;
- …
首先我们来谈谈JWT的组成吧
它是由header(消息头),payload(载荷),signature(签名) 三部分组成
1、header头部主要是包含以下一些信息:
- 主要采用的key-value来阐述信息
- typ:代表是消息体类型,这里指的是JWT
- alg:采用的加密算法,默认是HS256(HMAC-SHA256),当然你可以指定其他类型的算法
2、payload主体主要是包含以下一些信息:
- payload是存在需要发送的数据,可以支持自定义数据,当然也是有如下默认数据可以填入
- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
你也可以自定义为
{
“name”:xxx ,
“age”:xxx
}
3、signature代表是签名:
- 签名是根据你设置的秘钥和加密算法来进行加密的
好的,理论说完了,我们开始来一个实战吧
请在你的maven下加入下面配置信息
<!--单元测试-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!--lombok 注解式开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!--JWT 包-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.1</version>
</dependency>
上面我用到了lombok注解开发,如果你没有接触过lombok,你需要去插件商店下载lombok,如下
我的包结构如下(实际只用到下面贴出代码的三个class)
/*TokenUtils.java*/
package com.ysj.pojo;
import com.auth0.jwt.*;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("all")
public class TokenUtils {
// 设置过期时间为30分钟
private static final long EXPIRATION_TIME = 30*60*1000;
// 设置加密字符串,要足够复杂足够乱
private static final String SECRET_KEY = "<sda122D4dD4AS5SF894FS5>?sd!@21fDADsdadgrhdfop";
// 根据token来 生成一个加密的token字符串
public String getToken(Token token){
// 设置过期时间
Date lastDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME);
// 选择算法
Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
// 设置header
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
// 生成token
return JWT.create()
.withHeader(header)
.withClaim("userid",token.getUserid())
.withClaim("name",token.getName())
.withClaim("age",token.getAge())
.withClaim("lastTime",token.getLastTime())
.withExpiresAt(lastDate)
.sign(algorithm);
}
// 获取token数据
public Token getTokenData(String tokenString){
// 对token的加密字符串进行解密
DecodedJWT jwt = JWT.decode(tokenString);
// 通过Token类的建造者模式来实例化类,注意builder是一个静态方法
Token token = Token.builder()
.userid(jwt.getClaim("userid").asString())
.name(jwt.getClaim("name").asString())
.age(jwt.getClaim("age").asInt())
.lastTime(jwt.getClaim("lastTime").asDate()).build();
return token;
}
// 创建一个token
public String createToken(String userid,String name,int age){
// 创建初始时间日期
Date date = new Date();
Token token = Token.builder()
.userid(userid)
.name(name)
.age(age)
.lastTime(date)
.build();
return this.getToken(token);
}
}
/*Token.java*/
package com.ysj.pojo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder()
public class Token {
private String userid;
private String name;
private int age;
private Date lastTime;
}
下面是测试类
package com.ysj.testPackage;
import com.ysj.pojo.Token;
import com.ysj.pojo.TokenUtils;
import org.junit.jupiter.api.Test;
public class testToken {
@Test
public void testTokenOperation(){
TokenUtils tokenUtils = new TokenUtils();
String tokenString = tokenUtils.createToken("ysj66666","凌寒天",20);
System.out.println("------生成token-----------\n"+tokenString);
Token tokenData = tokenUtils.getTokenData(tokenString);
System.out.println("------获取token数据-----------\n"+tokenData.toString());
}
}
输出如下
加密字符串是以. 进行分割的,总共有三部分 (header,payload,signature) ,所以有三个点,每个点对应一个加密字符串。
服务端用秘钥进行对加密字符串的解密,获取用户信息并返回。
这就是简单的token加密与解密测试。