HTTP 协议是一种无状态的协议,这意味着用户提供账号和密码进行登录认证后,下次再请求的时候,仍然需要认证,因为服务器并不知道是谁发送的请求,并不知道该用户已经认证过一次了。
所以为了解决这个问题,保持客户端和服务端的会话状态,在服务器的缓存中需要为每一位用户分配存储空间,用于存储用户的个人登录信息等。且没份存储空间有个唯一标识ID作为自己的身份证。
这样在作响应的时候,将该ID返回给浏览器,浏览器存储到本地,以便后续再次请求时都可以携带着这个ID,服务器就能根据这个ID去对应缓存空间中去查找是否存在对应的缓存区,能找到则表示该用户之前已经访问过了,存储区存储的登录信息也可以直接使用,就不用再次登陆了。
这就是传统的session + cookie 的会话保持技术,session 是存在于服务端的一个缓存区,相当于一个存储数据的map数据结构,每个session 对应着自己的session ID,而cookie 则是存在于客户端浏览器的一种数据存储区。
上述流程实际上是浏览器的第一次访问服务器的时候分配session 空间和生成session ID,并将session ID存储在响应头的Set-Cookie
;浏览器会专门处理Cookie数据并进行存储,在下一次发请求的时候会在请求头的Cookie字段携带之前存储的cookie数据,也就是sessionID,服务器会在收到请求后先在请求头中的cookie 拿到这个ID,这样后续就能找到和操作专属这个用户的数据存储区了。
JWT 的出现就是为了解决传统Session + Cookie 技术存在的各种问题,实际上随着前后端分离的发展,以及数据中心的建立,越来越多的公司会创建一个中心服务器,同时服务于各种产品线,如:统一身份认证平台,这些产品线上的产品,他们可能有各种终端设备,包括但不仅限于浏览、桌面应用、移动端应用、平板应用、甚至于智能家居。
JWT(json web token),json格式的网络令牌,简称token,它要解决的问题,就是为多种终端设备,提供统一的、安全的令牌格式。
因此,JWT 只是一个令牌格式而已,你可以把他存储到cookie,也可以存储到localstorage,没有任何限制。同样的,对于传输,你可以使用任何传输方式来传输JWT,一般来说,我们会使用HTTP消息头来传输他(header 的 Authorization)
当客户端拿到令牌后,存储他即可。可以存到各种位置,比如手机文件、PC文件、local storage、Cookie等。当后续请求发生时,你只需要将他作为请求的一部分发送到服务器即可。
这样一来,服务器就能收到这个令牌,通过对令牌的验证,即可知道该令牌是否有。他们的完整交互流程是非常简单清晰的!
JWT是可以存储用户信息的,自定义一个用户信息的结构体里面自己带一些信息然后拿整个结构体区生成token
Header 是一个令牌头部,记录了整个令牌的类型和签名算法,格式是一个json对象。
签名算法有两种。
对称加密和非对称加密
设置好了header 的结构之后,还需要对header 的JSON 对象进行Base64 URL编码,最后编码生成的字符串才是header 部分。
注意:Base64 URL 不是一个加密算法,而是一种编码格式,它是在Base64 算法的基础上对 + = / 三个字符做出特殊处理的一种变种算法。而Base64 是使用64个可打印字符来表示一个二进制数据。
这部分是JWT 主体信息,仍然是一个JSON 对象
实际上就是存用户信息的,通常都自定义一个结构,其实payload 只是一个JSON对象,如下也是一个有效的payload!
payload 部分也和 header一样,需要通过Base64 URL 编码。
这一部分是 JWT 的签名,正是因为这部分的存在,保证了整个JWT不被篡改。这部分的生成方式与上述两部分直接编码JSON对象不同,他需要将前面两部分的编码结果通过.
连接起来,然后按照头部指定的加密方式进行加密(结合自定义的密钥)
最后,将三部分通过 .
组合在一起,就得到了完整的JWT。并且由于签名使用的密钥保证存在服务器,客户端就无法伪造出签名。JWT无法伪造的本质就在于第三部分,因为前两部分并没有加密,只是一个编码结果,可以几乎认为是明文传输的。这个没有问题,因为登陆成功后是可以查看自己的个人信息的。
JWT的signature 如何保证令牌不被篡改呢
若用户人为的修改payload中的个人信息再进行编码发送到服务器,服务器如何识别呢。
1、对header + payload 用同样的密钥和加密算法进行加密
2、然后把加密的结果和传入的JWT 的signature 进行对比,如果完全相同那么就没被篡改
当令牌验证为没有被篡改之后,服务器可以进行其他验证,比如是否过期、听众是否满足要求等。
这些验证都由服务器手动完成,不会自动验证,你也可以通过第三方库来完成这些操作
如果想设置Token 长时间有效,可以采用redis 的方式。
作为生成Token的参数
不论如何自定义什么结构体,必须带有标准库提供的 claim,里面有签名 ,有效期等必要信息
其中jwt.NewWithClaims
new一个token,他有两个参数,返回值就是token
jwt.SigningMethodHS256
另一种非对称加密上文有提及。jwt.MapClaims
,也可以自定义一个结构体,如果是自定义一个结构体需要实现jwt.StandardClaim
结构体,里面可以定义过期时间颁发者等。
这里的IsContainArr
作用是排除掉那些不需要验证权限的路由。
这个验证不是解析,可有可无只是get出来一些值并且插入其中
这里使用类型断言去转成我们定义的结构体类型,claims本质上是一个接口,由token包定义的(由token包定义的)
解密使用jwt.Parse
方法,如果上面使用了自己定义的结构体的话就使用jwt.ParseWithClaims
方法
jwt.Parse
有两个参数
jwt.ParseWithClaims
有三个参数
更新操作目的在于延长token