token验证的机理和简单实现

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 00:13   677   0

摘要:token验证,机制实现

一、cookie验证和token验证的比较

cookie验证的机制是通过在客户端生成cookie,在服务器端生成session,然后每次请求时通过核对前端传来的cookie和服务器端session是否一致来管理用户的状态。当我们关闭浏览器的时候session会被释放,而cookie也可以自定义失效时间使其在一定时间内失效。

token的验证机制放弃了session,为用户生成包含各类信息的一个集合,将其编码为一个令牌(token),将这个令牌发给前端,前端每次需要验证身份时携带令牌,服务器检查令牌的合法性以验证身份。简化了服务器的存储,但是要比session+cookie的验证方式更加消耗计算。是一种以时间换空间的方式。


相较于cookie验证的几点好处:

  1. 支持跨域:cookie是不支持跨域的,token则可以做到。这一点对于我们项目深有感触,前端和后台是分离开发和测试,前端的测试常常需要跨域测试,没有token之前总是统一汇总测试,很麻烦,也想当拖节奏。
  2. 无状态:因为cookie验证是依赖于session的状态的,而token则完全不用在session中保存任何数据,所以可以做到无状态,只要有token并且合法就可以。

二、token的组成和简单实现

token主要有三个部分组成:头部,载荷,签名。

头部和载荷使用的是base64编码的,签名则是使用的HS256。当时不是很明白这个HS256,实际上就是HMAC using SHA-256。一种带密钥的加密。base64可以利用java有自带的工具Base64Encoder。

具体参考下面的博客:

https://blog.csdn.net/weixin_39800144/article/details/78853323

我们通过一个类来实现这个token的功能

此处使用了导入了Jackson的jar包,方便将对象转化为json,不然需要手动的转化。

import java.util.Date;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.core.util.Base64Encoder;
import com.zhiku.util.Data;

public class Token {
 
 //JWT的header,实际工作中从配置文件中获得
 public static String Header = "{\"typ\": \"JWT\",\"alg\": \"HS256\"}";
 //密钥
 public static String SECRET_KEY = "secret";
 //base64编码工具
 public static Base64Encoder be = new Base64Encoder();
 
 public static String getToken(Data message) throws Exception{
  String header = setHeader();
  String payload = setPayload(message);
  String signature = setSignature(header + "." + payload);
  return header + "." + payload + "." + signature;
  
 }

 /**
  * 设置JWT的荷载payload,荷载中包含主要的信息
  * @param payload 一个可以格式化为json的对象
  * @return
  * @throws Exception
  */
 public static String setPayload(Object payload) throws Exception{
  String base64_payload = om.writeValueAsString(payload);
  return be.encode(base64_payload.getBytes());
 }
 
 public static Object getPayload(String base64_payload) throws Exception{
  byte[] h = be.decode(base64_payload);
  return om.readValue(h, Object.class);
 }
 
 /**
  * 设置header
  * 获取已有的header,然后生成base64编码
  * @return
  */
 public static String setHeader(){
  return be.encode(Header.getBytes());
 }
 
 /**
  * 解析header
  * @param base64_Header base64编码的header部分
  * @return 返回解码后的header,以一个data的形式返回对应的json
  * @throws Exception 在将json转变为data对象时可能出现异常
  */
 public static Object getHeader(String base64_Header) throws Exception{
  byte[] h = be.decode(base64_Header);
  return om.readValue(h, Object.class);
 }
 
 /**
  * 将header和payload使用HS256加密
  * 然后对加密信息进行base64编码
  * 生成JWT的签名
  * @param message header.payload
  * @return JWT的签名
  * @throws Exception
  */
 public static String setSignature(String message) throws Exception{
  Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
     SecretKeySpec secret_key = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
     sha256_HMAC.init(secret_key);

     String hash = be.encode(sha256_HMAC.doFinal(message.getBytes()));
     return hash;
 }
 
 /**
  * 验证token的正确性
  * @param token 待验证token
  * @return token是否正确
  */
 public static boolean testSign(String token){
  boolean equal = false;
  String fore_message = token.substring(0,token.lastIndexOf('.'));
  String sign = token.substring(token.lastIndexOf('.')+1);
  try{
   if(sign.equals(setSignature(fore_message))){
    equal = true;
   }else{
    equal = false;
   }
  }catch(Exception e){
   equal = false;
  }
  return equal;
 }
 
 
}

注意:在经过base64转码之后有些时候会包含“+”,在前端传给后台的时候,“+”会变成“ ”(加号变成了空格)。这个时候就会影响token的验证,可以使用字符串将其替换。


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP