是什么?
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
jwt是一套完整的签名验证机制,可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公用/专用密钥对对JWT进行签名。
什么时候应该使用JSON Web令牌?
以下是JSON Web令牌有用的一些情况:
- 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。
- 信息交换:JSON Web令牌是在各方之间安全地传输信息的好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确定发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否遭到篡改。
JSON Web令牌结构是什么?
JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:
- 标头
- 有效载荷
- 签名
因此,JWT通常如下所示。
xxxxx.yyyyy.zzzzz
让我们分解不同的部分。
标头
标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。
例如:
{
"alg": "HS256",
"typ": "JWT"
}
然后,此JSON被Base64Url编码以形成JWT的第一部分。
有效载荷
令牌的第二部分是有效负载,其中包含声明。声明是有关实体(通常是用户)和其他数据的声明。声明有以下三种类型:
-
标准中注册的声明:这些是一组非强制性的但建议使用的预定义权利要求,以提供一组有用的,可互操作的权利要求。其中一些是: iss(发布者), exp(到期时间), sub(主题), aud(受众群体)等。
请注意,声明名称仅是三个字符,因为JWT是紧凑的。
-
公共的声明:使用JWT的人员可以随意定义这些声明。但是为避免冲突,应在 IANA JSON Web令牌注册表中定义它们,或将其定义为包含抗冲突名称空间的URI。
-
私有的声明:这些都是使用它们同意并既不是当事人之间建立共享信息的自定义声明注册或公众的权利要求。
有效负载示例可能是:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后,对有效负载进行Base64Url编码,以形成JSON Web令牌的第二部分。
请注意,对于已签名的令牌,此信息尽管可以防止篡改,但任何人都可以读取。除非将其加密,否则请勿将机密信息放入JWT的有效负载或报头元素中。
签名
要创建签名部分,您必须获取编码的标头,编码的有效载荷,机密,标头中指定的算法,并对其进行签名。
例如,如果要使用HMAC SHA256算法,则将通过以下方式创建签名:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
签名用于验证消息在此过程中没有更改,并且对于使用私钥进行签名的令牌,它还可以验证JWT的发送者是它所说的真实身份。
放在一起
输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递这些字符串,与基于XML的标准(例如SAML)相比,它更紧凑。
下面显示了一个JWT,它已对先前的标头和有效负载进行了编码,并用一个秘密进行签名。
如果您想使用JWT并将这些概念付诸实践,则可以使用jwt.io Debugger解码,验证和生成JWT。
简单示例
是不是看到这里还是不知所云,其实我当时也是这么觉得的,后来通过实践,才慢慢理解,所以这里我们也废话少说,show me code。
创建springboot项目
本次示例采用的技术架构为:springboot + redis + shiro,页面模板引擎用的是thymeleaf,首先我们创建个spring boot项目,为啥用spring boot,因为构建项目快啊,简单方便。然后添加如下依赖(完整的直接去githu,会放链接):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
因为我们这里用不到数据库,所以就不配置数据库相关信息,但如果你不配置数据库,springboot启动会报错,为了让springboot启动不报错,我们要在springboot启动入口添加如下注解:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
然后就可以启动了,如果没有什么问题,继续往下看
编写jwt工具类
如果你在此之前连jwt是什么也不清楚,那也没关系,先参照下面的代码写,我下面会解释。我当时第一次接触jwt的时候也是这么过来的,别害怕,就是干,别问谁给我的勇气,反正不是梁静茹