JWT详解&工具类封装

举报
汪小成 发表于 2022/10/18 17:40:34 2022/10/18
【摘要】 JWT 是 JSON Web Token 的简称,是目前比较流行的用户身份验证解决方案。JWT 从本质上来说就是一个字符串,中间用点(.)分隔成三个部分:头部、负载、签名。

JWT简介

JWT 是 JSON Web Token 的简称,是目前比较流行的用户身份验证解决方案

JWT的数据结构

JWT 从本质上来说就是一个字符串,中间用点(.)分隔成三个部分:头部负载签名

image.png

一个简单的 JWT 如下图所示:

image.png

Header

Header 部分用于描述 JWT 的元数据。

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg 属性:签名的算法(algorithm),默认是 SHA256;
  • typ 属性:这个令牌(token)的类型(type),JWT 令牌统一写为 JWT 。

Payload

Payload 部分用来存放实际需要传递的数据。JWT 规定了7个官方字段:

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

除了官方字段,还可以在这个部分定义私有字段,下面就是一个例子。

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

注意: JWT 默认是不加密的,任何人都可以读到,不要把秘密信息放在这个部分。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法,按照下面的公式生成签名。

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

签名生成后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

在Spring Boot Web项目中使用JWT

1、引入 Maven 类库

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

2、工具类封装

笔者主要对JWT工具类做了如下封装:

  • 定义默认属性
  • 生成 token
  • 解析 token - 获取 username
  • 解析 token - 根据键值获取属性值

2.1、定义默认属性

/**
 * 默认签名密钥
 */
private static final String DEFAULT_SECRET_KEY = "thisisasimpledemoicomefromshandongprovincejiningcityrenchengcountryanjuzhenhelloworld";
/**
 * 签发者
 */
private static final String ISSUER = "jasmine";

笔者定义了两个默认属性:签发者和默认签名密钥。

2.2、签名加密方法

public static String getBase64SecurityKey() {
    return Base64.getEncoder().encodeToString(DEFAULT_SECRET_KEY.getBytes(StandardCharsets.UTF_8));
}

该方法主要是对签名进行 Base64 加密处理,生成的签名加密字符串主要用于生成 token 和解析 token 。

2.3、生成 token

/**
 * 生成 Token
 */
public static String createToken(String username, Map<String, Object> claims) {
    byte[] keyBytes = Decoders.BASE64.decode(getBase64SecurityKey());
    Key key = Keys.hmacShaKeyFor(keyBytes);
    return Jwts.builder()
        // 受众
        .setAudience(username)
        // 签发者
        .setIssuer(ISSUER)
        .setClaims(claims)
        // 面向的用户
        .setSubject(username)
        // 签发时间
        .setIssuedAt(new Date())
        // 签名算法、签名密钥
        .signWith(key, SignatureAlgorithm.HS512)
        .compact();
}

生成 token 笔者这里主要设置了受众、签发者、面向的用户、签发时间、签名算法、签名密钥。参数claims中除官方定义字段外,还可以添加自定义字段。

2.4、获取 username 及属性值

/**
 * 从 Token 中获取 username
 */
public static String getUsernameFromToken(String token) {
	try {
		final Claims claims = getClaimsFromToken(token);
		if (Objects.isNull(claims) || StringUtils.isEmpty(claims.getSubject())) {
			return null;
		}
		return claims.getSubject();
	} catch (Exception e) {
		LOGGER.error("获取用户名称失败,异常信息:", e);
		return null;
	}
}

/**
 * 根据键值从 Token 中获取属性值
 */
public static String get(String token, String key) {
	Claims claims = getClaimsFromToken(token);
	if (Objects.isNull(claims) || StringUtils.isBlank(key)) {
		return null;
	}
	Object value = claims.get(key);
	return Objects.isNull(value) ? null : String.valueOf(value);
}

public static Claims getClaimsFromToken(String token) {
	Claims claims;
	try {
		byte[] keyBytes = Decoders.BASE64.decode(getBase64SecurityKey());
		Key key = Keys.hmacShaKeyFor(keyBytes);
		claims = Jwts.parserBuilder()
				.setSigningKey(key)
				.build()
				.parseClaimsJws(token)
				.getBody();
	} catch (Exception e) {
		if (e instanceof ExpiredJwtException) {
			throw new RuntimeException("TOKEN超时,请重新登录。");
		} else {
			LOGGER.error("解析Token信息时发生异常:", e);
			claims = null;
		}
	}
	return claims;
}

2.5、测试

public static void main(String[] args) {
    Map<String, Object> userMap = Maps.newHashMap();
    userMap.put("roleId", "1");
    userMap.put("orgId", "1");
    String token = createToken("admin", userMap);
    System.out.printf("token: %s%n", token);
    System.out.printf("username: %s%n", getUsernameFromToken(token));
    System.out.printf("roleId: %s%n", get(token, "roleId"));
}

输出结果:

token: eyJhbGciOiJIUzUxMiJ9.eyJyb2xlSWQiOiIxIiwib3JnSWQiOiIxIiwic3ViIjoiYWRtaW4iLCJpYXQiOjE2NjYwODQzODh9.ZkAjtpBSQCVbHAHyIcKzNGE-S-L5MC3kgH_d2ekDnXmq-_K3kdmy4jyY7Qmbx9LqAr-8YzsohWHC2V6pkL9FJQ
username: admin
roleId: 1
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。