springboot中JJWT的简单使用

一 什么是JWT

jwt全称json web token,是基于json协议的用于结局认证授权的方法。token就是令牌,其主要作用是用来进行授权(Authorization),授权和认证是不同的概念,认证是系统通过账号密码或其他手段来验证身份,授权是在认证后获得系统授予的权限。系统想知道用户有什么权限,需要给用户贴上标签,方式有很多种,token是其中一种。当用户获得token后,用户后续的请求只需附带token,系统便可进行授权。jwt不同与cookie-session,它不会在服务端进行储存,token会被写入进http请求的header或body中进行双向的传输。

JWT组成

JWT由三部分组成,分别是header,playload和signature。由于本文主要写JJWT的使用,JWT结构就不详细展开了,对于这部分还不了解的可以去JWT官网上查看,连接:JWT官网介绍

  • Header
  • PlayLoad
  • Signature

二 什么是JJWT

JJWT旨在成为最易于使用和理解的库,用于在JVM和Android上创建和验证JSON Web令牌(JWT)。 JJWT是完全基于JWT,JWS,JWE,JWK和JWA RFC规范以及Apache 2.0许可条款下的开源的纯Java实现。 该图书馆由Okta的高级建筑师Les Hazlewood创建,并由贡献者社区提供支持和维护。 Okta是面向开发人员的完整身份验证和用户管理API。 我们还添加了一些不属于该规范的便利扩展,例如JWS压缩和声明执行。

以上内容由jjwt官方文档机翻而来

简而言之,JJWT提供了java环境下的使用JWT的能力,使得具体实现对开发者透明,开发者只需用简单几行代码即可创建或者解析一个token。

三 简单入门

  1. 导入依赖(Maven环境)
<!--老版本-->
<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.0</version>
</dependency>

注意,现在jjwt已经更新到0.11.0,理论上说0.xx.x到0.xx.x的更新属于兼容性的,但经过尝试,发现还是有不少改动。这里也贴出来最新的版本的Maven工程依赖

<!--新版本-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
    <version>0.11.1</version>
    <scope>runtime</scope>
</dependency>
  1. 生成token
    jjwt中,使用Jwts.builder()进行构建token。
// 假设代码块被包含在一个函数中
Map<String, Objcet> claims = new HashMap<>();
// claims就是playload中的内容,可以写入自己需要的键值
claims.put("key", "value");
// 构建
JwtBuilder jwtBuilder = Jwts.builder()
    		// 设置有效载荷
            .setClaims(claims)
    		// 设置subject
            .setSubject(subject)
    		// 设置签发时间
            .setIssuedAt(new Date())
    		// 采用HS256方式签名,key就是用来签名的秘钥
            .signWith(SignatureAlgorithm.HS256, key);
// 调用compact函数将token打包成String并返回
return jwtBuilder.compact();

在上述代码块中,对JWT的playload进行了设置,没有自定义header。

这里采用的HS256加密,HS256是一种对称秘钥加密手段,利用哈希MAC加密方式,即加密解密都使用相同的秘钥"key"。也可以采用RS256加密或者其他加密方法,RS256是公开秘钥加密,秘钥分为公钥和私钥,私钥用于生成JWT,公钥用于解密,想知道更具体的可以学习信息安全。但无论哪种加密方法,都得保护好秘钥,并确保秘钥难以被碰撞。

  1. 服务端发送token
    在步骤2中调用compact()会生成一长串经过编码的字符串,字符串格式为xxxxxx.xxxxxxxx.xxxxxxx,三个部分分别对应头,载荷和签名。将token发送给客户端往往通过httpheaders中的Authorization字段发送。当然也可以放在http数据包中发送给客户端。
// 在header中写入Authorization
@RequestMapping(value = "/jwttest1", method = RequestMethod.GET)
public String test1(HttpServletRequest request, HttpServletResponse response) {
    // 通过步骤2中的方法获取token
    String token = ...
    response.setHeader("Authorization", token);
    return "success";
}

这里要注意,在前端如果是跨域请求,js获取heaer中隐私的信息在规范中是不安全的,所以服务器需设置,让这个header暴露出来

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**")
            .allowCredentials(true)
            .exposedHeaders("Authorization");
}
  1. 服务端解析token
    发送token后,客户端的请求会附带token,token同样可以放httpheader中或报文中,接下来需要解析token
@RequestMapping(value = "/jwttest2", method = RequestMethod.GET)
public void test2(HttpServletRequest request, HttpServletResponse response) {
    String token = request.getHeader("Authorization");
    // 调用下面写的方法解析即可,由于没有写方法的类,这里就不演示了
}
// 解析token
public Claims jwtParser(String token) {
    Claims claims;
    // token不一定通过验证,所以需要包裹try-catch捕获jjwt提供的JwtException
    try {
        claims = Jwts.parser()
        	// HS256是对称加密体系,加密解密使用同一个key
            .setSigningKey(key)
            .parseClaimsJws(token)
            .getBody();
    } catch (JwtException e) {
        claims = null;
        e.printStackTrace();
    }
    return claims;
}

获取到的claims就是playload,如果是playload中的标准字段可以直接通过函数取出如

String subject = (String) claims.getSubject();

如果是自己自定义的键值可以通过get()方法取出,假设储存的值可以转换String

String value = (String) claims.get("key");

这样就获取到并解析了token。