Spring Boot中使用token:jwt

token由3部分组成:Header,Payload,Signature。

其中Header记录了签名算法和token 的类型。

Payload是以明文存储的一些信息,包括用户自定义信息。

Signature是使用签名算法,对Payload结合服务端才知道的私钥进行签名后得出的结果。

服务端对这3部分使用base64编码,然后以.号分隔,就得到了token字符串,格式为:

xxxxxx.yyyyyy.zzzzzz

每次前端进行请求时,带上这个token字符串。当服务端收到请求后,就会对Payload计算签名,然后与token中的Signature进行比较,若一致,则通过。

逻辑简单,完全可以自己写一个token类来实现以上功能。

Spring Boot中提供了一个第三方的token库,叫jwt。

 

  1. 在pom.xml中引入依赖:
<!-- JWT Json Web Token 依赖 -->
<dependency>
   <groupId>io.jsonwebtoken</groupId>
   <artifactId>jjwt</artifactId>
   <version>0.7.0</version>
</dependency>
  1. 生成token:
public String getAccessToken() {
    JwtBuilder jwtBuilder = Jwts.builder();

    long nowMillis = System.currentTimeMillis();
    // 7个官方Payload字段
    jwtBuilder.setId("1.0"); // 编号/版本
    jwtBuilder.setIssuer("ISSUER"); // 发行人
    jwtBuilder.setSubject("SUBJECT"); // 主题
    jwtBuilder.setAudience("AUDIENCE"); // 受众
    jwtBuilder.setIssuedAt(new Date(nowMillis)); // 签发时间
    jwtBuilder.setNotBefore(new Date(nowMillis)); // 生效时间
    jwtBuilder.setExpiration(new Date(nowMillis + (60 * 60 * 1000))); // 失效时间

    // 用户自定义字段
    jwtBuilder.claim("id", "AXDBDCD");
    jwtBuilder.claim("name", "testname");
    jwtBuilder.claim("value", "123456");
    
    // 定义私钥
    String HS256KEY = "xxxxxx";
    // 签名算法
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    // 计算签名key值
    Key signingKey = new SecretKeySpec(Base64.decodeBase64(HS256KEY), signatureAlgorithm.getJcaName());

    // 进行签名
    jwtBuilder.signWith(signatureAlgorithm, signingKey);

    // 获取token字符串
    String tokenString = jwtBuilder.compact();

    return tokenString;
}

注意:

  • 要先为jwtBuilder设置claims,然后再调用jwtBuilderset接口来设置7个官方payload字段。这是因为jwtBuilder是将7payload字段存储在claims中的。若先设置了payload,再设置claims,会将已设置的payload覆盖掉。
  • payload字段中:id通常设为版本号;issuer是发行人,通常设为公司名;subject是主题,通常设为工程名;audience是受众,通常设为获取token 的接口名。一般来说,audience多被设为” login”,也就是登录。
  • 7个官方payload字段中的3个时间:签发时间(issued at),生效时间(not before),失效时间(expiration)。3个时间使用的单位是秒。调用jwtBuilderset接口来设置这3个时间时,只需要传入Date()型数据即可,JwbBuilder会自动将其转换为秒存储在claims的相应字段里。详见附录。
  1. 解析token:
public boolean isTokenValid(String token) {
    try {
        Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token.trim()).getBody();
        Date date = claims.getExpiration();
        return new Date().before(date);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

将token字符串解析为Jws对象,然后获取claims,这样就能拿到claims中存储的各个字段。然后自行判断即可。其中解析用的signingKey与2中生成的signingKey是同一个。

由于(当前解析的时间<生效时间)或(当前解析的时间>失效时间)均会抛出异常,所以需要对异常进行捕获,返回false给前端,表示token已失效。

 

 

附录

  1. claims的设置

JwtBuilder允许用户自定义claims,并提供了3个相关函数:

JwtBuilder setClaims(Claims var1);
JwtBuilder setClaims(Map<String, Object> var1);
JwtBuilder claim(String var1, Object var2);

其中:

  • 两个setClaims会将之前已存在的claims覆盖掉。
  • claim会将新的值push给当前claims。

JwtBuilder将7个官方payload字段也存储在claims中。也就是说,用户自定义的字段和官方的payload字段是存放在一起的。

因此,若已经为JwtBuilder设置了claims字段,则不能再调用setClaims(),否则已设置的会被覆盖。推荐使用claim()函数。

JwtBuilder提供了7个set函数来设置7个官方payload字段。以setExpiration()为例。

·若还没有调用JwtBuilder的setClaims(),则调用setExpiration(),会先为JwtBuilder添加一个空的claims,然后将expiration设置进去。

·若已经调用了JwtBuilder的setClaims(),则调用setExpiration(),会将expiration的值push到已存在的claims中。

  1. not before与expiration

7个官方payload字段中,有3个时间:

·issue at:签发时间

·not before:生效时间

·expiration:失效时间

当Jwts对token字符串进行解析时,若存在not before和expiration,则会进行判断:

当前解析的时间必须>=not before

当前解析的时间必须<=expiration

否则会抛出异常。所以解析时需要对异常进行捕获处理。

若这2个时间都没有进行设置,则不会进行判断。

然而,其流程是:

JwtBuilder调用setNotBefore()来设置时间→生成token返回给用户→用户请求→对请求的token进行解析。

其中:

  • setNotBefore()来设置时间时,将时间转换为秒,是一个long型的数据,存储到claims中。
  • 解析时,将claims中的秒取出来,转成Date型数据,然后与当前时间的Date比较,来判断是否抛出异常。

因此:

jwtBuilder.setNotBefore(new Date());
jwtBuilder.claim("nbf", new Date().getTime()/1000);

效果是相同的。

其中7个字段名(例如"nbf")定义在Claims接口中(例如Claims.NOT_BEFORE)。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值