登录功能的实现
-
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属性》