利用JWT生成Token

开篇

实现Token的方式有很多,本篇介绍的是利用Json Web Token(JWT)生成的Token.JWT生成的Token有什么好处呢?

  • 安全性比较高,加上密匙加密而且支持多种算法。
  • 携带的信息是自定义的,而且可以做到验证token是否过期。
  • 验证信息可以由前端保存,后端不需要为保存token消耗内存。

本篇分3部分进行讲解。

    1. 什么是JWT
    1. JWT的代码实现
      • 用HS256 对称算法加密
      • 用RS256 非对称算法加密
    1. 总结

如果原理很难懂,没关系。可以直接看JWT的代码实现。代码已经上传github。已经对代码进行封装成工具类。可以直接使用。

什么是JWT

JSON Web Token 简称JWT。
一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名
JWT生成的token是这样的

eyJpc3MiOiJKb2huI.eyJpc3MiOiJ.Kb2huIFd1IEp

生成的token,是3段,用.连接。下面有解释。

头部

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

{
   "typ": "JWT",
  "alg": "HS256"
}

载荷

其实就是自定义的数据,一般存储用户Id,过期时间等信息。也就是JWT的核心所在,因为这些数据就是使后端知道此token是哪个用户已经登录的凭证。而且这些数据是存在token里面的,由前端携带,所以后端几乎不需要保存任何数据。
例如:

{
  "uid": "xxxxidid",  //用户id
  "exp": "12121212"  //过期时间
}

签名

签名其实就是:
1.头部和载荷各自base64加密后用.连接起来,然后就形成了xxx.xx的前两段token。
2.最后一段token的形成是,前两段加入一个密匙用HS256算法或者其他算法加密形成。

  1. 所以token3段的形成就是在签名处形成的。

JWT的原理参考文章

代码实现

1.看代码前一定要知道JWT是由头部载荷签名组成。
2.代码已上传github,希望点个赞

  1. 代码将JWT封装成两个工具类,可以直接调用。

需要下载的jar包


            com.nimbusds
            nimbus-jose-jwt
            6.0

HS256 对称加密

生成token

 /**
     * 1.创建一个32-byte的密匙
     */

    private static final byte[] secret = "geiwodiangasfdjsikolkjikolkijswe".getBytes();


    //生成一个token
    public static String creatToken(Map payloadMap) throws JOSEException {

        //3.先建立一个头部Header
        /**
         * JWSHeader参数:1.加密算法法则,2.类型,3.。。。。。。。
         * 一般只需要传入加密算法法则就可以。
         * 这里则采用HS256
         *
         * JWSAlgorithm类里面有所有的加密算法法则,直接调用。
         */
        JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);

        //建立一个载荷Payload
        Payload payload = new Payload(new JSONObject(payloadMap));

        //将头部和载荷结合在一起
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);

        //建立一个密匙

        JWSSigner jwsSigner = new MACSigner(secret);

        //签名
        jwsObject.sign(jwsSigner);

        //生成token
        return jwsObject.serialize();
    }

验证token

 //解析一个token

    public static Map valid(String token) throws ParseException, JOSEException {

//        解析token

        JWSObject jwsObject = JWSObject.parse(token);

        //获取到载荷
        Payload payload=jwsObject.getPayload();

        //建立一个解锁密匙
        JWSVerifier jwsVerifier = new MACVerifier(secret);

        Map resultMap = new HashMap<>();
        //判断token
        if (jwsObject.verify(jwsVerifier)) {
            resultMap.put("Result", 0);
            //载荷的数据解析成json对象。
            JSONObject jsonObject = payload.toJSONObject();
            resultMap.put("data", jsonObject);

            //判断token是否过期
            if (jsonObject.containsKey("exp")) {
                Long expTime = Long.valueOf(jsonObject.get("exp").toString());
                Long nowTime = new Date().getTime();
                //判断是否过期
                if (nowTime > expTime) {
                    //已经过期
                    resultMap.clear();
                    resultMap.put("Result", 2);

                }
            }
        }else {
            resultMap.put("Result", 1);
        }
        return resultMap;

    }

调用的业务逻辑

//生成token的业务逻辑
    public static String TokenTest(String uid) {
        //获取生成token

        Map map = new HashMap<>();

        //建立载荷,这些数据根据业务,自己定义。
        map.put("uid", uid);
        //生成时间
        map.put("sta", new Date().getTime());
        //过期时间
        map.put("exp", new Date().getTime()+6);
        try {
            String token = TokenUtils.creatToken(map);
            System.out.println("token="+token);
            return token;
        } catch (JOSEException e) {
            System.out.println("生成token失败");
            e.printStackTrace();
        }
        return null;

    }

    //处理解析的业务逻辑
    public static void ValidToken(String token) {
        //解析token
        try {
            if (token != null) {

                Map validMap = TokenUtils.valid(token);
                int i = (int) validMap.get("Result");
                if (i == 0) {
                    System.out.println("token解析成功");
                    JSONObject jsonObject = (JSONObject) validMap.get("data");
                    System.out.println("uid是" + jsonObject.get("uid"));
                    System.out.println("sta是"+jsonObject.get("sta"));
                    System.out.println("exp是"+jsonObject.get("exp"));
                } else if (i == 2) {
                    System.out.println("token已经过期");
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (JOSEException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] ages) {
        //获取token
        String uid = "kkksuejrmf";
        String token = TokenTest(uid);
        //解析token
        ValidToken(token);
    }

RS256 非对称加密

生成加密密钥

/**
     * 创建加密key
     */
    public static RSAKey getKey() throws JOSEException {
        RSAKeyGenerator rsaKeyGenerator = new RSAKeyGenerator(2048);
        RSAKey rsaJWK = rsaKeyGenerator.generate();
        return rsaJWK;
    }

生成token

public static String creatTokenRS256(Map payloadMap,RSAKey rsaJWK) throws JOSEException {

        //私密钥匙
        JWSSigner signer = new RSASSASigner(rsaJWK);

        JWSObject jwsObject = new JWSObject(
                new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(rsaJWK.getKeyID()).build(),
                new Payload(new JSONObject(payloadMap))
        );
        //进行加密
        jwsObject.sign(signer);

        String token= jwsObject.serialize();
        return token;
    }

解析token

  //验证token
    public static Map validRS256(String token,RSAKey rsaJWK) throws ParseException, JOSEException {
        //获取到公钥
        RSAKey rsaKey = rsaJWK.toPublicJWK();
        JWSObject jwsObject = JWSObject.parse(token);
        JWSVerifier jwsVerifier = new RSASSAVerifier(rsaKey);
        //验证数据
        return verify(jwsObject, jwsVerifier);
    }

总结

JWT 的实践其实还是挺简单。安全性也是得到了保证,后端只需要保存着密匙,其他数据可以保存在token,由前端携带,这样可以减低后端的内存消耗。
虽然token是加密的,但是携带的验证数据还是不要是敏感数据

 

你可能感兴趣的:(java)