JWT单点登录(源码学习)

JWT单点登录(源码学习)三、JWT源码学习//登录成功之后,需要生成tokenStringtoken=Jwts.builder().setSubject(“用户名/用户信息”)//主题,可以放用户的详细信息.setIssuedAt(newDate())//token创建时间.setExpiration(newDate(System.currentTimeMillis()+60000))//token过期时间.setId(“用户ID”)//用户ID

大家好,又见面了,我是你们的朋友全栈君。

你好我是辰兮,很高兴你能来阅读,本篇给是初学JWT单点登录后的困惑,现在源码学习后更加了解JWT的结构,小结下来,献给初学者,共同成长,一起进步。



一、JWT的结构

JWT的结构是什么样的?
在这里插入图片描述
token构成包含三个部分:

  • Header 头部
  • Payload 负载
  • Signature 签名

在这里插入图片描述

//格式如下
xxx.yyy.zzz

在这里插入图片描述
①JWT-header(头信息)

由两部分组成,令牌类型(即:JWT)散列算法(HMAC、RSASSA、RSASSA-PSS等)

JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

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

然后,这个JSON被编码为Base64Url,形成JWT的第一部分。


②有效载荷payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。

JWT的第二部分是payload,其中包含claims。claims是关于实体(常用的是用户信息)和其他数据的声明

claims有三种类型: registered, public, and private claims。

  • Registered claims: 这些是一组预定义的claims,非强制性的,但是推荐使用, iss(发行人), exp(到期时间),sub(主题), aud(观众)等;
  • Public claims: 自定义claims,注意不要和JWT注册表中属性冲突,这里可以查看JWT注册表;
  • Private claims:这些是自定义的claims,用于在同意使用这些claims的各方之间共享信息,它们既不是Registered claims,也不是Public claims;
{ 
   
  "sub": "1234567890",
  "name": "Tom",
  "admin": true
}

请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

JSON对象也使用Base64 URL算法转换为字符串保存。


③签名哈希

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SfcKxwRJSMeKF2QT4fwpMeJf36POkayJV_adQssw6f

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用”.”分隔,就构成整个JWT对象。


④Base64URL算法

如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。

作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是”+”,”/“和”=”,由于在URL中有特殊含义,因此Base64URL中对他们做了替换:”=“去掉,”+“用”-“替换,”/“用”_”替换,这就是Base64URL算法。


二、JWT源码学习

  • 参考一下常见的代码初学者可能看上去很复杂,接下来分析一波
//登录成功之后,需要生成token
String token = Jwts.builder().setSubject("用户名/用户信息") //主题,可以放用户的详细信息
        .setIssuedAt(new Date()) //token创建时间
        .setExpiration(new Date(System.currentTimeMillis() + 60000)) //token过期时间
        .setId("用户ID") //用户ID
        .setClaims(hashMap) //配置角色信息
        .signWith(SignatureAlgorithm.HS256, "WuHan") //加密方式和加密密码
        .compact();
  • 首先Jwts是Java已经被人写好的一个被final修饰的类,然后里面自带很多方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.jsonwebtoken;

import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.impl.DefaultHeader;
import io.jsonwebtoken.impl.DefaultJwsHeader;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import io.jsonwebtoken.impl.DefaultJwtParser;
import java.util.Map;

public final class Jwts { 
   
    private Jwts() { 
   
    }

    public static Header header() { 
   
        return new DefaultHeader();
    }

    public static Header header(Map<String, Object> header) { 
   
        return new DefaultHeader(header);
    }

    public static JwsHeader jwsHeader() { 
   
        return new DefaultJwsHeader();
    }

    public static JwsHeader jwsHeader(Map<String, Object> header) { 
   
        return new DefaultJwsHeader(header);
    }

    public static Claims claims() { 
   
        return new DefaultClaims();
    }

    public static Claims claims(Map<String, Object> claims) { 
   
        return new DefaultClaims(claims);
    }

    public static JwtParser parser() { 
   
        return new DefaultJwtParser();
    }

    public static JwtBuilder builder() { 
   
        return new DefaultJwtBuilder();
    }
}

  • JwtBuilder是一个接口,里面也自带很多方法,每一个方法的返回值类型是自己本身,这里就是给用户设置相关信息的,所以代码你看上去是连着set的。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.jsonwebtoken;

import java.security.Key;
import java.util.Date;
import java.util.Map;

public interface JwtBuilder extends ClaimsMutator<JwtBuilder> { 
   
    JwtBuilder setHeader(Header var1);

    JwtBuilder setHeader(Map<String, Object> var1);

    JwtBuilder setHeaderParams(Map<String, Object> var1);

    JwtBuilder setHeaderParam(String var1, Object var2);

    JwtBuilder setPayload(String var1);

    JwtBuilder setClaims(Claims var1);

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

    JwtBuilder addClaims(Map<String, Object> var1);

    JwtBuilder setIssuer(String var1);

    JwtBuilder setSubject(String var1);

    JwtBuilder setAudience(String var1);

    JwtBuilder setExpiration(Date var1);

    JwtBuilder setNotBefore(Date var1);

    JwtBuilder setIssuedAt(Date var1);

    JwtBuilder setId(String var1);

    JwtBuilder claim(String var1, Object var2);

    JwtBuilder signWith(SignatureAlgorithm var1, byte[] var2);

    JwtBuilder signWith(SignatureAlgorithm var1, String var2);

    JwtBuilder signWith(SignatureAlgorithm var1, Key var2);

    JwtBuilder compressWith(CompressionCodec var1);

    String compact();
}

JWT的解析

try { 
   
    JwtParser parser = Jwts.parser();
    parser.setSigningKey("WuHan");//解析 要和上面“暗号”一样
    Jws<Claims> claimsJws = parser.parseClaimsJws(token);
    Claims body = claimsJws.getBody();
    String username = body.getSubject();
  // Object role = body.get("role");

    return true;
} catch (ExpiredJwtException e) { 
   
    e.printStackTrace();
} catch (UnsupportedJwtException e) { 
   
    e.printStackTrace();
} catch (MalformedJwtException e) { 
   
    e.printStackTrace();
} catch (SignatureException e) { 
   
    e.printStackTrace();
} catch (IllegalArgumentException e) { 
   
    e.printStackTrace();
}

setSigningKey() 与builder中签名方法signWith()对应,parser中的此方法拥有与signWith()方法相同的三种参数形式,用于设置JWT的签名key,用户后面对JWT进行解析。

package io.jsonwebtoken;

import java.security.Key;
import java.util.Date;

public interface JwtParser { 
   
    char SEPARATOR_CHAR = '.';

    JwtParser requireId(String var1);

    JwtParser requireSubject(String var1);

    JwtParser requireAudience(String var1);

    JwtParser requireIssuer(String var1);

    JwtParser requireIssuedAt(Date var1);

    JwtParser requireExpiration(Date var1);

    JwtParser requireNotBefore(Date var1);

    JwtParser require(String var1, Object var2);

    JwtParser setClock(Clock var1);

    JwtParser setAllowedClockSkewSeconds(long var1);

    JwtParser setSigningKey(byte[] var1);

    JwtParser setSigningKey(String var1);

    JwtParser setSigningKey(Key var1);

    JwtParser setSigningKeyResolver(SigningKeyResolver var1);

    JwtParser setCompressionCodecResolver(CompressionCodecResolver var1);

    boolean isSigned(String var1);

    Jwt parse(String var1) throws ExpiredJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

    <T> T parse(String var1, JwtHandler<T> var2) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

    Jwt<Header, String> parsePlaintextJwt(String var1) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

    Jwt<Header, Claims> parseClaimsJwt(String var1) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

    Jws<String> parsePlaintextJws(String var1) throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

    Jws<Claims> parseClaimsJws(String var1) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
}

Claims认证很难解释清楚,甚至我都找不到一个合适的中文词语来翻译它。只好用一个比喻来说明。

  • 在实行社保卡之前,我们去医院看病的时候,需要拿着身份证去办理一张就诊卡,办卡的工作人员校验完你的身份证以后,会将你的个人信息录入到卡里面。当你去找医生就诊的时候,医生扫描一下你的就诊卡,就知道了你的所有信息。这个就诊卡就相当于Claims认证中的token,里面的每条信息就是一个Claim.
package io.jsonwebtoken;

import java.util.Date;
import java.util.Map;

public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> { 
   
    String ISSUER = "iss";
    String SUBJECT = "sub";
    String AUDIENCE = "aud";
    String EXPIRATION = "exp";
    String NOT_BEFORE = "nbf";
    String ISSUED_AT = "iat";
    String ID = "jti";

    String getIssuer();

    Claims setIssuer(String var1);

    String getSubject();

    Claims setSubject(String var1);

    String getAudience();

    Claims setAudience(String var1);

    Date getExpiration();

    Claims setExpiration(Date var1);

    Date getNotBefore();

    Claims setNotBefore(Date var1);

    Date getIssuedAt();

    Claims setIssuedAt(Date var1);

    String getId();

    Claims setId(String var1);

    <T> T get(String var1, Class<T> var2);
}

上述Claims接口中定义了一些变量,我找到了相关图片给你们参考

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

在这里插入图片描述


三、JWT 的特点小结

在这里插入图片描述

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。


The best investment is to invest in yourself

在这里插入图片描述

2020.06.04 记录辰兮的第76篇博客

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/143640.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • UDP协议的详细解析「建议收藏」

    UDP协议的详细解析「建议收藏」UDP数据报一、UDP的概述二、UDP的首部格式UDP校验

  • MySQL EXPLAIN type类型说明[通俗易懂]

    MySQL EXPLAIN type类型说明[通俗易懂]EXPLAIN执行计划中type字段分为以下几种:ALL    INDEX    RANGE    REF    EQ_REF    CONST,SYSTEM    NULL自上而下,性能从最差到最好 type=ALL,全表扫描,MYSQL扫描全表来找到匹配的行(因为film表中rating不是索引)mysql&gt;explainexten…

    2022年10月10日
  • Werkzeug库[通俗易懂]

    Werkzeug库[通俗易懂]简介Werkzeug是一个Python写成的WSGI工具集。它遵循WSGI规范,对服务器和Web应用之间的“中间层”进行了开发,衍生出一系列非常有用的Web服务底层模块。关于Werkzeug功能的最简单的一个例子如下:12345678910fromwerkzeug.wrappersimportRequest,…

  • Inno Setup:x64 vs x86

    Inno Setup:x64 vs x86BydefaultInnoSetupalwaysinstallsyourapplicationin32-bitmode.Butifyourapplicationiscompiledto64-bitbinaries(atleast,partly)youwill,probably,wanttouse64-bitmode.Firstthin

  • 什么是泛型编程

    什么是泛型编程1.C++两种抽象方法(1)面向对象编程封装(Encapsulation)继承(Inheritance)多态(Polymorphism)(2)泛型编程概念(concepts)模型化(modeling)强化(refinement)2.泛型编程概念泛型编程(GenericProgramming)指在多种数据类型上皆可操作。和面向对象编程不同,它并不要求额外的间接…

  • C# ACCESS数据库操作类

    这个是针对ACCESS数据库操作的类,同样也是从SQLHELPER提取而来,分页程序的调用可以参考MSSQL那个类的调用,差不多的,只是提取所有记录的数量的时候有多一个参数,这个需要注意一下!推荐:

    2021年12月27日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号