赞
踩
前言:
在Spring的众多组件中,个人认为Springsecurity组件绝对是比较有难度的一款。对于springSecurity涵盖的内容比较多,在接下来的内容中会分几篇内容进行梳理讲解。先从JWT这里开始。需要明确的一点就是:JWT并不是springsecurity的一部分,其是单独的一个标准,只不过,在spring security组件中使用了它。如果是在开发过程中,你不想使用Springsecurity来完成jwt这种操作,是可以单独使用相关的jar包来实现。springsecurity组件是将认证和授权机制统一的组件。提供的认证方式有多种,JWT是其中一种,使用组件形式,在实际开发中就需要关心集体如何生成,只需要关注业务流程即可
什么是JWT(JSON Web Token)?
官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
虽然JWT可以加密以在各方之间提供保密,但只将专注于签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明。当使用公钥/私钥对签署令牌时,签名还证明只有持有私钥的一方是签署私钥的一方。
通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据。
什么情况下使用JWT比较适合?
授权:这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛;
信息交换:JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。
总之:JWT要解决的问题,就是为多种终端设备,提供统一的、安全的令牌格式。
对于传输,你可以使用任何传输方式来传输jwt,一般来说,我们会使用消息头来传输它。当客户端拿到令牌后,它要做的只有一件事:存储它。
当后续请求发生时,你只需要将它作为请求的一部分发送到服务器即可。
虽然jwt没有明确要求应该如何附带到请求中,但通常我们会使用如下的格式:
GET /api/resources HTTP/1.1
...
authorization: bearer jwt令牌
...
这种格式是OAuth2附带token的一种规范格式
至于什么是OAuth2,那是另一个话题了
这样一来,服务器就能够收到这个令牌了,通过对令牌的验证,即可知道该令牌是否有效。
它们的完整交互流程是非常简单清晰的:
令牌的组成
为了保证令牌的安全性,jwt令牌由三个部分组成,分别是:
它们组合而成的完整格式是:header.payload.signature
比如,一个完整的jwt令牌如下:
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc
-
它各个部分的值分别是:
下面分别对每个部分进行说明
它是令牌头部,记录了整个令牌的类型和签名算法
它的格式是一个json对象,如下:
- {
- "alg":"HS256",
- "typ":"JWT"
- }
该对象记录了:
设置好了header之后,就可以生成header部分了
具体的生成方式及其简单,就是把header部分使用base64 url编码即可。
需要明确的是:
base64 url不是一个加密算法,而是一种编码方式,它是在base64算法的基础上对+、=、/三个字符做出特殊处理的算法,而base64是使用64个可打印字符来表示一个二进制数据。
这部分是jwt的主体信息,它仍然是一个JSON对象,它可以包含以下内容:
- {
- "ss":"发行者",
- "iat":"发布时间",
- "exp":"到期时间",
- "sub":"主题",
- "aud":"听众",
- "nbf":"在此之前不可用",
- "jti":"JWT ID"
- }
以上属性可以全写,也可以一个都不写,它只是一个规范,就算写了,也需要你在将来验证这个jwt令牌时手动处理才能发挥作用
上述属性表达的含义分别是:
对于应用系统中需要携带的自定义的参数就是放到此payload部分,对于Springsecurity组件来说,是增强部分。
当用户登陆成功之后,我可能需要把用户的一些信息写入到jwt令牌中,比如用户id、账号等等,因为可以被随时解密,密码不要进行存储。
其实很简单,payload这一部分只是一个json对象而已,你可以向对象中加入任何想要加入的信息
比如,下面的json对象仍然是一个有效的payload:
- {
- "foo":"bar",
- "iat":1587548215
- }
foo: bar是我们自定义的信息,iat: 1587548215是jwt规范中的信息。
signature
这一部分是jwt的签名,正是它的存在,保证了整个jwt不被篡改
这部分的生成,是对前面两个部分的编码结果,按照头部指定的方式进行加密
比如:头部指定的加密方法是HS256,前面两部分的编码结果是
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9
则第三部分就是用对称加密算法HS256对字符串
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9进行加密,当然你得指定一个秘钥,比如shhhhh。
最终,将三部分组合在一起,就得到了完整的jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1ODc1NDgyMTV9.BCwUy3jnUQ_E6TqCayc7rCHkx-vxxdagUwPOWqwYCFc
由于签名使用的秘钥保存在服务器,这样一来,客户端就无法伪造出签名,因为它拿不到秘钥。
换句话说,之所以说无法伪造jwt,就是因为第三部分的存在。
而前面两部分并没有加密,只是一个编码结果而已,可以认为几乎是明文传输。
这不会造成太大的问题,因为既然用户登陆成功了,它当然有权力查看自己的用户信息
甚至在某些网站,用户的基本信息可以被任何人查看
你要保证的,是不要把敏感的信息存放到jwt中,比如密码。
jwt的signature可以保证令牌不被伪造,那如何保证令牌不被篡改呢?
比如,某个用户登陆成功了,获得了jwt,但他人为的篡改了payload,比如把自己的账户余额修改为原来的两倍,然后重新编码出payload发送到服务器,服务器如何得知这些信息被篡改过了呢?
这就要说到令牌的验证了
令牌在服务器组装完成后,会以任意的方式发送到客户端
客户端会把令牌保存起来,后续的请求会将令牌发送给服务器
而服务器需要验证令牌是否正确,如何验证呢?
首先,服务器要验证这个令牌是否被篡改过,验证方式非常简单,就是对header+payload用同样的秘钥和加密算法进行重新加密
然后把加密的结果和传入jwt的signature进行对比,如果完全相同,则表示前面两部分没有动过,就是自己颁发的,如果不同,肯定是被篡改过了。
当令牌验证为没有被篡改后,服务器可以进行其他验证:比如是否过期、听众是否满足要求等等,这些就视情况而定了
注意:这些验证都需要服务器手动完成,没有哪个服务器会给你进行自动验证,当然,你可以借助第三方库来完成这些操作
最后,总结一下jwt的特点:
jwt本质上是一种令牌格式。它和终端设备无关,同样和服务器无关,甚至与如何传输无关,它只是规范了令牌的格式而已
jwt由三部分组成:header、payload、signature。主体信息在payload
jwt难以被篡改和伪造。这是因为有第三部分的签名存在。
最后需要明确一点是:JWT能够保证数据的有效性,但是不能保证数据的安全性,对于数据安全需要通过其他手段来保证,比如HTTPS的方式。
因篇幅所限,下节内容将讲解Springsecurity如何实现JWT机制及其他认证的实现方式,如token等,欢迎持续关注。
以上为全部内容。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。