基于Token的登录流程 您所在的位置:网站首页 APP登录token存放在哪 基于Token的登录流程

基于Token的登录流程

2024-07-01 06:28| 来源: 网络整理| 查看: 265

一.身份验证(Authentication)

要想区分来自不同用户的请求的话,服务端需要根据客户端请求确认其用户身份,即身份验证

在人机交互中,身份验证意味着要求用户登录才能访问某些信息。而为了确认用户身份,用户必须提供只有用户和服务器知道的信息(即身份验证因子),比如用户名/密码

Web 环境下,常见的身份验证方案分为 2 类:

基于 Session 的验证基于 Token 的验证

基于 Session 的方案中,登录成功后,服务端将用户的身份信息存储在 Session 里,并将 Session ID 通过 Cookie 传递给客户端。后续的数据请求都会带上 Cookie,服务端根据 Cookie 中携带的 Session ID 来得辨别用户身份

而在基于 Token 的方案中,服务端根据用户身份信息生成 Token,发放给客户端。客户端收好 Token,并在之后的数据请求中带上 Token,服务端接到请求后校验并解析 Token 得出用户身份,过程如下:

token based login

P.S.用户名/密码属于知识因子,另外还有占有因子和遗传因子:

知识因子:用户登录时必须知道的东西都是知识因子,比如用户名、密码等占有因子:用户登录时必须具备的东西,比如密码令牌、ID 卡等遗传因子:个人的生物特征,比如指纹、虹膜、人脸等

P.S.Authentication(验证)与 Authorization(授权)不同,前者验证身份,后者验证权限

二.Token

身份验证中的 Token 就像身份证,由服务端签发/验证,并且在有效期内都具有合法性,认“证”(Token)不认“人”(用户)

Session 方案中用户身份信息(以 Session 记录形式)存储在服务端。而 Token 方案中(以 Token 形式)存储在客户端,服务端仅验证 Token 合法性。这种区别在单点登录(SSO,Single Sign On)的场景最为明显:

基于 Session 的 SSO:考虑如何同步 Session 和共享 Cookie。比如登录成功后把响应 Cookie 的 domain 设置为通配兄弟应用域名的形式,并且所有应用都从身份验证服务同步 Session基于 Token 的 SSO:考虑如何共享 Token。比如进入兄弟应用时通过 URL 带上 Token

Token 相当于加密过的 Session 记录,含有用户 ID 等身份信息,以及 Token 签发时间,有效期等用于 Token 合法性验证的元信息,例如:

代码语言:javascript复制{ // 身份信息 user_id: 9527, // Token元信息 issued_at: '2012年3月5号12点整', expiration_time: '1天' } // 加密后 895u3485y3748%^HGdsbafjhb

任何带有该 Token 的请求,都会被服务端认为是来自用户 9527 的消息,直到一天之后该 Token 过期失效,服务端不再认可其代表的用户身份

Token 形式多种多样,其中,JSON Web Token是一种比较受欢迎的 Token 规范

三.JSON Web Token

JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.

简言之,一种通信规范(简称 JWT),用来安全地表示要在双方之间传递的声明,能够通过 URL 传输

P.S.声明可以是任意的消息,比如用户身份验证场景中的“我是用户 XXX”,好友申请中的“用户 A 添加用户 B 为好友”

Token 格式

JWT 中的 Token 分为 3 部分,Header、Payload 与 Signature,例如:

代码语言:javascript复制eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJvdGhlckZpZWxkIjoiZXRjLiJ9.BkrYadYHS66ZrONzFoA91gBJK0iE-S2ZYOWCpRhgFJQ

两个.字符隔开三部分,即:

代码语言:javascript复制Header.Payload.Signature

含义上,Header表示 Token 相关的基本元信息,如 Token 类型、加密方式(算法)等,具体如下(alg是必填的,其余都可选):

typ:Token typecty:Content typealg:Message authentication code algorithm

Payload表示 Token 携带的数据及其它 Token 元信息,规范定义的标准字段如下:

iss:Issuer,签发方sub:Subject,Token 信息主题(Sub identifies the party that this JWT carries information about)aud:Audience,接收方exp:Expiration Time,过期时间nbf:Not (valid) Before,生效时间iat:Issued at,生成时间jti:JWT ID,唯一标识

这些字段都是可选的,Payload 只要是合法 JSON 即可

生成

Token 的三部分分别为:

代码语言:javascript复制Base64编码的Header.Base64编码的Payload.对前两部分按指定算法加密的结果

例如,对于

代码语言:javascript复制// JOSE Header const header = JSON.stringify({"typ":"JWT", "alg":"HS256"}); // JWT Claims Set const claims = JSON.stringify({ "iss":"joe", "exp":1300819380, "http://example.com/is_root":true });

对 JOSE Header 和 JWT Claims Set 分别进行 Base64 编码得到 JWT Token 中的 Header 与 Payload 部分:

代码语言:javascript复制const tokenHeader = Buffer.from(header).toString('base64'); const tokenPayload = Buffer.from(header).toString('base64');

接着把 Header 与 Payload 用.字符连接起来,并通过 HMAC SHA-256 算法(Header 中alg字段指定的加密算法)加密,得到 Signature 部分:

代码语言:javascript复制// https://www.npmjs.com/package/jwa const jwa = require('jwa'); const hmac = jwa('HS256'); const toSign = `${tokenHeader}.${tokenPayload}`; const tokenSignature = hmac.sign(toSign, 'mySecret');

最后把 Signature 也用.字符连接在最后:

代码语言:javascript复制const token = `${toSign}.${tokenSignature}`;

得到结果:

代码语言:javascript复制eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlLCJvdGhlckZpZWxkIjoiZXRjLiJ9.BkrYadYHS66ZrONzFoA91gBJK0iE-S2ZYOWCpRhgFJQ

P.S.可以通过JWT.IO解析验证该 Token

P.S.注意,Base64 编码的 Header 与 Payload 需要去掉末尾等号(padding trailing equals)

传输

服务端生成 Token 之后,放在响应体里传递到客户端

客户端收到之后,将 Token 存放到 LocalStorage/SessionStorage 中,之后请求数据时,将 Token 塞到请求头的Authentication字段里带到服务端:

代码语言:javascript复制Authorization: Bearer

服务端收到数据请求后,从Authorization字段取出 Token,并校验其合法性,进一步解析 Token 内容,获知用户身份

验证

校验 Token 合法性需要确认几件事情:

Token 有没有过期是不是自己签发的

从 Payload 部分解析(直接 Base64 解码)出iat、nbf和exp三个时间相关的字段,检查是否满足以下关系:

代码语言:javascript复制iat签发时间 { // 废掉token invalidateToken(res.locals.token); res.status(200).json({ status: 'success' }); }); // 3.过期时去黑 // 4.操作时验证是否已黑 decodeToken(token, (err, payload) => { if (err) { // remove expired token from blacklist removeInalidatedToken(token); return res.status(401).json({ status: 'Token has expired' }); } else { // check invalidated token if (isTokenInvalidated(token)) { return res.status(401).json({ status: 'Token has been invalidated' }); } //... } });

P.S.上例中,黑名单只放在内存中,服务重启时会丢失,比较完备的实现应该是加黑/去黑(即过期)时落库,验证时走内存缓存,重启时读库加载

除黑名单外,还有一些常见策略,如:

删掉客户端 Token:把发出去的 Token 干掉,Token 消失了,登录状态也就不存在了。但服务端仍然认为 Token 合法,不安全用过期时间很短的 Token,经常轮转:过期时间足够短的话,自动过期就相当于立即过期。但太短又丧失了保持状态的优势Token 带上注销时间:把注销时间也像密码一样存库、校验,像改密码一样让 Token 立即作废。但需要多存/取、校验一个字段,性能相关

必要的话,这 4 种策略可以多管齐下,比如无论使用哪种策略,客户端 Token 都是理应删掉的

P.S.关于如何立即作废 JWT 的更多讨论,见:

Invalidating JSON Web TokensHow to destroy JWT Tokens on logout?How to log out when using JWT:废话比较多七.FAQJWT 的 Payload 安全吗? 不安全,仅经 Base64 编码过,相当于明文传输,因此不要携带敏感数据用户输入的密码需要在客户端加密吗? 不需要加密,直接明文传,客户端密码安全由 SSL 保证服务端收到密码应该如何加密? 一般做法是 Hash 加盐(Adding Salt to Hashing),具体见Adding Salt to Hashing: A Better Way to Store Passwords参考资料JSON Web Token – 在 Web 应用间安全地传递信息八幅漫画理解使用 JSON Web Token 设计单点登录系统Token-Based Authentication with NodeRFC 7519 – JSON Web Token (JWT)JSON Web TokenA plain English introduction to JSON web tokens (JWT): what it is and what it isn’tUnderstanding User Authentication: 3 Basics You Should KnowThe proper way of implementing user login systemNode.js encryption of passwordshokaccha/node-jwt-simpleauth0/node-jsonwebtoken


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有