SpringBoot系列: Web应用鉴权思路

时间:2021-02-04 11:06:59

==============================
web 项目鉴权
==============================
主要的鉴权方式有:
1. 用户名/密码鉴权, 然后通过 Session/Cookie 保存鉴权结果.
2. 用户名/密码鉴权, 通过 Basic authentication 方式, 每次请求都要将 appId/appSecret 加到 HTTP [Authorization] header. 
3. 用户名/密码鉴权, 然后通过 JWT token 保存鉴权结果, 每次请求带上jwt token, 加到 HTTP  X-Auth-Token header上.

JWT 的优点:
1. Jwt 是一个 stateless 方案, 不需要解决 session 共享问题.
2. Jwt 更适合于手机端的访问, 因为移动端不支持 cookie.
3. Jwt 更适合于微服务端的验证, 因为微服务的使用方通常不是web browser, 而是服务端程序, 使用 jwt 更方便.
3. Jwt 支持跨域访问, 而 Cookie不允许.
4. Jwt 因为不依赖cookie, 所以不需要考虑CSRF(跨站请求伪造)攻击.
JWT 的缺点:
1. 服务器端不能主动注销 Jwt token, 使用 Session 方式时, 服务器端是能主动 logout 一个登陆.
2. 对于普通 Web 浏览器, 使用 cookie/Session 几乎无感, 而 jwt 需要在请求的时候加 header.

Basic authentication 的优点:
1. 优点和Jwt差不多, 另外它比Jwt 更容易实现. 
basic authentication 的缺点:
1. 每次请求时都需要发送密码, 所以仅仅适合于API项目的鉴权, 不适合WebUI 项目;
2. 和 jwt token 方式相比, 每次请求都需要受访API服务验证"密码", 这往往要查询DB, 而 jwt 只需要用算法即可 token 是否有效.

==============================
Web项目鉴权的推荐
==============================
(1) Web UI 项目, 建议采用 Session 来做权限验证, Web UI 项目关注的是用户级的权限.
(2) 纯粹企业内部的 API 服务项目, 建议采用 Basic Auth 做权限验证, 关注的是来源 app 是否有权限(粗粒度权限管控), 并不是每个登陆用户的权限.
(3) 开放 API 平台, 建议采用 Jwt 做权限验证, 关注的是来源 app 是否有权限(粗粒度权限管控), 并不是每个登陆用户的权限.

不管是Session, 还是jwt token和basic authentication 方式, 至少都需要发送一次密码, 如果是公网访问的话, 应该走 https, 否则不管采用什么高级的框架, 都存在安全风险.
对于企业内部的API 服务项目, 通信都是发生在服务器之间, 可以不走 https, 为了防止 appId/appSecret 泄漏, 最好再结合 IP 白名单. 我猜测, 很多企业内部的API服务其实没有任何鉴权, 完全是在裸奔.

==============================
Jwt Token 流程
==============================

----------------------------
App Authentication 服务器
----------------------------
专门建有统一的 App Authentication 服务器, 集中管理 Access token 签发, 可以将企业中的所有"微服务"都纳入其管理, 统一管理, 有利用服务的治理.

SpringBoot系列: Web应用鉴权思路
----------------------------
Access token 的申请
----------------------------
一个 WebUI 应用或微服务如要访问其他微服务, 需要向统一的 App Authentication 服务器申请 Access Token, 申请时提供自己的 appId/appSecret 和目标微服务 appId, 由 App Authentication 服务器负责决定是否签发 access token, 在 token 中记录申请 appId/目标 appId, 以及赋予的角色.

为了防止 appId 被冒用, 需要提早将 appId 和它所在的服务器 IP 加到 App Authentication 的白名单中.

为了防止 access token 被大量扩散, 针对同一个 appId 和目标 appId, 同时只能有两个 Access Token, 之所以是两个, 一方面可有效限制了 token 的数量, 另外能无缝覆盖 token 失效期.

为了防止重复攻击, 需要设定token有效期, 企业内部也不宜太短, 设置1~2天较好一些.

为了防止重复攻击, 可以在token中带上申请app 的白名单IP,  在验证token的时候可以顺带验证request 请求来源IP是否是白名单中.

一个应用申请到 Access token 之后, 有责任先将该 token 保存, 最好保存到 DB 中, 另外最好有一个定时任务定期申请 access token, 并在 DB 中维护一个 token 池, 在用的时候应使用有效期更远的那个 token.

----------------------------
Access token 的使用
----------------------------
在每次访问微服务的时候, 都要在 headers 上加上 token 信息, 加到 X-Auth-Token 上.
在受访微服务中, 验证 access token 是否有效, 是否已经过期, 并提取出来源 app 的角色, 然后进行下一步权限上的管控.

==============================
术语解释
==============================
JWT: 全称 json web token, 包含 header 和 claims 和 signature 三个部分. 
   因为 token 中已经带有 signature, 所以具有 token 自验证机制, 能防止伪造.
   在 claim 中可以设定一个过期时间, 所以能防止 token 被人长期冒用.

Access token(基于 jwt):
   Access token: 对于开放平台, 一般通过 appId/appSecret 来获取一个 access token, 比如微信就是这样, 该 access token 有效期为 2 个小时, 调用微信公众号的所有接口都需要带上 access token.
在微信平台中, 重复获取 Access token, 将导致之前获取的 Access token 失效. 个人认为: 要在服务器端主动失效已经签发的 token, 这将又走到了 session 的路子上, 这是一个 1+1<1 的做法, 也许只要控制每个 appId 同时能申请几个 Access token 就行了, 比如限制 2 个有效 Access token.

Refresh token(基于 jwt)
微信是通过 appId/appSecret 来获取 Refresh token, 有些平台是通过 Refresh token 来获取 Access token, 所以 Refresh token 有效期比较长. 个人认为, 微信的做法更好一些, 一般情况下没有必要再引入 Refresh token 这个概念.

==============================
参考
==============================
基于Token的WEB后台认证机制
http://www.cnblogs.com/xiekeli/p/5607107.html
使用JSON Web Token设计单点登录系统
https://leon_lizi.gitbooks.io/json-web-token/content/chapter2.html#
聊一聊JWT与session
https://juejin.im/post/5a437441f265da43294e54c3
重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API
https://juejin.im/post/58c29e0b1b69e6006bce02f4