Skip to content

服务的登录方式

原理:

  1. 客户端通过请求将账户密码发送到服务端
  2. 服务端验证账户密码
    • 验证成功返回加密的set-cookie响应请求头,将用户信息设置到客户端cookie
    • 验证失败返回错误信息
  3. 之后客户端发送的请求都会自动携带此cookie,服务端通过cookie获得用户的信息
    • 在用户信息中可设置过期时间
    • 过期或者解密失败

特点:

  • 储存在浏览器的一段字符串,不大于5kb
  • 跨域不共享
  • 服务端可以修改cookie返回
  • 浏览器可以修改cookie(有限制)
  • 发送http请求自动携带对应请求域cookie发送给服务端

node操作:

  • req.headers.cookie

浏览器操作:

  • document.cookie

参数:

  • 不允许客户端修改:httpOnly
  • 过期时间,GMT格式:expires

存在的问题:

  • 暴露用户信息等敏感数据

解决cookie存在的暴露问题。

原理:

  1. 对用户信息等敏感数据生成对应的token,将用户信息保存在服务端session,将token设置到客户端cookie
  2. 客户端请求时会携带保存有tokencookie到服务端,服务端根据token查找缓存在session中的用户信息

特点:

  • 用户信息保存在服务端,避免将敏感数据发送到客户端
  • 方便服务端对用户进行管理
  • 占用服务端内存

存在的问题:

  • sessionjs变量,放在进程内存中,变量过大内存不足怎么办
  • 跨域不会携带cookie,需要设置跨域资源共享cors,并在客户端请求配置中设置跨域携带cookie的设置
  • 操作系统会限制一个进程的最大可用内存
  • 如果是多进程的,进程之间的内存不能共享

如何解决session共享的问题:

  1. 数据持久化
    • 使用共享内存:redis
    • 写入数据库
  2. 不保存session数据,将数据保存在客户端
    • jwt

JWT(Json web token)

相关文章:

原理:

  1. 服务端认证后,生成用户数据对象,并使用对应规则签名这个对象获得一个加密字符串JWT,返回给客户端
  2. 客户端自己将token保存在cookie或者storage
  3. 客户端在请求时手动将JWT附在请求头中,通常为Authorization: Bearer <token>
  4. 服务端获取到客户端发送的token,解密验证token的有效性,使用附带的数据

JWT的结构

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

它被.分割为三个部分:header.payload.signature

header 包含两部分信息:

  • 声明类型:JWT
  • 声明加密算法:通常用HMAC SHA256
json
{
  "typ":"JWT",
  "alg":"HS256"
}

对这部分内容进行base64加密,得到header字符串:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload 存放有效信息的地方,包含三个部分内容

  • 标准声明
  • 公共声明
  • 私有声明

标准声明(建议但不强制使用):

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共声明(任意): 添加业务相关的必要信息

私有声明(任意): 添加与业务无关的信息

json
{
  "sub": "1234567890",  // 标准
  "name": "John Doe",   // 公共
  "admin": true         // 私有
}

对这部分内容进行base64加密,得到payload字符串:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature 对前两部分的签名,防止数据篡改

  1. 首先指定一个私有密钥:secret,保存在服务端
  2. 使用.组合base64处理后的headerpayload,得到一个待加密的字符串
  3. 使用header中声明的加密方式通过secret对待加密的字符串加盐加密得到第三部分:signature
  4. 最终可以组合成jwt
javascript
const secret = 'abc123'
const encodedString = base64(header) + '.' base64(payload)
const signature = HMACSHA256(encodedString, secret);
const jwt = `${base64(header)}.${base64(payload)}.${signature}`

优点:

  • 根据json的通用性,可以支持多语言服务端
  • 构成简单,方便传输
  • 不在服务端保存会话信息,便于扩展

缺点:

  • payloadbase64对称加密,jwt不加密不应该保存敏感信息到payload
  • 无法实时管理用户数据

SSO : Single Sign On

在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。

同域下的单点登录

一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。 比如我们有个域名叫做:a.com,同时有两个业务系统分别为:app1.a.comapp2.a.com。 我们要做单点登录,需要一个登录系统,叫做:sso.a.com。 我们需要在sso.a.com登录后在 app1.a.comapp2.a.com 也就登录了。

  • 如何在他们之间共享登录状态?
    • cookiedomain选项表示的是cookie所在的域,默认为请求的地址,将domain设置为.a.com就可以使二级域名都可以访问到这个cookie

不同域下的单点登录

首次登录流程:

  1. 用户访问app1系统,app1系统是需要登录的,但用户现在没有登录。
  2. 跳转到SSO登录系统,SSO系统也没有登录,弹出用户登录页。
  3. 用户填写用户名、密码,SSO系统进行认证后,将登录状态写入SSOsession,浏览器(Browser)中写入SSO域下的Cookie
  4. SSO系统登录完成后会生成一个STService Ticket),SSO登录系统携带ST跳转到app1系统。
  5. app1系统拿到ST后,从后台向SSO发送请求,验证ST是否有效。
  6. 验证通过后,app1系统将登录状态写入session并设置app1域下的Cookie

其他系统登录流程:

  1. 之后未登录的app2app3会跳转SSO系统
  2. 此时SSO已经登录完成,不需要重新登录验证
  3. 重复首次登录的 4-5 步骤

OAuth2

第三方鉴权的常用方式

应用场景:应用提供给第三方应用部分使用权限,如:qq授权网易云音乐,使网易云音乐获得qq的用户名和头像

OAuth2 中几个名词:

  • Third-party application:第三方应用程序,本文中又称"客户端",指例子中的 网易云音乐
  • HTTP service:HTTP服务提供商,指 qq
  • Resource Owner:资源所有者,指用户
  • User Agent:用户代理,指浏览器
  • Authorization server认证服务器,即服务提供商(qq)专门用来处理认证的服务器。
  • Resource server:资源服务器,即服务提供商(qq)存放用户生成的资源的服务器。

运行流程

  1. 用户打开客户端以后,客户端要求用户给予授权。
  2. 用户同意给予客户端授权。
  3. 客户端使用上一步获得的授权,向认证服务器申请令牌。
  4. 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  5. 客户端使用令牌,向资源服务器申请获取资源。
  6. 资源服务器确认令牌无误,同意向客户端开放资源。

在步骤2中,客户端授权的方式有多种:

授权码模式

  1. 用户访问网易云音乐,网易云音乐要求用户提供qq的授权,指定一个"重定向URI",并跳转到qq设置的授权登录页面
  2. qq设置的授权登录页面,用户选择授权范围,完成验证后qq认证服务器返回一个授权码,携带授权码返回网易云音乐指定的"重定向URI"
  3. 网易云音乐获得授权码,附上指定的"重定向URI",向认证服务器申请令牌
  4. 认证服务器核对了授权码和"重定向URI",确认无误后,向客户端发送访问令牌access token)和更新令牌refresh token)。

1步骤中,授权登录页面,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值,避免csrf