1️⃣ What is token?
token是令牌的意思,作用就像“通关令牌”一样,持有token的请求会被“放行”,不持有token的请求可以被拦截(可以设置白名单使不被拦截,例如登陆请求)。
token是由服务端创建的一串字符串,登陆成功后发送给前端存储在浏览器或本地中,以后每次发送请求都携带上。
1. 使用拦截器在请求发出前在请求头中添加上 token。
2. 将 token 存储在浏览器的 cookies 中,符合一些条件每次请求都会自动带上 token。
2️⃣ Why do we use token?
在前后端分离的web项目中,HTTP是无状态协议,即使使用账号密码登陆,后端仍然无法分辨是谁发出的请求,要么后端不需要确认请求者的身份,要么每次请求都携带身份信息供后端确认。
显然第一种方法对软件的安全会造成极大的威胁,那么第二种方法就被改善成了token,token就是加密了的用户身份信息。
3️⃣ token组成(Composition of token)
以jwt(java web token)为例,下图介绍了详细介绍了token的组成。
4️⃣ 校验token(Verify token)
这里只说最基础的校验。
token的加密一般是可逆的,后端接收到token中,还可以根据加密的算法再进行解密,以获取荷载中的用户信息,因此荷载中不能放置密码等信息。
第三部分由于加入了自己制定的秘钥(秘钥一般保存在后端代码中),解密成功后会与前两部分和保存的秘钥进行对比,对比成功了才算token验证通过。经过这样的双重保障,这三部分每一部分被篡改都会被发现。
校验流程:
5️⃣ 实操:生成token(Generate token)
以生成jwt为例子,代码如下:
<!-- 引入jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.2</version>
</dependency>
@Slf4j
public class JwtUtil {
/**
* 使用固定的解密秘钥
*/
private static final String SECRET = TOKEN_SECRET;
/**
* @version: V1.0
* @description: 生成token并验证token并解密token中的信息
* @param: userInfo 用户手机号和用户Id
* @return: java.lang.String 返回token
**/
public static String getToken(UserInfoEntity userInfo) {
try{
//用秘钥生成签名
Algorithm algorithm = Algorithm.HMAC256(SECRET);
//默认头部+载荷(手机号/id)+签名=jwt
String jwtToken= JWT.create()
.withClaim("userPhone", userInfo.getUserPhone())
.withClaim("userId", userInfo.getUserId())
.sign(algorithm);
log.info("用户{}的token生成成功:{}",userInfo.getUserId(),jwtToken);
return jwtToken;
}catch (Exception e){
log.error("用户{}的token生成异常:{}",userInfo.getUserId(),e);
return null;
}
}
/**
* @version: V1.0
* @description: 校验token是否正确
* @param: token
* @param: userPhone
* @return: UserInfoEntity token中的用户信息(姓名/id)
**/
public static UserInfoEntity verify(String token) {
try {
// 根据用户信息userInfo生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.build();
// 效验TOKEN
verifier.verify(token);
log.info("token:{}校验成功成功",token);
//返回token内容
return getTokenInfo(token);
} catch (Exception exception) {
log.error("token校验异常:{}",exception);
return null;
}
}
/**
* @version: V1.0
* @Title: getUsername
* @description: 从Token中解密获得Token中的用户信息
* @param: token
* @return: UserInfoEntity token中的用户信息(姓名/id)
**/
private static UserInfoEntity getTokenInfo(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
UserInfoEntity userInfo=new UserInfoEntity();
userInfo.setUserPhone(jwt.getClaim("userPhone").asString());
userInfo.setUserId(jwt.getClaim("userId").asString());
log.info("用户{}从token获取用户信息成功",userInfo.getUserId());
return userInfo;
} catch (JWTDecodeException e) {
log.error("从token:{}获取用户信息异常:{}",token,e);
return null;
}
}
}