Jwt入门教程:实战( 二) | Java/.Net/Python中的使用

Jwt在Java/Python/.Net中的使用


本文着重讲解在Java和Python中Jwt的使用,Jwt的加密/解密,同时能Java能解密Python的token。


如果对JWT还不理解的请参考第一篇:Jwt入门教程( 一) | 原理和用法.

一、Java中的使用


这里使用 jjwt的方式
首先引入maven依赖

<dependency>  
    <groupid>io.jsonwebtoken</groupid>  
    <artifactid>jjwt</artifactid>  
    <version>0.7.0</version>  
</dependency>


1.1、Java代码:(第一版由于没有很多的时间去规范,最近规范整理了一下代码。第一版写的比较细一点,大家可以参考一下,因为注释的比较多。)

Java第二版代码如下:(第一版为了大家的方便,放再最后。)1


import io.jsonwebtoken.*;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 慌途L
 */
@Component
public class JwtUtilsHelper {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtilsHelper.class);

    private static final String SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";
    private static final String SECRET_KEY_REF = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";

    private static Integer ACCESS_EXPIRES = 60 * 60 * 4;  //access_token有效时间,4hour
    private static Integer REFRESH_EXPIRES = 60 * 60 * 8;  //refresh_token有效时间,8hour

    private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;//指定签名的时候使用的签名算法

    /**
     *  获取加密key
     * @param keys     过期时间变量
     * @return
     */
    private static Key getEncryptedKey(String keys){
        Key key = new SecretKeySpec(keys.getBytes(), signatureAlgorithm.getJcaName());
        return key;
    }

    /**
     * 生成Jwt Token
     * @param data          JWT payload负载(自己需要封装的参数放这里,json格式的字符串)
     * @param expiresDate   过期时间
     * @param secretKey     签名的秘钥,和生成的签名的秘钥一模一样
     * @return
     */
    private static String createToken(String data, Integer expiresDate, String secretKey){
        /** header  BEGIN */
        Map<String,Object> header = new HashMap<String, Object>();//JWT header头部
        long time = new Date().getTime();
        header.put("alg","HS512");//表示签名的算法,默认也是这个,可选
        header.put("iat", time/1000);//签发时间(除以1000,得到十位毫秒值,去除微秒,因为python是十位)
        header.put("exp", (time/1000) + expiresDate);//过期时间
        /** header  END */

        JwtBuilder builder = Jwts.builder()
                .setHeader(header)          //new一个JwtBuilder,设置jwt的body
                .setPayload(data)         //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的payload赋值,
                // 一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .signWith(signatureAlgorithm, getEncryptedKey(secretKey));//设置签名使用的签名算法和签名使用的秘钥
        return builder.compact();           //开始压缩为xxx.xxx.xxx格式的jwt
    }

    /**
     * 解密Jwt得到payload负载的数据
     * @param jwt
     * @param secretKey     签名的秘钥,和生成的签名的秘钥一模一样
     * @return
     */
    private static Claims decodeTokenPart(String jwt, String secretKey){
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(getEncryptedKey(secretKey))
                    .parseClaimsJws(jwt).getBody();
            return claims;
        } catch (Exception e) {
            logger.debug("validate is Jwt token payload error :", e);
            return null;
        }
    }

    /**
     * 解密Jwt得到全部数据
     * @param jwt
     * @param secretKey     签名的秘钥,和生成的签名的秘钥一模一样
     * @return
     */
    private static Jwt decodeTokenAll(String jwt, String secretKey){
        try {
            Jwt parse = Jwts.parser()
                    .setSigningKey(getEncryptedKey(secretKey))
                    .parse(jwt);
            return parse;
        } catch (Exception e) {
            logger.debug("validate is Jwt token error :", e);
            return null;
        }
    }


    /**
     * 生成Jwt token
     * @param data   JWT payload负载
     * @return
     */
    public static String accessTonken(String data) {
        return createToken(data, ACCESS_EXPIRES, SECRET_KEY);
    }

    /**
     * 生成Jwt token
     * @param data   JWT payload负载
     * @return
     */
    public static String refreshToken(String data) {
        return createToken(data, REFRESH_EXPIRES, SECRET_KEY_REF);
    }

    /**
     * 解密JWT  (得到包含header,payload,signature全部数据)
     * @param jwt
     * @return
     */
    public static Jwt accessTonkenDecodeAll(String jwt) {
        return decodeTokenAll(jwt, SECRET_KEY);
    }

    /**
     * 解密JWT  (得到包含header,payload,signature全部数据)
     * @param jwt
     * @return
     */
    public static Jwt refreshTokenDecodeAll(String jwt) {
        return decodeTokenAll(jwt, SECRET_KEY_REF);
    }


    /**
     * 解密jwt    (得到payload部分的数据)
     *
     * @param jwt
     * @return
     */
    public static Claims accessTonkenDecodePart(String jwt) {
        return decodeTokenPart(jwt, SECRET_KEY);
    }

    /**
     * 解密jwt    (得到payload部分的数据)
     *
     * @param jwt
     * @return
     */
    public static Claims refreshTokenDecodePart(String jwt){
        return decodeTokenPart(jwt, SECRET_KEY_REF);
    }


    /**
     * 判断token是否过期
     *
     * @param expiration    token过期时间
     * @return true:过期  false:未过期
     */
    public static boolean isTokenExpired(Long expiration){
        long nowDate = new Date().getTime() / 1000;
        return (nowDate > expiration) ? true : false;
    }


    public static void main(String[] args) {

        /** payload  BEGIN */
        int userId = 169061;
        String userName = "admin";
        String ip = "192.168.110.555";
        Map<String,Object> payload = new HashMap<String, Object>();
        payload.put("user_id",userId);
        payload.put("user_name",userName);
        payload.put("ip",ip);
        /** payload  END */


        //得到jwt
        String jwt = accessTonken(JSONObject.fromObject(payload).toString());
        String jwt2 = refreshToken(JSONObject.fromObject(payload).toString());
        System.out.println(jwt);
        System.out.println(jwt2);
        System.out.println("\n");

        //解析jwt
        Jwt parseJwt = accessTonkenDecodeAll(jwt);//四小时过期
        Jwt parseJwt2 = refreshTokenDecodeAll(jwt2);//八小时过期

        System.out.println("parseJwt=="+parseJwt);

        System.out.println("parseJwt2=="+parseJwt2);
    }
}


1.2、输出结果:

jwt==eyJleHAiOjE1NDI4NTAyMTUsImlhdCI6MTU0Mjg1MDIwMSwiYWxnIjoiSFM1MTIifQ.eyJ1c2VyX2lkIjoxNjkwNjEsInVzZXJfbmFtZSI6ImFkbWluIiwiaXAiOiIxOTIuMTY4LjExMC41NTUifQ.-BGabALIWolNBVqCsjwsmC-lE7AnJYe20MXWK2wEvbCGHdEiX9UStvmHguadyiSlksJmqghNHzLA4KcRuqnniw

claims=={user_id=169061, user_name=admin, ip=192.168.110.555}
parseJwt==header={exp=1542850215, iat=1542850201, alg=HS512},body={user_id=169061, user_name=admin, ip=192.168.110.555},signature=-BGabALIWolNBVqCsjwsmC-lE7AnJYe20MXWK2wEvbCGHdEiX9UStvmHguadyiSlksJmqghNHzLA4KcRuqnniw



解码网址:https://www.sojson.com/base64.html

1.2.1 jwt
1.2.2 jwt的header:

eyJleHAiOjE1NDI4NTAyMTUsImlhdCI6MTU0Mjg1MDIwMSwiYWxnIjoiSFM1MTIifQ

1.2.3 解密结果:

在这里插入图片描述

1.2.4 jwt的payload:

eyJ1c2VyX2lkIjoxNjkwNjEsInVzZXJfbmFtZSI6ImFkbWluIiwiaXAiOiIxOTIuMTY4LjExMC41NTUifQ

1.2.5 解密结果:

在这里插入图片描述


二、Python中的使用


2.1、Python中的代码:

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer


def generate_auth_token(key, data, expiration=60 * 60 * 2):
    """获取token"""
    # data = {'user_id': user_id}
    s = Serializer(key, expires_in=expiration)
    return s.dumps(data).decode('utf-8')


if __name__ == "__main__":
    SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe" #key

    ACCESS_EXPIRES = 60 * 60 * 4  # access_token有效时间,4hour
   
    
    user_id = 169061
    user_name = 'admin'
    ip = '192.168.110.555'
    access_tonken = generate_auth_token(
        key=SECRET_KEY,
        data={'user_id': user_id, 'user_name': user_name, 'ip': ip},#封装进payload的数据
        expiration=ACCESS_EXPIRES #过期时间设置
    )
   
    print("%s\n%s" % (access_tonken))

    quit(0)

2.2、输出:格式为:header.payload.signature(用"."分割)
eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODE4MjcyfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.klw-jeFDpN_WVq4VkYmkYsHNrxErbmrnBBUdgGLRfLW_5gTnIdT1-8p4i_ZDAM6BloD0DVcZOuazSLtMrz0NdQ
eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODMyNjcyfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.vRBhW37d5jck7PocGX7O9usZ96XsxKytWkFqdbflcfKeAXfqHTxul78Z3JSB4YkFL93VsW8eJiV9H3GPnz6Usw


2.3 可以到网上进行在线解码:解码网址:https://www.sojson.com/base64.html
2.3.1 比如第一条的header:

eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0MjgwMzg3MiwiZXhwIjoxNTQyODE4MjcyfQ

2.3.2 解密结果:

在这里插入图片描述


2.4.1 比如第一条的payload:

eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25h
bWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ

2.4.1 解密结果:

在这里插入图片描述


注:由于python中输出的时间戳是十位的毫秒值,而Java中时间戳为13位,所以注意转换。


三、.Net中的使用


个人没有涉及到,可以参考:https://www.cnblogs.com/byxxw/p/6742230.html

此文写的比较简单,各位可以多到网上参考参考。


四、最后,希望本文能为大家解惑!

可能描述和注释的思维比较跳,有不理解和写的不对的地方可以留言,及时更正


下一篇:Jwt入门教程:实战( 三) | 基于拦截器的Jwt判断用户登录以及安全校验.

下面是第一版Java代码:


package test;

import io.jsonwebtoken.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * @author 慌途L
 */
public class JWTTest {

    private static final Logger logger = LoggerFactory.getLogger(JWTTest.class);
    
	/**
     * 秘钥key(跟python一样)
     */
    private static String SECRET_KEY = "adsfkoemejn13443#@^*(%$-=4=+*&%fe";
    
	/**
     * access_token有效时间,4hour
     */
    private static Integer ACCESS_EXPIRES = 60 * 60 * 4;  

	/**
     * 使用什么加密算法,这里我使用HMacSHA512,Jwt默认HMacSHA256
     */
    private static final  String MAC_INSTANCE_NAME = "HMacSHA512";
    


    /**
     *  获取加密key
     * (为什么还要加密key呢?)
     * 因为Python里面已经将它加密再做的签名
     * (这里被坑了一把)
     * @return
     */
    public static SecretKey getEncryptedKey(String keys){
        SecretKey key = new SecretKeySpec(keys.getBytes(), MAC_INSTANCE_NAME);
        return key;
    }


    /**
     * 创建jwt
     * @param payload   JWT payload负载数据
     * @return
     * @throws Exception
     */
    public static String accessTonken(Map<String,Object> payload) throws Exception {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512; //指定签名的时候使用的签名算法

        /** header  BEGIN */
        /** 
         * 这里我覆盖了默认的签发时间和过期时间,所以下面异常里的token过期不会提示
         */
        Map<String,Object> header = new HashMap<String, Object>();//JWT header头部
        long time = new Date().getTime();
        header.put("alg","HS512");//表示签名的算法,默认也是这个,可选
        header.put("iat", time/1000);//签发时间(除以1000,得到十位毫秒值,去除微秒,因为python是十位)
        header.put("exp", (time/1000) + expiresDate);//过期时间
        /** header  END */

        JwtBuilder builder = Jwts.builder()
                .setHeader(header)          //new一个JwtBuilder,设置jwt的body
//                .setIssuedAt(now) //签发时间(去除默认的设置体位置,我在header  BEGIN里面封装了 )
                .setClaims(payload)         //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .signWith(signatureAlgorithm, getEncryptedKey(SECRET_KEY));//设置签名使用的签名算法和签名使用的秘钥
//        builder .setExpiration(new Date(nowMillis + 1));     //设置过期时间(去除默认的设置体位置,我在header  BEGIN里面封装了 )

        return builder.compact();           //转为xxx.xxx.xxx格式的jwt
    }


    /**
     * 解密jwt
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT_Claims(String jwt) throws Exception{
        Claims claims = Jwts.parser()  //得到DefaultJwtParser
                .setSigningKey(getEncryptedKey(SECRET_KEY)) //设置签名的秘钥,和生成的签名的秘钥一模一样
                .parseClaimsJws(jwt).getBody();//设置需要解析的jwt,body相对于payload
        return claims;
    }


    /**
     * 解密jwt
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Jwt parseJWT(String jwt) throws Exception{
        Jwt parse = Jwts.parser()   //得到DefaultJwtParser
                .setSigningKey(getEncryptedKey(SECRET_KEY)) //设置签名的秘钥,和生成的签名的秘钥一模一样
                .parse(jwt);    //设置需要解析的jwt
        return parse;
    }


	

    public static void main(String[] args) {

        /** payload  BEGIN */
        //封装数据
        int userId = 169061;
        String userName = "admin";
        String ip = "192.168.110.555";
        Map<String,Object> payload = new HashMap<String, Object>();
        payload.put("user_id",userId);
        payload.put("user_name",userName);
        payload.put("ip",ip);
        /** payload  END */

        String jwt = null;
        try {
            jwt = accessTonken(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("jwt=="+jwt);

        /** python中生成的数据  进行java解密 */
        String jwt2 = "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0Mjc5MzcxOSwiZXhwIjoxNTQyNzkzNzIwfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.4ozHmr8PcxEFBchU10_27VSNob1iWLcttguTQk8FPTPtSfR2G4-jSWVuEVOrJESyES-7iza8f1nJb4vCc-UTKA";
        String jwt3 = "eyJhbGciOiJIUzUxMiIsImlhdCI6MTU0Mjc5OTE4MywiZXhwIjoxNTQyODEzNTgzfQ.eyJ1c2VyX2lkIjoxMDAxLCJ1c2VyX25hbWUiOiJhZG1pbiIsImlwIjoiMTkyLjE2OC4xMC4yMDMifQ.sPcjFHbdq9PxAOhDsfq1TOYINB-tkLnXQOSz4KSkgZkkMt74TlgX8stBzpgyRTNihq8QadOyGscYHnebiu5WGQ";

        /** payload中的数据 */
        Claims claims = null;
        /** 整个jwt的数据,包括header,payload ,signature*/
        Jwt parseJwt = null;
        try {
            claims = parseJWT_Claims(jwt);
            parseJwt = parseJWT(jwt);
        } catch (Exception e) {
            /**
             * 如果使用默认的builder .setExpiration(new Date(nowMillis + 1))方法可生效
             * 如果报错,则是token过期
             * 前提是不能只能用上面的builder.setIssuedAt(now)方法和builder .setExpiration(new Date(nowMillis + 1))方法
             * 不能覆盖
             * 如果覆盖,则需要自己解析header里面的封装参数
             * (这里过期时间我是放在header里面,如果用了自带的方法,则是在payload里面)
             */
            System.out.println("token过期");//使用默认过期方法才有效
            e.printStackTrace();
        }
        System.out.println("claims=="+claims);
        System.out.println("parseJwt=="+parseJwt);

    }

}


下一篇:Jwt入门教程:实战( 三) | 基于拦截器的Jwt判断用户登录以及安全校验.

欢迎关注公众号:慌途L
后面会慢慢将文章迁移至公众号,也是方便在没有电脑的情况下可以进行翻阅,更新的话会两边同时更新,大家不用担心!
在这里插入图片描述



  1. 第一版 ↩︎

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值