(转载)鉴权方案实现小结

目前我们常用的鉴权有四种:

  • HTTP Basic Authentication
  • session-cookie
  • Token 验证
  • OAuth(开放授权)

HTTP Basic Authentication

HTTP Basic Authentication 概念

HTTP Basic Authentication 授权方式是浏览器遵守 http 协议实现的基本授权方式,HTTP 协议进行通信的过程中,HTTP 协议定义了允许 HTTP 服务器对客户端进行用户身份验证的方法。

HTTP Basic Authenticatio 认证过程

  1. 客户端向服务器请求数据,请求的内容可能是一个网页或者是一个 ajax 异步请求,此时,假设客户端尚未被验证;
  2. 服务器向客户端发送验证请求代码 401,然后弹出用户登录界面;
  3. 用户输入用户信息和密码,浏览器会自动以 base64 形式进行加密;
  4. 服务器收到请求之后,将信息解密,将其与数据库中的用户信息进行对比,一直的话返回用户需要的请求内容。

登录失效的方案:在注销操作的时候,专门在服务器设置一个专门的注销账号,当接收到的 Authentication 信息为注销用户名密码的时候便注销成功了,而客户端在注销操作的时候,手动的去修改请求头的 Authentication,将它设置为服务器默认的注销账号和密码。

注:该方式很古老,基本没用,安全性较低。

利用服务器端的 session(会话)和浏览器端的 cookie 来实现前后端的认证,由于 http 请求时是无状态的,需要在服务器端创建一个会话(seesion),将同一个客户端的请求都维护在各自得会会话中,每当请求到达服务器端的时候,先去查一下该客户端有没有在服务器端创建 seesion,如果有则已经认证成功了,否则就没有认证。

  1. 服务器在接受客户端首次访问时在服务器端创建 seesion,然后保存 seesion 到内存当中,然后给这个 session 生成一个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。
  2. 浏览器中收到请求响应的时候会解析响应头,然后将 session_id 保存在本地 cookie 中,浏览器在下次 http 请求时请求头中会带上该域名下的 cookie 信息
  3. 服务器在接受客户端请求时会去解析请求头 cookie 中的 session_id,然后根据这个 session_id 去找服务器端保存的该客户端的 session,然后判断该请求是否合法.

缺点

  • 只适用于 web 系统(其他终端没有 cookie 一说)
  • session 的内存占用:每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以便用户下次请求的鉴别,通常而言 session 都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大
  • 分布式 session 的问题:分布式环境下,如何保证 session 在多台机器间共享(解决思路:1.共享存储(redis);2.只使用 cookie(类似于 token 验证);3.Nginx 负载均衡策略按 ip hash(保证同一用户的请求打到同一台机器))
  • CSRF 风险:由于依赖 cookie 进行用户识别,cookie 如果被截获,用户就会很容易受到跨站请求伪造的攻击

Token 验证

Token 认证过程

  1. 客户端使用用户名跟密码请求登录;
  2. 服务端收到请求,去验证用户名与密码;
  3. 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端;
  4. 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里;
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token(url 参数,post 参数,HTTP header 等地方都行);
  6. 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据。

token 验证方案 JWT

JWT 概念

JWT 是 Auth0 提出的通过对 JSON 进行加密签名来实现授权验证的方案,就是登陆成功后将相关信息组成 json 对象,然后对这个对象进行某种方式的加密,返回给客户端,客户端在下次请求时带上这个 token,服务端再收到请求时校验 token 合法性,其实也就是在校验请求的合法性。

JWT 是 OAuth2 协议中授权码模式的一种具体实现.

JWT 组成

  • Headers: 包括类别(typ)、加密算法(alg);
  • Claims :包括需要传递的用户信息;
  • Signature: 根据 alg 算法与私有秘钥进行加密得到的签名字串,这一段是最重要的敏感信息,只能在服务端解密;

基于 JWT 的实践

在 JWT 的实践中,引入 Refresh Token,将会话管理流程改进如下:

  1. 客户端使用用户名密码进行认证
  2. 服务端生成有效时间较短的 Access Token(例如 10 分钟),和有效时间较长的 Refresh Token(例如 7 天)
  3. 客户端访问需要认证的接口时,携带 Access Token
  4. 如果 Access Token 没有过期,服务端鉴权后返回给客户端需要的数据
  5. 如果携带 Access Token 访问需要认证的接口时鉴权失败(例如返回 401 错误),则客户端使用 Refresh Token 向刷新接口申请新的 Access Token
  6. 如果 Refresh Token 没有过期,服务端向客户端下发新的 Access Token
  7. 客户端使用新的 Access Token 访问需要认证的接口

OAuth(开放授权)

OAuth 概念

OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容,为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。我们常见的提供 OAuth 认证服务的厂商有支付宝,QQ,微信。

OAuth2 的四种模式

授权码模式

  1. 向用户请求授权,而当我们点击等第三方入口时,第三方授权服务会引导我们进入第三方登陆授权页面,此时路由附上重定向的地址 redirect_uri;
  2. 当用户点击授权并登陆后,授权服务器将生成一个用户凭证(code)。这个用户凭证 Authorization Code 会附加在重定向的地址 redirect_uri 的后面;
  3. 用户再去请求时携带用户凭证(code)(该部分操作在后台进行,由后台服务器带上 Authorization Code 和 client_id 去请求验证服务器),验证服务器返回一个 access_token 和 refresh_token;
  4. 后台再去拿着令牌 Access Token 请求资源时,就会得到受保护的资源信息。
先获取 code 的原因
  1. code 需要设置过期时间,一般设置的过期时间非常短,如 10 分钟等,用户需要在短时间内通过 code 换取 access_token,避免 code 被第三方拦截。当然,即便这种情况会发生,但因为 code 的过期时间非常短,也在一定程度上进行了保护,但这肯定不是完全安全的
  2. 授权成功后,code 是会直接显示在浏览器上的,如果不通过 code 换取 access_token,而是直接返回 access_token,那 access_token 会被暴露出来,而 code 换取 access_token 是直接通过 oauth 服务器进行换取的,不依赖浏览器,access_token 不会暴露出去。
access token & refresh token

为什么要刷新 access token 呢?一是因为 access token 是有过期时间的,到了过期时间这个 access token 就失效,需要刷新;二是因为一个 access token 会关联一定的用户权限,如果用户授权更改了,这个 access token 需要被刷新以关联新的权限。

为什么要专门用一个 token 去更新 access token 呢?如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,多麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。

简化模式

  1. 用户在客户端上点击要哪个系统的 OAuth2 来认证,此时客户端附上回调地址
  2. 用户在 OAuth2 服务器上选择是否授权
  3. 用户给于授权,OAuth2 服务器重定向到第一步给定的回调地址,并且附上 access_token 和 refresh_token

密码模式

用户直接输入用户名和密码,这种情况针对自家的 APP 或者 100%信任的 APP 可以这么干

客户端凭证模式

客户端自带认证,用户向客户端认证就可以