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篇博客

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 02_Hadoop集群环境的建立

    02_Hadoop集群环境的建立

    2021年8月22日
    57
  • 服务器对cpu的性能要求比较高_服务器cpu家用缺点

    服务器对cpu的性能要求比较高_服务器cpu家用缺点如今各种大型游戏对我们的电脑性能的要求越来越高,很多用了三四年的老电脑已经带不动最新的3A巨作了,这时候大家就需要考虑更换电脑或者是升级配置了。假如选择升级配置,效果最显著的就要数更换CPU和显卡了,今天编者就来谈谈由于种种原因,性价比很低几款CPU,假如大家想要更换以下的几款CPU可要三思了。首先,编者不推荐买英特尔的第六、七代酷睿处理器,以i57500为例,散片价格在1200元左右,与同为四…

    2026年1月28日
    9
  • Cannot return from outside a function or method.

    Cannot return from outside a function or method.Cannot return from outside a function or method.

    2022年4月25日
    46
  • javaweb项目图书管理系统教程(图书管理系统java课程设计)

    版权声明:本文为博主原创文章,未经博主允许不得转载2019.5.22更新看到很多人看这个项目我也没想到,不过我现在不在CSDN写文章了,博客地址链接←这是我的博客地址链接GitHub地址链接←这是我的github地址链接里面有我学习Java的过程以及笔记,希望大家一起交流。由于刚刚学习完JSP和Servlet在学习框架之前下你给更加巩固一下前面的知识所以写…

    2022年4月15日
    41
  • php网页设计导航栏代码,总结7种常见的导航条制作实例

    php网页设计导航栏代码,总结7种常见的导航条制作实例导航条是网页设计中不可缺少的部分,它是指通过一定的技术手段,为网站的访问者提供一定的途径,使其可以方便地访问到所需的内容,是人们浏览网站时可以快速从一个页面转到另一个页面的快速通道。利用导航条,我们就可以快速找到我们想要浏览的页面。今天分享一下简单导航栏的制作方法:第一步:引入css样式表,新建一个id为nav的层,使用、、标签来制作完成效果。这篇文章主要为大家详细介绍了微信小程序实战之顶部导航栏…

    2022年7月22日
    17
  • oracle更改用户的密码

    oracle更改用户的密码第一种情况,不知道该用户的密码,以管理员身份或者其他有权限的用户更改。1、以system或者sys的身份登录。登录语句sqlplus system/psw@ora_name或者sqlplus  sys/psw@ora_name assysdba。2、alter语句修改用户user1的密码。alter user  user1 identified bynew_psw;3

    2022年7月28日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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