登录功能的实现
-
Cookie + Session 登录
Cookie + Session
用户向服务器发送用户名和密码,服务器通过验证后,在当前对话(session)里面保存相关数据(比如用户角色,登录时间等),服务器向用户返回一个session_id
,写入cookie,用户随后的每一次请求都会通过cookie,将session_id
传回服务器,服务器收到session_id
,找到前期保存的数据,由此得知用户的身份。
-
Token 登录
- Token 是通过服务端生成的一串字符串,以作为客户端请求的一个令牌。当第一次登录后,服务器会生成一个 Token 并返回给客户端,客户端后续访问时,只需带上这个 Token 即可完成身份认证。
-
SSO 单点登录
- 单点登录是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。本质就是在多个应用系统中共享登录状态。用户首次访问时,需要在认证中心登录;会创建授权码
ticket
;当信任的其他系统时,认证中心验证账号密码,如果登录还有效,会重定向并带上授权码ticket
,就可以验证登录了。
- 单点登录是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。本质就是在多个应用系统中共享登录状态。用户首次访问时,需要在认证中心登录;会创建授权码
-
OAuth 第三方登录
可以看这篇:前端常见登录实现方案 + 单点登录方案
cookie和session
HTTP
协议是一种无状态协议
,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session
和Cookie
的主要目的就是为了弥补HTTP
的无状态特性。
cookie是什么?
cookie
是服务器发送到Web
浏览器的一小块数据,服务器发送到浏览器的Cookie
,浏览器会进行存储,并与下一个请求一起发送到服务器,用于判断请求是否来自同一个浏览器,例如用户保持登录状态。
cookie的属性
属性 | 说明 |
---|---|
name=value | 键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型 - 如果值为 Unicode 字符,需要为字符编码。 如果值为二进制数据,则需要使用 BASE64 编码。 |
domain | 该字段为可以访问此cookie 的域名,即cookie 在哪个域有效,默认是当前域名 |
size | cookie 的大小(不超过4kb) |
path | cookie 的有效路径,默认是'/' 。Domain 和Path 标识共同定义了Cookie 的作用域:即 Cookie 应该发送给哪些URL 。 |
maxAge | Max-Age 有效期的时间戳(服务器返回的时间,和客户端可能存在误差),默认为-1,页面关闭立即失效。优先级高于expires 。 |
expires | 过期时间,在设置的某个时间点后该 cookie 就会失效。 一般浏览器的 cookie 都是默认储存的,当关闭浏览器结束这个会话的时候,这个 cookie 也就会被删除 |
secure | 标记为Secure 的Cookie 只应通过被HTTPS 协议加密过的请求发送给服务端,可以保护Cookie 在浏览器和Web 服务器间的传输过程中不被窃取和篡改。 |
httpOnly | 设置为true 时不允许通过脚本document.cookie 去更改cookie 值,也不可获取,能有效的防止xss攻击。但发送请求仍会携带cookie。 |
SameSite | 该属性可以让Cookie 在跨站请求时不会被发送,用来防止CSRF攻击和用户追踪。 |
- SameSite的三个取值:
- Strict:完全禁止第三方
cookie
,跨站点时,任何情况下都不会发送cookie
。也就是说,只有当前网页的URL
与请求目标一致,才会带上cookie
。 - Lax: 大多数情况不发送第三方
cookie
,但导航到目标网址的get
请求(链接,预加载请求,GET表单)除外。 - None: 网站可以选择显式关闭
SameSite
属性,将其设为None
。不过,前提是必须同时设置Secure
属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
- Strict:完全禁止第三方
F12
查看cookie:
cookie安全
安全问题可以看我总结的这篇:前端安全—常见的攻击方式及防御方法
cookie跨域
跨域可以看这篇:九种跨域方法及原理
session是什么?
Session是保存在服务器记录客户状态的机制。客户端浏览器访问服务器的时候,服务器会为这次请求开辟一块内存空间
,这个对象便是Session 对象,存储结构为 ConcurrentHashMap
。Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。
session的创建
- 用户向服务器发送用户名和密码
- 服务器通过验证后,在当前对话(session)里面保存相关数据(比如用户角色,登录时间等)
- 服务器向用户返回一个
session_id
,写入cookie - 用户随后的每一次请求都会通过cookie,将
session_id
传回服务器 - 服务器收到
session_id
,找到前期保存的数据,由此得知用户的身份。
SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
cookie和session的区别
- 储存方式:cookie是服务端产生,储存在客户端;session储存在服务端
- 储存大小:单个cookie不超过4kb;session没有大小限制,但当访问过多,会占用过多的服务器资源
- 安全性:session更安全
- 储存内容:cookie只能保存字符串,以文本的方式;session通过类似
hashtable
的数据结构来储存,能支持任何数据类型 - 使用方式:
- cookie机制:如果不在浏览器设置过期时间,cookie被保存在
内存
中,cookie生命周期随浏览器的关闭而结束。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘
中,关闭浏览器后,cookie数据仍然存在,知道过期时间才消失。 - session机制:当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含session_id,如果有,服务器将根据id返回对应的session对象。如果没有session_id,服务器会创建新的session对象,并把session_id在本次响应中返回给客户端。
- cookie机制:如果不在浏览器设置过期时间,cookie被保存在
cookie、localStorage和sessionStorage
HTML5提供了两种在客户端存储数据的新方法:localStorage和sessionStorage
,挂载在window对象下。
webStorage
是本地存储,数据不是由服务器请求传递的。从而它可以存储大量的数据,而不影响网站的性能。
Web Storage
的目的是为了克服由cookie
带来的一些限制,当数据需要被严格控制在客户端上时,无须持续地将数据发回服务器。比如客户端需要保存的一些用户行为或数据,或从接口获取的一些短期内不会更新的数据,我们就可以利用Web Storage
来存储。
localStorage
生命周期是永久性的。localStorage
存储的数据,以“键值对”的形式存在。即使关闭浏览器,也不会让数据消失,除非主动的去删除数据。如果想设置失效时间,需自行封装。localStorage
在所有同源窗口中都是共享的。
- localstorage设置过期时间
localStorage
只能存储字符,存入时将对象转为json
字符串,读取时也要通过JSON.stringify
解析- set(key, value, expired) 设置过期时间
可以看这篇:localstorage设置过期时间
sessionStorage
sessionStorage
保存的数据用于浏览器的一次会话,当会话结束(关闭浏览器或者页面),数据被清空;SessionStorage的属性和方法与LocalStorage完全一样。
sessionStorage
特别的一点在于,即便是相同域名下的两个页面,只要它们不在同一个浏览器窗口中打开,那么它们的 sessionStorage 内容便无法共享;
localStorage
在所有同源窗口中都是共享的; cookie
也是在所有同源窗口中都是共享的。除了保存期限的长短不同,
cookie、localStorage和sessionStorage的区别
- 共同点:都是保存在浏览器端,且都遵循同源策略。
- 不同点:在于生命周期与作用域等不同
WebStorage 提供方法操作数据
sessionStorage和localStorage可使用的API都相同,其功能包括保存数据、读取数据、删除数据、得到索引的key值等。
- setItem(key, value) : 保存数据,以键值对方式储存信息
- getItem(key) : 获取数据,将键值传入,即可获取到对应的value值
- removeItem(key) : 删除单个数据
- clear() : 删除所有数据
- key(index) : 允许获取一个指定位置的键值
若是 localStorage 存满了,再往里存东西,或者要存的东西超过了剩余容量,会发生什么?存满了怎么办?
存不进去并报错(QuotaExceededError) localStorage 会可以将第一次请求的数据直接存储到本地,这个相当于一个 5M 大小的针对于前端页面的数据库,相比于 cookie(4k) 可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的。
但是一旦本地前端对数据存储要求比较高 比如聊天记录 等对缓存容积比较高的 比如超过5M 就要想别的办法了 比如优秀的indexDB
IndexedDB
IndexedDB
是一个运行在浏览器上的非关系型数据库,储存空间大,用于客户端存储大量结构化数据(包括文件和blobs) 。IndexedDB是一个基于JavaScript的面向对象的数据库,可以存字符串,也可以存二进制数据,数据以"键值对"的形式保存,不能有重复,否则会报错。除非被清理,否则一直存在。
- 键值对储存
- 异步
- 支持事务
- 同源策略
- 支持二进制储存
JWT(JSON Web Token)
互联网服务离不开用户认证。一般流程看上面session的创建
。
什么是Token?
-
Token的定义:
Token
是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token
便将此Token
返回给客户端,以后客户端只需带上这个Token
前来请求数据即可,无需再次带上用户名和密码。token
其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。 -
简单 token 的组成:
uid
(用户唯一的身份标识)time
(当前时间戳)sign
(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
-
token 的身份验证流程:
- 客户端使用用户名跟密码请求登录
- 服务端收到请求,去验证用户名与密码
- 客户端收到
token
以后,会把它存储起来,比如放在cookie
里或者localStorage
里 - 客户端每次向服务端请求资源的时候需要带着服务端签发的
token
- 服务端收到请求,然后去验证客户端请求里面带着的
token
,如果验证成功,就向客户端返回请求的数据
-
使用Token的目的:
Token
的目的是为了减少频繁的查询数据库,减轻服务器的压力。基于Token
用户认证是一种服务器无状态的认证方式,服务器不存放数据,所有数据都保存在客户端,每次请求都发回服务器,用解析token
的时间来换取session
的储存空间,从而减轻服务器的压力,减少频繁的查询数据库。token
完全由应用管理,所以它可以避开同源策略。
什么是 JWT?
JWT的原理
JWT
的原理是,服务器认证以后,生成一个 JSON
对象,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JSON
对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。服务器就不保存任何 session
数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
JWT的数据结构
-
Header(头部):
Header
部分是一个JSON
对象,描述JWT
的元数据,使用Base64
编码转成字符串。 -
Payload(负载):
Payload
是一个JSON
对象,用来存放实际需要传递的数据,使用Base64
编码转成字符串。- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号
-
Signature(签名):
Signature
是对前两部分的签名,防止数据篡改。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header
里面指定的签名算法(默认是 HMAC SHA256)产生签名。用"点"(.
)分隔拼接成字符串后返回给用户。
JWT的特点
- 默认不加密,也可以加密
- 可以用于认证,也可以用于交换信息。降低服务器查询数据库的次数,减小服务器压力
- 服务器无状态,因此无法在使用过程中废除某个
Token
,或者更改Token
的权限。即一旦JWT
签发了,在到期之前始终有效,除非服务器部署额外的逻辑 JWT
本身包含了认证信息,为保证安全性,有效期应设置得比较短- 为了减少盗用,
JWT
应使用HTTPS
协议传输
token存入localstorage还是cookie好
- 将Token存储于LocalStorage或SessionStorage
- 由于LocalStorage 和 SessionStorage 都可以被 javascript 访问,所以容易受到
XSS
攻击。尤其是项目中用到很多第三方的Javascript类库。另外,需要应用程序来保证Token只在HTTPS下传输。
- 由于LocalStorage 和 SessionStorage 都可以被 javascript 访问,所以容易受到
- 将Token存储于Cookie
- 优点:可以指定
httpOnly
,来防止被Javascript读取,也可以指定secure
,来保证token只在HTTPS下传输 - 缺点:不符合Restful 最佳实践。容易遭受
CSRF
攻击 (可以在服务器端检查 Refer 和 Origin) 推荐使用Cookie来存储Token:相比较而言,Web Storage比Cookie更容易受到攻击。
- 优点:可以指定
可以看这篇:JWT Token存储在Cookie还是LocalStorage
参考文献1:阮一峰老师的《Cookie的SameSite属性》