JWT的介绍、代码实现与解决方案

简介

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单

 

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

载荷就是存放有效信息的地方

签证信息由3部分组成

header (base64后的)

payload (base64后的)

secret

 

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

 

优点

体积小,因而传输速度更快

多样化的传输方式,可以通过URL传输、POST传输、请求头Header传输(常用)

简单方便,服务端拿到jwt后无需再次查询数据库校验token可用性,也无需进行redis缓存校验

在分布式系统中,很好地解决了单点登录问题

很方便的解决了跨域授权问题,因为跨域无法共享cookie

 

缺点

因为JWT是无状态的,因此服务端无法控制已经生成的Token失效,是不可控的,这一点对于是否使用jwt是需要重点考量的

获取到jwt也就拥有了登录权限,因此jwt是不可泄露的,网站最好使用https,防止中间攻击偷取jwt

在退出登录 / 修改密码时 token仍生效。下面有解决方案

 

核心代码

    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.2.0</version>
    </dependency>
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
    </dependency>
package com.mtgg.utils;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;

@Slf4j
public class JwtUtils {

    public static void main(String[] args) throws InterruptedException {
        String x = createJWT("mtgg", "", "1", 120 * 1000);
        Claims claims = parseJWT(x);
        System.out.println(JSON.toJSON(claims));
        claims.setIssuer("lskdfjl");

        Claims claims1 = parseJWT(x);
        System.out.println(JSON.toJSON(claims1));
    }


    /**
     * 签发JWT
     *
     * @param
     * @param subject   可以是JSON数据 尽可能少
     * @param ttlMillis
     * @return String
     */
    public static String createJWT(String subject, String roles, String version, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
//                .setId(id)      //唯一编号
                .setSubject(subject)   // 主题
                .setIssuer("user")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .claim("roles", roles)      //自定义
                .signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            builder.setExpiration(expDate); // 过期时间
        }
        String jwt = builder.compact();
        System.out.println("签发的jwt: " + jwt);
        return jwt;
    }


    /**
     * @Auther: Gale Li
     * @Date: 2020/2/19 19:50
     * @Description:
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.decode("MTGG2#J2*qlk64&HYU");
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }


    /**
     * 解析JWT字符串
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) {
        try {
            SecretKey secretKey = generalKey();
            return Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (ExpiredJwtException e) {
            log.error("过期异常:{}", e.toString());
            return null;
        } catch (Exception e) {
            log.error("解析异常:「」:{}", e.toString());
            return null;
        }
    }
}

 

如何主动使token失效方案?

将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。

维护一个 token 黑名单,失效则加入黑名单中。个人还是推荐这种方式,可以设置token到redis中,其过期时间可以就是当初用JWT生成token时的失效时间,因为先生成后设置黑名单,所以黑名单中的token一定在JWT的token失效时间之后

 

网上还有说白名单,同理;还有更改secret的,每个用户都有一个自己的secret,想了一下一样要借助外部记录自己的secret,在更改的时候才会起作用

比较尴尬的token存储的数据不能修改,如果找到一种方式修改,那么不依赖外物可修改本身的一个version,解密的时候验证此version就可以达到验证失败的效果。如果大神有办法可以不吝赐教哦😄

 

token被盗用怎么办?怎样安全?

token毕竟是服务端返给客户端的,如果被别的端盗用了怎么办?也就是怎么验证当初登陆的人和现在请求接口的人是一个人?

可以用https,如果没有https呢?

本来想着token的value存着信息,如果能验证手机号就好了,但是手机号不能传给客户端,所以也就无法回传

后来想了个办法

登陆时将生成的token和ip绑定,后续验证token-ip,若ip变了则将token放到黑名单中失效,这样就保证了安全

 

整体来说比较好用,jwt应该还有其他好的处理,待后续深入研究后补充

 

参考: https://www.jianshu.com/p/99a458c62aa4

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是小酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值