JWT学习

JWT

1,JWT介绍

全名:Json W eb Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。
JWT一般放入是做头部一起进行请求Authorization ,所以跨域资源共(CORS)不会成为问题,因为它不使用cookie。

官网地址

2,JWT组成结构

一个JWT实际上就是一个字符串,由三部分组成头部,载荷,签名
通常情况下

xxxxx.yyyyy.zzzzz

1,头部(Header)

头部通常由两部分组成:令牌的类型(即JWT)和所使用的签名加密算法,例如HMAC SHA256或RSA。

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

然后此json被base64编码,形成JWT的第一部分(可解码)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2,载荷(payload)

载荷就是存放有效信息的地方,这些有效信息包括三部分
1,Registered claims: 注册声明

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

2,Public claims: 公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

3,Private claims:私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息,这个指的就是自定义的claim

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

像admin:true就是自定义的claim,根据自己的验证规则,确定是否有用
然后进行base64编码(可解密),得到JWT的第二部分

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

3,签名(signature)

要创建签名部分,您必须获取编码的Header,编码的payload,secret,然后用Header中指定的算法,三部分组成。
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐 secret(一般是放在服务端,可以理解为密钥) 组合加密,然后就构成了jwt的第三部分

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

形成

TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6I
kpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

3,Java实现JWT(JJWT)

JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache
License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。

3.1 引入依赖

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

3.2 生成不过期的token

1,创建类CreateJwtDemo.java

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/**
 * @Author : FuYu
 * @Despriotion : 生成不过期token
 */
public class CreateJwtDemo {
    public static void main(String[] args) {
        JwtBuilder builder= Jwts.builder().setId("123")
                .setSubject("小白")
                //签发时间
                .setIssuedAt(new Date())
                //签名密钥 (jjwt)加密
                .signWith(SignatureAlgorithm.HS256,"jjwt");
        System.out.println( builder.compact() );
    }
}

控制台

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjIxODB9.txnBHKh7Hf0in0vNwPj8fwho4Ze31Z5lJ1GqUoV5ScM

再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间。

3.3 解析不过期的token

1,创建类ParseJwtDemo.java

/**
 * @Author : FuYu
 * @Despriotion : 解析不过期token
 */
public class ParseJwtDemo {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjIxODB9.txnBHKh7Hf0in0vNwPj8fwho4Ze31Z5lJ1GqUoV5ScM";
        Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(token).getBody();
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        System.out.println("IssuedAt:" + claims.getIssuedAt());
    }
}

前提:要知道密钥,先前密钥是jjwt,所以这里用jjwt做密钥解密
控制台

id:123
subject:小白
IssuedAt:Fri Oct 04 12:09:40 CST 2019

试试把jjwt换成别的密钥,会发现报错

3.4 生成可过期token的

有很多时候,我们并不希望签发的token是永久生效的,所以我们可以为token添加一个过期时间
1,创建CreateJwtDemo2

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/**
 * @Author : FuYu
 * @Despriotion : 生成会过期的的token
 */
public class CreateJwtDemo2 {
    public static void main(String[] args) {
        //当前时间
        long now = System.currentTimeMillis();
        //过期时间为1分钟
        long exp = now + 1000 * 60;
        JwtBuilder builder = Jwts.builder().setId("123")
                .setSubject("小白")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "jjwt")
                //设置过期时间
                .setExpiration(new Date(exp));
        System.out.println(builder.compact());
    }
}

控制台

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjI4OTksImV4cCI6MTU3MDE2Mjk1OX0.pqNSTcAMMiG2kSUFJTIQcu7GEZ9xaCTd18aW3cbQhXM

3.5 解析可过期的token

1,创建ParseJwtDemo2.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author : FuYu
 * @Despriotion : 解析会过期token
 */
public class ParseJwtDemo2 {
    public static void main(String[] args) {
        String compactJws = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjI4OTksImV4cCI6MTU3MDE2Mjk1OX0.pqNSTcAMMiG2kSUFJTIQcu7GEZ9xaCTd18aW3cbQhXM";
        Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(compactJws).getBody();
        System.out.println("id:" + claims.getId());
        System.out.println("subject:" + claims.getSubject());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
        System.out.println("签发时间:" + sdf.format(claims.getIssuedAt()));
        System.out.println("过期时 间:"+sdf.format(claims.getExpiration()));
        System.out.println("当前时间:" + sdf.format(new Date()));
    }
}

控制台

id:123
subject:小白
签发时间:2019‐10‐04 12:21:39
过期时 间:2019‐10‐04 12:22:39
当前时间:2019‐10‐04 12:22:18

但是等一分钟后再运行,会发现控制台报错,jwt过期报错

Exception in thread "main" io.jsonwebtoken.ExpiredJwtException: JWT expired at 2019-10-04T12:22:39Z. Current time: 2019-10-04T12:23:01Z, a difference of 22250 milliseconds.  Allowed clock skew: 0 milliseconds.
	at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:385)
	at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
	at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
	at com.fy.ParseJwtDemo2.main(ParseJwDemo2.java:16)

3.6 生成自定义claims的token

1,创建CreateJwtDemo3.java

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/**
 * @Author : FuYu
 * @Despriotion : 自定义claims
 */
public class CreateJwtDemo3 {
    public static void main(String[] args) {
        //当前时间
        long now = System.currentTimeMillis();
        //过期时间为1分钟
        long exp = now + 1000 * 60;
        JwtBuilder builder = Jwts.builder().setId("123")
                .setSubject("小白")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, "jjwt")
                .setExpiration(new Date(exp))
                //自定义claim
                .claim("roles", "admin")
                .claim("logo", "logo.png");
        System.out.println(builder.compact());
    }
}

控制台

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjMyMDgsImV4cCI6MTU3MDE2MzI2OCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9._8YdsqBQSW1ctNmoXVTVpGWyYm8wsF-6Pt2ZKbXfyrk

3.7 解析自定义claims的token

1,创建ParseJwtDemo3.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author : FuYu
 * @Despriotion : 解析自定义的claims token
 */
public class ParseJwtDemo3 {
    public static void main(String[] args) {
        String compactJws="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjMiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1NzAxNjMyMDgsImV4cCI6MTU3MDE2MzI2OCwicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9._8YdsqBQSW1ctNmoXVTVpGWyYm8wsF-6Pt2ZKbXfyrk";
        Claims claims = Jwts.parser().setSigningKey("jjwt").parseClaimsJws(compactJws).getBody();
        System.out.println("id:"+claims.getId());
        System.out.println("subject:"+claims.getSubject());
        System.out.println("roles:"+claims.get("roles"));
        System.out.println("logo:"+claims.get("logo"));
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss");
        System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
        System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
        System.out.println("当前时间:"+sdf.format(new Date()) );
    }
}

控制台

id:123
subject:小白
roles:admin
logo:logo.png
签发时间:2019‐10‐04 12:26:48
过期时间:2019‐10‐04 12:27:48
当前时间:2019‐10‐04 12:27:47

注意:如果claim中没有要取得值,会返回null,而不是报错

JWT工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

/**
 * jwt工具类
 *
 * @author FuYu
 */
public class JwtUtil {
	//密钥
    private String key;

    /**
     * 一个小时
     */
    private long ttl;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public long getTtl() {
        return ttl;
    }

    public void setTtl(long ttl) {
        this.ttl = ttl;
    }

    /**
     * 生成JWT
     *
     * @param id
     * @param subject
     * @return
     */
    public String createJWT(String id, String subject, String roles) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        JwtBuilder builder = Jwts.builder().setId(id)
                .setSubject(subject)
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256, key);
        if (ttl > 0) {
            builder.setExpiration(new Date(nowMillis + ttl));
        }
        return builder.compact();
    }

    /**
     * 解析JWT
     *
     * @param jwtStr
     * @return
     */
    public Claims parseJWT(String jwtStr) {
        return Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwtStr)
                .getBody();
    }
}

总结:jwt用了很长一段时间得,但是一直没有写笔记,总觉得很简单,事实上也确实是很简单。

网上找资料,自己写demo,会有进步的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值