JWT原理与实践

一.JWT概述

1.1.什么是JWT

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,用于在各方之间作为JSON对象安全地传输信息。 此信息可以通过数字签名进行验证和信任。 JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

虽然JWT可以加密以在各方之间提供保密,但我们将专注于签名token。 签名token可以验证其中包含的声明的完整性,而加密token则隐藏其他方的声明。 当使用公钥/私钥对签名token时,签名还证明只有持有私钥的一方是签署它的一方。

2. 何时使用JWT?

  • 授权:这是我们使用JWT最广泛的应用场景。一次用户登录,后续请求将会包含JWT,对于那些合法的token,允许用户连接路由,服务和资源。如今在单独签名上是一种使用JWT的广泛特征。因为他们开销很小并且可以在不通领域轻松使用。

  • 信息交换:JSON Web Token是一种在各方面之间安全信息传输的好的方式 因为JWT可以签名 - 例如,使用公钥/私钥对 - 您可以确定发件人是他们所说的人。 此外,由于使用标头和有效负载计算签名,您还可以验证内容是否未被篡改。

3.JSON Web Token(JWT)由什么组成?

JSON Web Token由三部分组成,以 " . " 分割。

他们是:
Header
Payload
Signature
因此,一个JWT通常以下面这种形式出现。

xxxxx.yyyyy.zzzzz

3.1.Header

标头通常由两部分组成:token的类型,即JWT,以及正在使用的签名算法,例如HMAC SHA256或RSA。

For example:

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

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

3.2.Payload

token的第二部分是payload(有效负载),其中包含claims(声明)。Claims是关于一个实体(通常是用户)和其他数据类型的声名。claims有三种类型:registered,public,and private claims。

  • 已注册的声明:这些是一组预定义声明,不是强制性的,但建议使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行人),exp(到期时间),sub(主题),aud(观众)and others。(请注意,声明名称只有三个字符,因为JWT意味着紧凑。)
  • 公开声明:这些可以由使用JWT的人随意定义。 但为避免冲突,应在IANA JSON Web Token Registry中定义它们,或者将其定义为包含防冲突命名空间的URI。
  • 私人声明:这些声明是为了在同意使用它们的各方之间共享信息而创建的,并且既不是注册声明也不是公开声明。
    An example payload could be:
{
 "sub": "1234567890",
 "name": "John Doe",
 "admin": true
}

3.3.Signature(重点)

要创建签名部分,您必须采用编码header,编码的payload,a secret,标头中指定的算法,并对其进行签名。
for example如果你想使用HMAC SHA256算法,签名会以下面的方式创建:

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

签名用于验证消息在此过程中未被更改,并且,在使用私钥签名的token的情况下,它还可以验证JWT的发件人是否是它所声称的人

好了以上部分都是官方对JWT的介绍,下面来看看身份认证使用session、直接使用token以及JWT的过程

3.4. 身份认证使用session、直接使用token以及JWT的流程

3.4.1.基于session身份认证的方案

在这里插入图片描述

3.4.2.基于token份认证的方案

在这里插入图片描述

3.4.3.基于JWT身份认证的方案

在这里插入图片描述

二.JWT 简单实用演示介绍

1.引入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.1</version>
</dependency>

我们通过三个方法来演示如何使用JWT

1.createToken() 创建不携带自定义信息的 token
2.createTokenWithClaim() 创建携带自定义信息的 token
3.verifyToken() 验证我们的token信息并解析token中的内容

2.生成不携带自定义信息 JWT token

第一步:构建头部信息

Map<String, Object> map = new HashMap<String, Object>();
map.put("alg", "HS256");
map.put("typ", "JWT");

第二步:构建密钥信息

Algorithm algorithm = Algorithm.HMAC256("secret");

第三步:我们通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token

String token = JWT.create()
   .withHeader(map)// 设置头部信息 Header 
   .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
   .withSubject("this is test token")//设置 载荷 签名的主题
   // .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
   .withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
   .withIssuedAt(nowDate) //设置 载荷 生成签名的时间
   .withExpiresAt(expireDate)//设置 载荷 签名过期的时间
   .sign(algorithm);//签名 Signature

详细代码如下:

@Test
	public void createToken() {
 
		String secret = "secret";// token 密钥
		Algorithm algorithm = Algorithm.HMAC256("secret");
 
		// 头部信息
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("alg", "HS256");
		map.put("typ", "JWT");
 
		Date nowDate = new Date();
		Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期
		
		String token = JWT.create()
			.withHeader(map)// 设置头部信息 Header 
			.withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器
			.withSubject("this is test token")//设置 载荷 签名的主题
			// .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的.
			.withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的
			.withIssuedAt(nowDate) //设置 载荷 生成签名的时间
			.withExpiresAt(expireDate)//设置 载荷 签名过期的时间
			.sign(algorithm);//签名 Signature
		Assert.assertTrue(token.length() > 0);
	}

3.生成携带自定义信息 JWT token

自定义信息通过 withClaim 方法进行添加,具体操作如下:

JWT.create()
    .withHeader(map)
    .withClaim("loginName", "zhuoqianmingyue")
    .withClaim("userName", "张三")
    .withClaim("deptName", "技术部")

生成携带自定义信息 JWT token 详细代码如下:

 @Test
	public String createTokenWithChineseClaim() {
 
		Date nowDate = new Date();
		Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期
 
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("alg", "HS256");
		map.put("typ", "JWT");
 
		Algorithm algorithm = Algorithm.HMAC256("secret");
		String token = JWT.create().withHeader(map)
				/* 设置 载荷 Payload */
				.withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "张三").withClaim("deptName", "技术部")
				.withIssuer("SERVICE")// 签名是有谁生成 例如 服务器
				.withSubject("this is test token")// 签名的主题
				// .withNotBefore(new Date())//定义在什么时间之前,该jwt都是不可用的
				.withAudience("APP")// 签名的观众 也可以理解谁接受签名的
				.withIssuedAt(nowDate) // 生成签名的时间
				.withExpiresAt(expireDate)// 签名过期的时间
				/* 签名 Signature */
				.sign(algorithm);
		
		Assert.assertTrue(token.length() > 0);
		return token;
	}

4.验证 JWT Token

第一步:构建密钥信息

Algorithm algorithm = Algorithm.HMAC256("secret");

第二步:通过密钥信息和签名的发布者的信息生成JWTVerifier (JWT验证类)

JWTVerifier verifier = JWT.require(algorithm)
		        .withIssuer("SERVICE")
		        .build();

不添加 .withIssuer(“SERVICE”) 也是可以获取 JWTVerifier 。

第三步:通过JWTVerifier 的verify获取 token中的信息。

DecodedJWT jwt = verifier.verify(token);

如下面代码所示就可以获取到我们之前生成 token 的 签名的主题,观众 和自定义的声明信息。

String subject = jwt.getSubject();
List<String> audience = jwt.getAudience();
Map<String, Claim> claims = jwt.getClaims();
for (Entry<String, Claim> entry : claims.entrySet()) {
    String key = entry.getKey();
	Claim claim = entry.getValue();
	System.out.println("key:"+key+" value:"+claim.asString());
}

验证 JWT Token 详细代码如下:


@Test
	public void verifyToken() throws UnsupportedEncodingException {
		String token = createTokenWithChineseClaim2();
		
		Algorithm algorithm = Algorithm.HMAC256("secret");
		JWTVerifier verifier = JWT.require(algorithm).withIssuer("SERVICE").build(); // Reusable verifier instance
		DecodedJWT jwt = verifier.verify(token);
		
		String subject = jwt.getSubject();
		List<String> audience = jwt.getAudience();
		Map<String, Claim> claims = jwt.getClaims();
		for (Entry<String, Claim> entry : claims.entrySet()) {
			String key = entry.getKey();
			Claim claim = entry.getValue();
			log.info("key:" + key + " value:" + claim.asString());
		}
		Claim claim = claims.get("loginName");
 
		log.info(claim.asString());
		log.info(subject);
		log.info(audience.get(0));
 
	}
	public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {
 
		Date nowDate = new Date();
		Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小过期
 
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("alg", "HS256");
		map.put("typ", "JWT");
 
		User user = new User();
		user.setUserNaem("张三");
		user.setDeptName("技术部");
		Gson gson = new Gson();
		String userJson = gson.toJson(user);
 
		String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());
 
		Algorithm algorithm = Algorithm.HMAC256("secret");
		String token = JWT.create().withHeader(map)
 
				.withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 签名是有谁生成
				.withSubject("this is test token")// 签名的主题
				// .withNotBefore(new Date())//该jwt都是不可用的时间
				.withAudience("APP")// 签名的观众 也可以理解谁接受签名的
				.withIssuedAt(nowDate) // 生成签名的时间
				.withExpiresAt(expireDate)// 签名过期的时间
				.sign(algorithm);//签名 Signature
 
		return token;
	}

三.使用JWT登录案例实战

参考 https://gitee.com/tang_sheng_sheng/jwt-demo.git

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值