这是我参与「第五届青训营」笔记创作活动的第13天。
今天给大家分享一下我们队伍针对大项目用户鉴权的解决方案。
鉴权操作使用最常用jwt进行。
Jwt介绍
Jwt
json web token(JWT)是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。它是以JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。
http协议无状态的,所以需要sessionId或token的鉴权机制,jwt的token认证机制不需要在服务端再保留用户的认证信息或会话信息。这就意味着基于jwt认证机制的应用程序不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利,jwt更适用于分布式应用
jwt的组成部分
标准的jwt令牌分为三部分,分别是Header、payload、signature;
Header
它的组成部分包括两点
参数类型-jwt、签名的算法-hs256
最后会经过Base64加密方式进行编码,看起来是这个样子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
Payload
它的组成就是登陆用户的一些信息,和token发行和失效时间等;这些内容里面有一些是标准字段,你也可以添加其它需要的内容。
- iss:Issuer,发行者
- sub:Subject,主题
- aud:Audience,观众
- exp:Expiration time,过期时间
- nbf:Not before
- iat:Issued at,发行时间
- jti:JWT ID
最后它也会通过Base64加密方式进行编码,仍然长的像上面的样子
Signature
它是由3个部分组成,先是用 Base64 编码的 header 和 payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。
secret就是在最后第二次加密时加的盐,算是一个秘钥(只保留在服务器),不向外部透露。
Jwt具体实践
根据jwt的构成结构可知,一个token字符串,分为头部的储存加密算法的字符串,中间部分的储存主要数据负载的字符串,和最后一部分通过前两部分和私有密钥进行加密的字符串。
我们这里使用go-jwt进行jwt封装:
这里我们需要在Claims中加入用户的id和name用来标识用户,同时在jwt中加入id也作为其他用户获取当前登录用户id的重要途径,这一点会在后面的中间件中重点介绍:
在jwt中间件中,我们解析token,然后得到claims,分析其中的expire,查看token是否过期,如果或者出现了鉴权失败等情况,我们终止中间件并且返回具体信息:
- 查看token是否合法:claims, err := service.ParseToken(token)
- 查看token是否过期:claims.Expire < int(time.Now().Unix())
- 在当前请求生命周期设置user_id,在请求中可以获取当前user_id,方便查看当前的用户id 最终,我们通过c.Next()继续请求。 这样,在controller层处理请求时,可以通过c.Get("user_id")来获取当前请求user_id。