服务的登录方式
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字符串:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9payload 存放有效信息的地方,包含三个部分内容
- 标准声明
- 公共声明
- 私有声明
标准声明(建议但不强制使用):
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字符串:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9signature 对前两部分的签名,防止数据篡改
- 首先指定一个私有密钥:
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 也就登录了。
- 如何在他们之间共享登录状态?
cookiedomain选项表示的是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服务提供商,指qqResource Owner:资源所有者,指用户User Agent:用户代理,指浏览器Authorization server:认证服务器,即服务提供商(qq)专门用来处理认证的服务器。Resource server:资源服务器,即服务提供商(qq)存放用户生成的资源的服务器。
运行流程
- 用户打开客户端以后,客户端要求用户给予授权。
- 用户同意给予客户端授权。
- 客户端使用上一步获得的授权,向认证服务器申请令牌。
- 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
- 客户端使用令牌,向资源服务器申请获取资源。
- 资源服务器确认令牌无误,同意向客户端开放资源。
在步骤2中,客户端授权的方式有多种:
授权码模式
- 用户访问网易云音乐,网易云音乐要求用户提供qq的授权,指定一个"重定向
URI",并跳转到qq设置的授权登录页面 - 在
qq设置的授权登录页面,用户选择授权范围,完成验证后qq认证服务器返回一个授权码,携带授权码返回网易云音乐指定的"重定向URI" - 网易云音乐获得授权码,附上指定的"重定向
URI",向认证服务器申请令牌 - 认证服务器核对了授权码和"重定向
URI",确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
1步骤中,授权登录页面,包含以下参数:
response_type:表示授权类型,必选项,此处的值固定为"code"client_id:表示客户端的ID,必选项redirect_uri:表示重定向URI,可选项scope:表示申请的权限范围,可选项state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值,避免csrf。
