Java生成token的工具类(对称签名)

简单了解Token与JWT

什么是Token

Token是什么?某度里这么说:
Token在计算机身份认证中是令牌(临时)的意思。一般作为邀请、登录系统使用。

也就是用户请求资源的的时候不需要每次都去验证账号密码等信息,而是只需要验证令牌是否正确即可。

打个简单的比方:假如你要进一个小区拜访一个富婆,如果是陌生人,小区的保安会询问你相关的信息,比如你叫什么,身份证多少,电话多少,要去拜访哪个住户,几栋几单元几号的用户。然后跟住户确定没问题之后才放你进去小区。

如果你要多次出入,保安大爷记性不好,记不住你的脸,但每次问这问那又很麻烦。于是大爷就给你发了一个“拜访证”,类似于以下这样:
在这里插入图片描述

这样的话,大爷只要看拜访证,就可以放你过去了。也就避免了每次都要把你当成陌生人来盘问的环节。

那么登录就可以看作这样子:
第一次登录
在这里插入图片描述
后续进入系统进行操作:
在这里插入图片描述
这样的话,就不必每次都去让用户输入账号和密码进行验证了,而只要在第一次登录验证通过的时候给用户一个携带有用户信息的token,后续只要携带token请求资源,就只需要校验token是否正确即可。

什么是JWT

JWT的全称是Json Web Token,直译过来就是Json格式的互联网令牌。其作用是为多种终端设备,提供统一的、安全的令牌格式。所以JWT是一种令牌的格式,大家约定好的一种令牌的格式。

为什么要用Token认证

早期我们使用session(客户端使用cookie)来存储登录信息来实现验证的,但相比于session,基于token的认证有以下优点

  1. 支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后不会存在信息丢失问题
  2. 无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服务端压力
  3. 更适用CDN:可以通过内容分发网络请求服务端的所有资料
  4. 更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
  5. 无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

以上信息来源于以下大佬的文章:JWT详解
token与session的区别及优势请戳上面大佬的文章。

使用token验证记住这一点:token中不存放敏感信息

了解使用到的类

引入的依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

简单了解需要使用的类

Claims

先看注释

This is ultimately a JSON map and any values can be added
to it, but JWT standard names are provided as
type-safe getters and setters for convenience.
Because this interface extends Map<String, Object>,
if you would like to add your own properties,
you simply use map methods, for example:
claims.put("someKey", "someValue");

简而言之这个类可以看成一个Json的Map映射集合,因为其继承了Map接口

SecretKey

这个之前讲AES算法的时候就说过了,是一个密钥的接口。

SecretKeySpec

密钥(SecretKey)的实现类

UUID

产生一个随机36字符的长字符串

JwtBuilder

A builder for constructing JWTs.

用于构造jwt的生成器,用于设置JWT的各种基本信息以及产生token字符

token工具类

import java.util.Base64;
import java.util.Date;
import java.util.UUID;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.DefaultClaims;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author 三文鱼先生
 * @title
 * @description 生成token和解析token的工具类
 * @date 2022/10/13
 **/
public class TokenUtil {
    //密钥明文I guess PeterPan was right,growing up's a waste of time.
    private static final String SECRET_CLEARTEXT = "PeterPan";
    //过期时间默认为半小时后过期
    private static final long EXPIRATION_TIME = 1000 * 60 * 30L;

    /**
     * @description 以Base64编码获取一个AES算法密钥
     * @author 三文鱼先生
     * @date 16:22 2022/10/14
     * @return javax.crypto.SecretKey
     **/
    public static SecretKey getSecretKey() {
        //以Base64编码获取到明文密钥的字节  以该数组生成一个AES算法的的密钥
        return new SecretKeySpec(Base64.getDecoder().decode(SECRET_CLEARTEXT) ,  "AES");
    }

    /***
     * @description 获取一个无 - 作为token的唯一ID
     * @author 三文鱼先生
     * @date 16:07 2022/10/14
     * @return java.lang.String
     **/
    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-" , "");
    }

    /***
     * @description 以默认的过期时间构造token
     * @author 三文鱼先生
     * @date 16:07 2022/10/14
     * @param str 存放在token里的信息
     * @return java.lang.String
     **/
    public static String getToken(String str) {
        return getJwtBuilder(str).compact();
    }

    /***
     * @description 以指定的过期时间构造token
     * @author 三文鱼先生
     * @date 16:07 2022/10/14
     * @param str 存放在token里的信息
     * @param mills 指定多少毫秒后过期
     * @return java.lang.String
     **/
    public static String getToken(String str , long mills) {
        return getJwtBuilder(str , mills).compact();
    }


    /**
     * @description 获取一个JWT的构造器
     * @author 三文鱼先生
     * @date 16:09 2022/10/14
     * @param str
     * @return io.jsonwebtoken.JwtBuilder
     **/
    public static JwtBuilder getJwtBuilder(String str) {
        if(str == null)
            throw new RuntimeException("实体数据为空");
        //获取到AES算法的密钥
        long nowMills = System.currentTimeMillis();
        DefaultClaims defaultClaims = new DefaultClaims();
        defaultClaims.put("msg" , str);
        return Jwts.builder()
                .setId(getUUID())          //唯一的ID
                .setSubject("token")       // 数据
                .setIssuer("PeterPan")     // 签发者
                .setClaims(defaultClaims)  //数据
                .setIssuedAt(new Date(nowMills))      // 签发时间设置为当前
                .signWith(SignatureAlgorithm.HS256, getSecretKey()) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(new Date(nowMills + EXPIRATION_TIME));//设置为超时时间
    }

    /***
     * @description 获取一个token的构造器
     * @author 三文鱼先生
     * @date 15:51 2022/10/14
     * @param str 数据
     * @param mills 过期时间
     * @return io.jsonwebtoken.JwtBuilder
     **/
    public static JwtBuilder getJwtBuilder(String str , long mills) {
        if(str == null)
            throw new RuntimeException("实体数据为空");
        //获取到AES算法的密钥
        long nowMills = System.currentTimeMillis();
        DefaultClaims defaultClaims = new DefaultClaims();
        defaultClaims.put("msg" , str);
        return Jwts.builder()//一个构造器 下面为必要属性的设置
                .setId(getUUID())           //唯一的ID
                .setSubject("token")        //主题为token
                .setIssuer("PeterPan")      // 签发者
                .setClaims(defaultClaims)   //数据存放
                .setIssuedAt(new Date(nowMills))      // 签发时间设置为当前
                .signWith(SignatureAlgorithm.HS256, getSecretKey()) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(new Date(nowMills + mills));//设置过期时间
    }

    /**
     * @description 从给定的token中获取msg信息
     * @author 三文鱼先生
     * @date 16:09 2022/10/14
     * @param token token
     * @return java.lang.String
     **/
    public static String getMsgFromToken(String token)
            throws UnsupportedJwtException ,//不支持的格式异常
            MalformedJwtException ,//平台jwt异常
            SignatureException,//签名异常
            ExpiredJwtException,//超时异常
            IllegalArgumentException { //非法参数异常
        return Jwts.parser()//token的语法分析器
                .setSigningKey(getSecretKey())//设置签名验证所用的密钥
                .parseClaimsJws(token)//处理token
                .getBody()//获取存入的token里的所有信息
                .get("msg").toString();//获取claims里面存放的msg数据
    }

}

测试

public class Test {
    public static void main(String[] args) {
        String token = TokenUtil.getToken("放入token中的msg");
        System.out.println(token);
        System.out.println("msg: " + TokenUtil.getMsgFromToken(token));

    }
}

测试结果

eyJhbGciOiJIUzI1NiJ9.eyJtc2ciOiLmlL7lhaV0b2tlbuS4reeahG1zZyIsImlhdCI6MTY2NTczNjE2NCwiZXhwIjoxNjY1NzM3OTY0fQ.CW3yZ7DP4fXHQTnJVSIQZwvQcabzkTd7FTNl3sIf9a4
msg: 放入token中的msg
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值