服务的登录方式
Cookie
原理:
- 客户端通过请求将账户密码发送到服务端
- 服务端验证账户密码
- 验证成功返回加密的
set-cookie
响应请求头,将用户信息设置到客户端cookie
中 - 验证失败返回错误信息
- 验证成功返回加密的
- 之后客户端发送的请求都会自动携带此
cookie
,服务端通过cookie
获得用户的信息- 在用户信息中可设置过期时间
- 过期或者解密失败
特点:
- 储存在浏览器的一段字符串,不大于5kb
- 跨域不共享
- 服务端可以修改
cookie
返回 - 浏览器可以修改
cookie
(有限制) - 发送
http
请求自动携带对应请求域cookie
发送给服务端
node操作:
req.headers.cookie
浏览器操作:
document.cookie
参数:
- 不允许客户端修改:
httpOnly
- 过期时间,
GMT
格式:expires
存在的问题:
- 暴露用户信息等敏感数据
基于 Cookie
的 Session
解决cookie
存在的暴露问题。
原理:
- 对用户信息等敏感数据生成对应的
token
,将用户信息保存在服务端session
,将token
设置到客户端cookie
- 客户端请求时会携带保存有
token
的cookie
到服务端,服务端根据token
查找缓存在session
中的用户信息
特点:
- 用户信息保存在服务端,避免将敏感数据发送到客户端
- 方便服务端对用户进行管理
- 占用服务端内存
存在的问题:
session
是js
变量,放在进程内存中,变量过大内存不足怎么办- 跨域不会携带
cookie
,需要设置跨域资源共享cors
,并在客户端请求配置中设置跨域携带cookie
的设置 - 操作系统会限制一个进程的最大可用内存
- 如果是多进程的,进程之间的内存不能共享
如何解决session
共享的问题:
- 数据持久化
- 使用共享内存:
redis
- 写入数据库
- 使用共享内存:
- 不保存
session
数据,将数据保存在客户端jwt
JWT
(Json web token
)
相关文章:
原理:
- 服务端认证后,生成用户数据对象,并使用对应规则签名这个对象获得一个加密字符串
JWT
,返回给客户端 - 客户端自己将
token
保存在cookie
或者storage
中 - 客户端在请求时手动将
JWT
附在请求头中,通常为Authorization: Bearer <token>
- 服务端获取到客户端发送的
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
对前两部分的签名,防止数据篡改
- 首先指定一个私有密钥:
secret
,保存在服务端 - 使用
.
组合base64
处理后的header
和payload
,得到一个待加密的字符串 - 使用
header
中声明的加密方式通过secret
对待加密的字符串加盐加密得到第三部分:signature
- 最终可以组合成
jwt
javascript
const secret = 'abc123'
const encodedString = base64(header) + '.' base64(payload)
const signature = HMACSHA256(encodedString, secret);
const jwt = `${base64(header)}.${base64(payload)}.${signature}`
优点:
- 根据
json
的通用性,可以支持多语言服务端 - 构成简单,方便传输
- 不在服务端保存会话信息,便于扩展
缺点:
payload
是base64
对称加密,jwt
不加密不应该保存敏感信息到payload
- 无法实时管理用户数据
SSO
: Single Sign On
在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
同域下的单点登录
一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。 比如我们有个域名叫做:a.com
,同时有两个业务系统分别为:app1.a.com
和 app2.a.com
。 我们要做单点登录,需要一个登录系统,叫做:sso.a.com
。 我们需要在sso.a.com
登录后在 app1.a.com
和 app2.a.com
也就登录了。
- 如何在他们之间共享登录状态?
cookie
domain
选项表示的是cookie
所在的域,默认为请求的地址,将domain
设置为.a.com
就可以使二级域名都可以访问到这个cookie
不同域下的单点登录
首次登录流程:
- 用户访问
app1
系统,app1
系统是需要登录的,但用户现在没有登录。 - 跳转到
SSO
登录系统,SSO
系统也没有登录,弹出用户登录页。 - 用户填写用户名、密码,
SSO
系统进行认证后,将登录状态写入SSO
的session
,浏览器(Browser
)中写入SSO
域下的Cookie
。 SSO
系统登录完成后会生成一个ST
(Service Ticket
),SSO
登录系统携带ST
跳转到app1
系统。app1
系统拿到ST后,从后台向SSO
发送请求,验证ST
是否有效。- 验证通过后,
app1
系统将登录状态写入session
并设置app1
域下的Cookie
。
其他系统登录流程:
- 之后未登录的
app2
、app3
会跳转SSO
系统 - 此时
SSO
已经登录完成,不需要重新登录验证 - 重复首次登录的 4-5 步骤
OAuth2
第三方鉴权的常用方式
应用场景:应用提供给第三方应用部分使用权限,如:qq授权网易云音乐,使网易云音乐获得qq的用户名和头像
OAuth2
中几个名词:
Third-party application
:第三方应用程序,本文中又称"客户端",指例子中的 网易云音乐HTTP service
:HTTP服务提供商,指qq
Resource Owner
:资源所有者,指用户User Agent
:用户代理,指浏览器Authorization server
:认证服务器,即服务提供商(qq
)专门用来处理认证的服务器。Resource server
:资源服务器,即服务提供商(qq
)存放用户生成的资源的服务器。
运行流程
- 用户打开客户端以后,客户端要求用户给予授权。
- 用户同意给予客户端授权。
- 客户端使用上一步获得的授权,向认证服务器申请令牌。
- 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
- 客户端使用令牌,向资源服务器申请获取资源。
- 资源服务器确认令牌无误,同意向客户端开放资源。
在步骤2中,客户端授权的方式有多种:
授权码模式
- 用户访问网易云音乐,网易云音乐要求用户提供qq的授权,指定一个"重定向
URI
",并跳转到qq
设置的授权登录页面 - 在
qq
设置的授权登录页面,用户选择授权范围,完成验证后qq
认证服务器返回一个授权码,携带授权码返回网易云音乐指定的"重定向URI
" - 网易云音乐获得授权码,附上指定的"重定向
URI
",向认证服务器申请令牌 - 认证服务器核对了授权码和"重定向
URI
",确认无误后,向客户端发送访问令牌(access token
)和更新令牌(refresh toke
n)。
1步骤中,授权登录页面,包含以下参数:
response_type
:表示授权类型,必选项,此处的值固定为"code"client_id
:表示客户端的ID,必选项redirect_uri
:表示重定向URI
,可选项scope
:表示申请的权限范围,可选项state
:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值,避免csrf
。