前端鉴权(cookie、session、token)

3 阅读7分钟

一、cookie

生成方式

1、服务端生成,在 Http响应头 Respond-Header 中 Set-Cookie

服务端可通过 Cookie类的构造函数和实例方法设置Cookie(key=value)并返回,

浏览器收到响应并发现响应头中有 Set-Cookie,浏览器就会把key、value保存在浏览器中。

在向同一个域名发起的后续请求中,浏览器会在请求头中加上该cookie字段(除非它过期了)。

服务器端通过设置Set-Cookie 的 ExpiresMax-Age两个属性来设置cookie的有效期。

Expires

Set-Cookie: sessionId=abc123; Expires=Thu, 01 Jan 1970 00:00:00 UTC;

 Expires 的值必须是一个完整的日期和时间,包括年、月、日、时、分、秒和时区。

  • Expires 值为 过去时,浏览器立马删除该Cookie;
  • Expires 值为 将来时,浏览器会在将来的那个时点删除该Cookie。

Max-Age

Set-Cookie: sessionId=abc123; Max-Age=0;

Max-Age的值是个秒值。

  • Max-Age = 0;浏览器立即删除该Cookie
  • Max-Age = -1; 浏览器重启后Cookie消失。关闭当前页签或者关闭浏览器,Cookie都会消失。
  • Max-Age=3600; 表示3600 秒(即 1H)内有效,超期失效。

同时设置这俩属性时,Max-Age 的优先级高于Expires

2、客户端生成

  • JavaScript 自带的 document.cookie。
  • 第三方依赖库:如js-cookie。

js操作

// 设置cookie
document.cookie = 'sessionId=abc123; path=/'
 
// 获取Cookie值
const getCookie = name => {
  var cookies = document.cookie.split(";");
  for (let i = 0; i < cookies.length; i++) {
    const [key, value] = cookies[i].split("=")
    if (key.trim() === name) {
      return value;
    }
  }
  return ""
};
 
 
// 删除cookie 
// 方式:将cookie的 过期时间(expires)属性指定为一个过去的时间
const deleteCookie = name => { 
  document.cookie = name + '=;expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';  
}
deleteCookie('password');

三方库操作

import Cookies from 'js-cookie';  
 
// 设置cookie  
Cookies.set('sessionId', 'abc123');  
  
// 获取cookie  
const sessionId = Cookies.get('sessionId');  
  
// 删除cookie  
Cookies.remove('sessionId');

安全风险

前端自己设置cookie存在以下安全问题‌:

  1. 隐私和安全问题‌:Cookie能够跟踪用户的浏览活动和个人信息,可能导致用户隐私的泄露。这些信息有可能被广告商、分析公司或其他第三方用于个人信息收集和定向广告,从而引发用户对隐私保护的担忧‌。
  2. 跨站点请求伪造(CSRF)攻击‌:恶意网站可以利用Cookie执行CSRF攻击,通过伪造用户的身份发送恶意请求,进行非法操作‌。
  3. ‌跨站脚本(XSS)‌攻击:如果Cookie没有设置HttpOnly标志,攻击者可以通过XSS攻击获取cookie信息,进而进行欺诈或非法操作‌。
  4. 敏感信息泄露‌:Cookie中存储的敏感信息(如用户登录凭证)在未加密的情况下,可能在传输过程中被截获‌。

防止cookie被盗用的方法

  1. 设置HttpOnly标志‌:通过设置HttpOnly标志,可以防止JavaScript脚本获取cookie值,降低XSS攻击的风险‌。
  2. 使用Secure标志‌:设置Secure标志后,cookie只能通过安全的HTTPS协议传输,防止在非安全连接中被截取或篡改‌。
  3. 使用加密技术‌:对存储在cookie中的敏感信息进行加密处理,即使被窃取也无法轻易解密‌。
  4. 定期更换cookie‌:通过定期更换cookie值,增加攻击者盗取后使用的难度‌。
  5. 设置SameSite属性‌:通过设置SameSite属性,限制cookie在跨站请求中的发送,增加对CSRF攻击的防御能力‌。

二、session

Cookie将内容(key、value等信息)通过响应头返回。如果这些内容不直接返回,而是保存在服务器端,给内容取个名字叫session上下文,给上下文取个id叫SessionId(随便叫什么id都行)。客户端跟服务器端通过SessionId关联。

SessionId通常以Cookie的形式存储在客户端。但Cookie并不是唯一方式,URL也可以。不过URL方式对用户不友好,所以基本上没有互联网项目会采用这种方案。

Session的问题

  • Session中保存的数据的大小要考虑到存储上限。
  • 依赖Session的关键业务一定要确保客户端开启了Cookie。
  • 在负载均衡的情况下,由于存在Web服务器内存中的Session无法共享,通常需要重写Session的实现。 
  • Session内容的丢失都是有原因的,通常都是由于Web服务器的重启造成的。

cookie或者sesssion,都是后端在主导,前端相关的动作都是由标准浏览器来完成。js在其中只是用户体验的工作,而不是逻辑功能层面的工作。

三、token

Token 是在服务端产生的。前端使用用户名/密码向服务端请求认证,服务端认证成功,那么服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。

Token的全称是JSON Web Token,简称jwt。

前端使用用户名/密码向服务端请求认证,服务端认证成功后,将用户id等信息进行打包,打包时利用secret(HS256算法、RS256算法等)加密,那么服务端会返回 Token 给前端。

前端拿到token,是解不开的,因为服务器肯定不会把secret传给前端,让前端去解。

前端可以在每次请求的时候带上 Token 证明自己的合法地位。后端在请求头(一般放在请求头)中拿到这个Token,进行验证。验证主要是两个层面:签名是否有效,是否过期。

四、宿主webview环境中token的使用

我们自己的app、小程序等经常会通过webview加载h5,并通过url参数带上sessionid的方式给h5应用进行鉴权。

因为在首页tab页中的webview容器重新加载url之后依然可以使用android中的返回键返回到上一个页面,而h5应用中我们经常会把获取的用户信息缓存起来,并根据是否有有效的用户信息来判断当前是否登录,于是可能就会造成h5页面的登录状态和宿主环境的登录状态不同步。

以小程序为例,考虑以下一些情况:

1、小程序环境已登录且登录会话在有效期内,初次加载h5带上有效的sessionid,h5根据sessionid获取用户信息并缓存到本地存储中,同时将sessionid保存到cookie或者sessionStorage中,此时h5应用登录状态为已登录;

2、h5应用跳转其他路由,链接上没有sessionid,h5应用根据缓存的sessionid调用接口,此时h5应用登录状态为已登录;

3、小程序环境退出登录,切换到webview所在的tab页面时,必须主动刷新webview容器url,此时h5应用强制刷新获取用户信息接口,并清理本地缓存的用户信息,用户信息获取失败,登录状态为未登录;

4、小程序环境登录会话过期,小程序调用后端接口刷新token,切换到webview所在的tab页面时,必须主动刷新webview容器url,并带上最新的sessionid,此时h5应用强制刷新获取用户信息接口,并清理本地缓存的用户信息,用户信息获取成功,登录状态为已登录;

5、小程序环境切换账号登录,切换到webview所在的tab页面时,必须主动刷新webview容器url,并带上最新的sessionid,此时h5应用强制刷新获取用户信息接口,并清理本地缓存的用户信息,最新的用户信息获取成功,登录状态为已登录;

参考:

前端鉴权方式

OAuth 2.0 的四种方式

傻傻分不清之 Cookie、Session、Token、JWT

前端鉴权的兄弟们:cookie、session、token、jwt、单点登录

前端鉴权方案

Token 认证的来龙去脉

快速上手Token登录认证

前端关于单点登录的知识

前端应该学习的Token登录认证知识

单点登录的三种实现方式

面试官:如何实现JWT鉴权机制?说说你的思路

关于鉴权,看懂这篇就够了

网络安全学习笔记——盗取Cookies跨站脚本(XSS)