关于后端:面试官小伙子你这单点登录学的可以啊

7次阅读

共计 3467 个字符,预计需要花费 9 分钟才能阅读完成。

我尝试用八幅漫画先让大家了解如何设计失常的用户认证零碎,而后再延长到单点登录零碎。

JWT 简介

JSON Web Token(JWT)是一个十分笨重的标准。这个标准容许咱们应用 JWT 在用户和服务器之间传递安全可靠的信息。

让咱们来假想一下一个场景。在 A 用户关注了 B 用户的时候,零碎发邮件给 B 用户,并且附有一个链接“点此关注 A 用户”。链接的地址能够是这样的

https://your.awesome-app.com/make-friend/?from_user=B&target_user=A

下面的 URL 次要通过 URL 来形容这个当然这样做有一个弊病,那就是要求用户 B 用户是肯定要先登录的。可不可以简化这个流程,让 B 用户不必登录就能够实现这个操作。JWT 就容许咱们做到这点。

JWT 的组成

一个 JWT 实际上就是一个字符串,它由三局部组成: 头部、载荷与签名。

载荷(Payload)

咱们先将下面的增加好友的操作形容成一个 JSON 对象。其中增加了一些其余的信息,帮忙今后收到这个 JWT 的服务器了解这个 JWT。

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "jrocket@example.com",
    "from_user": "B",
    "target_user": "A"
}

这外面的前五个字段都是由 JWT 的规范所定义的。

  • iss: 该 JWT 的签发者
  • sub: 该 JWT 所面向的用户
  • aud: 接管该 JWT 的一方
  • exp(expires): 什么时候过期,这里是一个 Unix 工夫戳
  • iat(issued at): 在什么时候签发的

这些定义都能够在规范中找到。

将下面的 JSON 对象进行 [base64 编码] 能够失去上面的字符串。这个字符串咱们将它称作 JWT 的 Payload(载荷)。

eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9

如果你应用 Node.js,能够用 Node.js 的包 base64url 来失去这个字符串。

var base64url = require('base64url')
var header = {
    "from_user": "B",
    "target_user": "A"
}
console.log(base64url(JSON.stringify(header)))
// 输入:eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
头部(Header)

JWT 还须要一个头部,头部用于形容对于该 JWT 的最根本的信息,例如其类型以及签名所用的算法等。这也能够被示意成一个 JSON 对象。

{
  "typ": "JWT",
  "alg": "HS256"
}

在这里,咱们阐明了这是一个 JWT,并且咱们所用的签名算法(前面会提到)是 HS256 算法。

对它也要进行 Base64 编码,之后的字符串就成了 JWT 的 Header(头部)。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
签名(签名)

将下面的两个编码后的字符串都用句号. 连贯在一起(头部在前),就造成了

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

这一部分的过程在 node-jws 的源码中有体现

最初,咱们将下面拼接完的字符串用 HS256 算法进行加密。在加密的时候,咱们还须要提供一个密钥(secret)。如果咱们用 mystar 作为密钥的话,那么就能够失去咱们加密后的内容

rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

这一部分又叫做签名。

最初将这一部分签名也拼接在被签名的字符串前面,咱们就失去了残缺的 JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

于是,咱们就能够将邮件中的 URL 改成

https://your.awesome-app.com/make-friend/?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

这样就能够平安地实现增加好友的操作了!

用户认证八步走

所谓用户认证(Authentication),就是让用户登录,并且在接下来的一段时间内让用户拜访网站时能够应用其账户,而不须要再次登录的机制。

小常识:可别把用户认证和用户受权(Authorization)搞混了。用户受权指的是规定并容许用户应用本人的权限,例如公布帖子、治理站点等。

首先,服务器利用(上面简称“利用”)让用户通过 Web 表单将本人的用户名和密码发送到服务器的接口。这一过程个别是一个 HTTP POST 申请。倡议的形式是通过 SSL 加密的传输(https 协定),从而防止敏感信息被嗅探。

接下来,利用和数据库核查用户名和明码。

核查用户名和明码胜利后,利用将用户的 id(图中的 user\_id)作为 JWT Payload 的一个属性,将其与头部别离进行 Base64 编码拼接后签名,造成一个 JWT。这里的 JWT 就是一个形同 lll.zzz.xxx 的字符串。

利用将 JWT 字符串作为该申请 Cookie 的一部分返回给用户。留神,在这里必须应用 HttpOnly 属性来避免 Cookie 被 JavaScript 读取,从而防止跨站脚本攻打(XSS 攻打)。

在 Cookie 生效或者被删除前,用户每次拜访利用,利用都会承受到含有 jwt 的 Cookie。从而利用就能够将 JWT 从申请中提取进去。

利用通过一系列工作查看 JWT 的有效性。例如,查看签名是否正确;查看 Token 是否过期;查看 Token 的接管方是否是本人(可选)。

利用在确认 JWT 无效之后,JWT 进行 Base64 解码(可能在上一步中曾经实现),而后在 Payload 中读取用户的 id 值,也就是 user\_id 属性。这里用户的 id 为 1025。

利用从数据库取到 id 为 1025 的用户的信息,加载到内存中,进行 ORM 之类的一系列底层逻辑初始化。

利用依据用户申请进行响应。

单点登录

Session 形式来存储用户 id,一开始用户的 Session 只会存储在一台服务器上。对于有多个子域名的站点,每个子域名至多会对应一台不同的服务器,例如:

www.taobao.com
nv.taobao.com
nz.taobao.com
login.taobao.com

所以如果要实现在 login.taobao.com 登录后,在其余的子域名下仍然能够取到 Session,这要求咱们在多台服务器上同步 Session。

应用 JWT 的形式则没有这个问题的存在,因为用户的状态曾经被传送到了客户端。因而,咱们只须要将含有 JWT 的 Cookie 的 domain 设置为顶级域名即可,例如

Set-Cookie: jwt=lll.zzz.xxx; HttpOnly; max-age=980000; domain=.taobao.com

留神 domain 必须设置为一个点加顶级域名,即.taobao.com。这样,taobao.com 和 *.taobao.com 就都能够承受到这个 Cookie,并获取 JWT 了。

作者:子回(John Wu)
blog.leapoahead.com/2015/09/07/user-authentication-with-jwt/

正文完
 0