Token在WEB系统中相当于临时令牌的作用,一般作为验证使用。JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。Token的出现主要解决了两方面问题。
Session会话机制,可以对用户信息进行存储,方便用户再次进入系统,或者进行相应的业务操作。但是,Session是存储于服务器的,会在一定的时间内消耗服务器的内存,同时,Session储存于固定的服务器中,因此,跨域问题就成了一个大麻烦。
Token将用户信息、加密算法、签名哈希进行加密发给客户端,让客户端进行存储,又有点类似于cookie机制了,但token可比cookie安全多了。
JWT的结构
JWT,即Json Web Token(以下简称jwt)即就是一种token的实现方式。一般来说,一个完整的jwt需要分为三个部分,token头、有效载荷、哈希签名 。(jwt官方网站: https://jwt.io/)
一、JWT头
{
"alg": "HS256", 签名使用的算法
"typ": "JWT" 令牌类型,这里就是jwt
}
将这个对象进行BASE64URL加密就是jwt的头。
二、PAYLOAD 有效载荷
{
"sub": "1234567890", 主题
"name": "John Doe", 自定义字段
"iat": 1516239022 发布时间
}
PAYLOAD有效载荷中既可以存放一些已经定义过的字段,也可以自定义字段。
~~payload预定义的一些字段~~
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
将这个对象同样使用BASE64URL加密并且与jwt头以点号( . )隔开。
三、VERIFY SIGNATURE 哈希签名
对jwt头和载荷信息使用指定算法进行哈希签名,确保数据的完整性。
在签名算法中需要指定一个私钥,这个私钥只存储于服务器中,不能向用户公开。
将生成的哈希签名作为第三部分与前两部分以点号隔开,就生成了一个完整的token。
JWT的存储
服务器在验证用户合法性后会生成token,并将这个token发送给客户端,由客户端(即浏览器)存储在cookie或者local storage中。
客户再次进入系统时,客户端将携带token发给服务器作验证,这时候,token一般位于HTTP请求的HEADER AUTHORITON字段中,有时,也会放在post请求的数据主体中。
JWT的优缺点
1、JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。
2、当JWT未加密方法是,一些私密数据无法通过JWT传输。
3、JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
4、JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
5、JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
6、为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。
JWT的实现
这里只介绍java中如何实现,其它编程语言请参考JWT官网。
1.先引入jwt所需的jar包
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
2.生成token以及验证
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class AuthTools {
public static String sign(String userName, String userId){
try {
Date date = new Date(System.currentTimeMillis() + Constant.EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(Constant.TOKEN);
//设置头部信息
Map<String, Object> header = new HashMap<>(2);
header.put("typ","jwt");
header.put("alg","HS256");
return JWT.create()
.withHeader(header)
//设置自定义载荷信息
.withClaim("userName",userName)
.withClaim("userId", userId)
//设置token过期时间
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(Constant.TOKEN);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (UnsupportedEncodingException e) {
return false;
}
}
}
//一些用到的常量
public class Constant {
//设置过期时间为15分钟
public static final long EXPIRE_TIME = 15*60*1000;
//token私钥
public static final String TOKEN = "cabsycbiabiebciubadiugi";
}
3.验证token的生成
import ch.qos.logback.core.net.SyslogOutputStream;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.test.travel.tools.AuthTools;
import com.test.travel.tools.Constant;
import java.io.UnsupportedEncodingException;
public class TestMain {
public static void main(String[] args) {
String token = AuthTools.sign("username","1");
System.out.println("用户是否合法: " + AuthTools.verify(token));
Algorithm algorithm = null;
try {
algorithm = Algorithm.HMAC256(Constant.TOKEN);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
System.out.println("header: " + jwt.getHeader());
System.out.println("payload:" + jwt.getPayload());
System.out.println("signature: " + jwt.getSignature());
System.out.println("token: " + jwt.getToken());
System.out.println("userName: " + jwt.getClaim("userName").asString());
System.out.println("userId: " + jwt.getClaim("userId").asString());
System.out.println("算法: " + jwt.getAlgorithm());
}
}
##执行结果
用户是否合法: true
header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
payload:eyJ1c2VyTmFtZSI6InVzZXJuYW1lIiwiZXhwIjoxNTU1NTkzMDMwLCJ1c2VySWQiOiIxIn0
signature: M7b7w1Zd4jeVCGii9VJRlfTQgjD_cCtQYWyDQhCeU2s
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6InVzZXJuYW1lIiwiZXhwIjoxNTU1NTkzMDMwLCJ1c2VySWQiOiIxIn0.M7b7w1Zd4jeVCGii9VJRlfTQgjD_cCtQYWyDQhCeU2s
userName: username
userId: 1
算法: HS256