登录鉴权方案设计
登陆和认证是什么?都是在鉴别用户的身份。如何鉴定识别出这是哪个用户?或者说,有什么方式只有用户自己知道(够安全),又能说出这是他自己?于是就有了"用户名+密码"、"用户名+手机号" 的方式出现。下面主要分析 “用户名+密码”的登陆鉴权方式:
- 出于安全考虑,我们不可能在所有的请求里都使用“用户名+密码”。
- "用户名+密码"不能存储在任何地方,最安全的方式就是存放在用户自己的脑子里,也就是说如果要使用 “用户名+密码” 就得让用户自己提供。
那么我们如何做到可以不用提供用户名和密码呢?我们在用户登录的时候,根据用户的身份信息去生成一种能标记用户的 token。
有了认证信息 token 以后,新的问题就是如何保证这个 token 是安全的呢?如果其他人拿到了这个 token 就能伪造用户身份了。解决方案就是鉴定这个 token 是不是用户本人在使用,一般我们通过以下方式,如果我们能保证下面的两种验证信息是一直都没变或者在可接受的范围内,我们就能认为这个用户一直在使用我们下发的 token。
- 位置信息(ip、经纬度)
- 设备信息
现在我们已经做到了 token + (位置、设备信息)来鉴定用户的身份。如果我们每次请求都要鉴定这些信息来确认用户身份,势必影响我们服务的性能。那么如何减少这样的判定呢?
- 服务端随机或者定时选定一次请求鉴定
- 客户端定时请求一次鉴定
第一种方式不建议,服务器维护每个用户定时复杂,随机可能会有大量用户同时鉴定的性能问题。那么就是第二种了,客户端定时请求一次鉴定,服务端需要强制客户端定时请求一次鉴定,而这个定时时间就是一个安全因素,如果在期间 token 泄露就会造成用户信息安全问题,时间不能太长,也不能太短,参考微信的是 7200s 就是 2 小时。
那么如何强制客户端每隔一段时间做一次验证呢?很简单,我们给 token 设个有效时间,到失效时间客户端就必须请求一次新的 token,于是就有了 refresh token。
二、鉴权方式
用户登录后,每次请求服务时客户端请求都要包含鉴权信息,服务端根据鉴权信息查询用户信息和其合法性。目前鉴权信息可以有如下方式:
1. 集中式 session 方式
在登陆完成后,服务端将返回作为认证鉴权的随机不重复 token,客户端每次请求带上这个 token(一般放在请求的 header 里面)。服务端通过 token 查询到 token 对应的用户信息。
2. 令牌方式
登陆完成后,服务端根据用户信息和其他安全因素加密生成一个安全令牌(也就是 JWTS,JSON Web Tokens),该令牌中包含了用户的身份信息,在认证鉴权时只需验证令牌的合法性即可,解密即可取到用户信息。
3. 比较
鉴权方式 | 优点 | 不足 |
---|---|---|
集中式 session 方式 | 1. 服务端维护用户状态,可以管理用户状态,没有失效和用户信息一致问题;
2. 安全性相对较高 |
1. 服务端维护 Session 状态,根据 Token 获取用户信息需要极高的性能要求 |
令牌方式 | 1. 客户端记录用户状态,服务是无状态的,没有集中式的性能问题。
2. 业务解耦,鉴权逻辑相对简单的多,令牌方式在技术实现性能和可靠性上也相对容易些 |
1. 令牌强制失效或者续签问题,令牌强制失效或更新需要额外工作(比如封禁用户)
2. 当用户信息更新后,令牌里的用户的同步问题 |
三、鉴权实现方案
1. 集中式 session 方式流程时序
- 客户端在未登录的状态下请求业务服务,在网关没有获取到认证信息时直接返回 401,告知客户端需要登录。
- 客户端使用“手机号+密码”、“手机号+验证码”的方式请求登陆服务。
- 网关发现是登陆服务后,请求登陆认证服务。
- 登陆认证服务通过手机号码查询用户信息,同时生成 token。
- 返回网关 token,网关将 token 返回给客户端。
- 客户端带着 token 请求网关,网关将 token 传给鉴权服务,鉴权服务通过 token 查询用户信息并返回给网关,网关将用户信息转给业务服务,完成接下来的业务流程。
2. 令牌方式流程时序
- 客户端在未登录的状态下请求业务服务,在网关没有获取到认证信息时直接返回 401,告知客户端需要登录。
- 客户端使用“手机号+密码”、“手机号+验证码”的方式请求登陆服务。
- 网关发现是登陆服务后,请求登陆认证服务,同时生成 token(JWT信息),并将 token 返回给客户端。
- 客户端带着 token 请求网关,网关根据 token(JWT信息)解密得到用户信息,并将用户信息转给业务服务,完成接下来的业务流程。
四、鉴权技术方案
1. 集中式Session鉴权技术方案
集中式Session鉴权验证用户身份有如下的实现方式:
实现 | 说明 | 优点 | 缺点 |
---|---|---|---|
RPC调用 | 网关通过RPC调用鉴权服务,通过token获取用户信息 | 架构逻辑简单,不需要额外工作 | 增加响应 RT |
Redis 直读 | 网关直接读取鉴权服务的 Redis,验证并获取用户信息 | 高性能 | 需要网关也集成鉴权验证逻辑,并且维护 Redis 的配置 |
Nginx Lua | 在 Nginx 里通过 lua 脚本实现鉴权逻辑 | 性能最好 | 1. 在网关之前,对签名无效的请求也需要做鉴权验证。
2. Nginx 改造,lua 开发成本 |
2. 令牌方式鉴权技术方案
令牌方式鉴权的实现就简单的多, 网关直接解析 JWT 信息获取用户信息,然后带着用户信息去请求业务数据