15

所用技术:
前端:Vue(2.x)VuexVue-RouterNginxnode.js
后端:jsp

其实我也不想纠结这个问题,但是业务逼着我纠结这个问题!(或许很多人甚至不认为这是一个问题!你们觉得呢?)也可能因为我就是一个纠结的人,但是我希望项目体验最好~~~

下面我列出了三种方案:

图片描述

1.方案一:cookie(不设置过期时间)

个人不太喜欢这种方案,因为很多时候,我打开一个站点,登陆,复制url,然后关闭浏览器,粘贴url,还是能够访问的(不需要重新登陆),说明这些站点并未采用这种存储方式。欢迎补充你们的想法~~~

2.方案二:cookie(设置过期时间)

优点:过期浏览器自动删除
缺点:需要人为维护过期时间
缺点解决方案:如果用户在一定时间未使用系统(未交互),我们可以理解为,一定时间内用户没有向服务器发送请求,我们可以在axios拦截里面修改cookie的过期时间。

3.方案三:localStorage

重点是第三种方案,这也是项目目前采用的方式,但是因为业务导致我纠结了。

项目相关背景:

本地存储方式:localStorage
数据访问:axios,采用拦截器方式,如果api返回的代码为未登录,元素js跳转至登陆页面

业务需求:

用户登录成功(localStorage存储登录信息)——》判断是否设置支付密码(localStorage存储是否设置支付密码)——》如果已设置,跳转到主页;未设置,跳转到设置页。

成功跳转至主页(包含很多子页——子路由),基本每个子路由页面都会在进入后调用一些查询api,api拦截器里面返回如果未登录,再跳转至未登录。

需求就是这么简单,但是问题来了——有两种情况下显得问题比较“严重”

1、假设登录成功——》查询到未设置支付密码——》然后关闭浏览器,三天过后输入登录地址,因为localStorage未清空(也不能想cookie一样过期),所以会跳转到设置支付密码页面,因为这个页面不需要调用查询api,所以不会跳转至登录页,这个时候用户会输入“支付密码”,点击提交,然后会提示“用户未登录”,这样体验就不好了——用户的操作被浪费了……

2、三天过后,首页(包含登陆按钮)——》点击登陆,会自动跳到“设置支付密码”页面(注意,不是登录页哦),因为是已登录状态,用户设置支付密码保存——》又跳转到“登陆页”,这种情况太严重了,已客户的角度简直不可理解!

于是,我想到了cookie+设置过期时间的方式来处理,但是也有一些比较麻烦的事情;

  1. 每次交互(有api调用)都需要重新设置(多个)cookie的过期时间,虽然在拦截器里面可以处理,但是随着业务增加,可能需要更多的cookie;
  2. 并不是每个api调用都需要设置cookie的过期时间,例如:登陆失败,扫码,登陆时发送邮件api……,这样会显得非常凌乱;

那么,对于这种需求该如何进行本地存储才是最佳方案呢?

2017-12-28 提问
17 个回答
8

首先,是登录信息:

登录信息存在 cookie 中是一直以来的做法,而且实现的很好,不用考虑各种问题。
而 localStorage 是在这个 API 诞生之后,一些 JS 党带起来的另一种实现方式。

使用 localStorage,你需要在每次请求的时候,都手动带上这个信息,这大大增加了开发过程中带来的困难,非常麻烦,而且还要手动维护过期时间。
而使用 cookie 的话,只需要在后端的 Auth 模块放个设置 header 的代码即可,其他完全不用考虑。为什么:

  • 用户未登录的情况下,Auth 判断没有权限,设置个跳转到登录页(或者是其他逻辑,比如以访客身份浏览之类的)
  • 用户登录时:将账号和密码 POST 到 Auth 模块后,Auth 设置一个 header,设置 Cookie 及过期时间
  • 用户登录后,在 Cookie 的有效期内(设置了过期时间就是过期时间内,没设置就是浏览器关闭前),任何请求都会自动带上 cookie,完全不用人工干预(fetch 请求除外,需要额外指定配置)
  • 在用户自动带上 cookie 请求后,需要授权的请求一定会经过 Auth 模块,判断 cookie 是否有效(防止恶意无效的 cookie),若 cookie 无效,则设置 header 删除 cookie(可选步骤),并将用户重定向到登录页。若 cookie 有效,则设置 header,为 cookie 续期(cookie 内容都可以完全不变)。
Auth 模块:
if (POST 方法请求登录) {
  if (账号密码不正确) {
    return 重定向到登录页面,并提示错误
  }
  设置 cookie,并指定过期时间为当前时间 +n 天
  return Auth 模块逻辑结束,进入其他模块逻辑
}
if (没有 cookie) {
  return 重定向到登录页面
}
if (cookie 无效) {
  // 可选步骤:设置 cookie 过期时间为 -1 (删除 cookie)
  return 重定向到登录页面
}
// 带了有效的 cookie
设置 cookie 过期时间为当前时间 +n 天(为 cookie 续期)
return Auth 模块逻辑结束,进入其他模块逻辑

注意:只有请求 Auth 模块才会给 cookie 续期,其他模块不续。所以权限认证的模块都统一到一起了。
前端 js 什么都不用管,后端其他模块也什么都不用管

然后是楼主的问题:

登录/设置密码:这是两个模块,不能搞混概念!然后有一个权限管理模块(Auth)来管理。
首先,必须要用户登录才能操作,这里不管你把登录信息存储到哪里。
登录成功后,有两种选择:

  1. 跳到首页,然后判断没有设置密码,跳到设置密码页
  2. 直接判断是否设置密码,跳转到密码页。如果没有设置密码,强行进入主页,又需要判断然后跳到设置密码页。

可以看出来,首页中判断是否设置了密码是必须的,而登录页不是。
那么,将判断代码放在首页,登录成功后进入首页,首页判断没有设置密码再跳到设置页,设置成功后,再跳转到首页。这个逻辑没有问题,也不会出现重复登录的问题。

首页:
if (未登录) {
  return 跳转到登录页;
}
if (未设置密码) {
  return 跳转到设置密码页;
}
// 已成功登录并设置密码,显示页面。
---------------------------------
登录页:
if (已登录) {  // 防止用户将登录页加入书签,或者由于某些原因在已登录的情况下进入登录页
  return 跳转到首页;
}
// 未登录,显示登录页
登录成功后直接跳回首页。
---------------------------------
设置密码页:
if (已设置密码) {
  显示修改密码页
} else {
  显示设置密码页
}
设置成功后直接跳回首页。

我不知道楼主设置密码后为什么会跳转到登录页面?既然已经登录了,在设置密码后应该直接跳回首页啊?这应该是其他方面的逻辑问题,而不是登录信息存储方式的问题吧?

最后补充一点:关于什么时候用 cookie,什么时候用 ***Storage

注:上面的 ***Storage 包括了 localStorage 和 sessionStorage。

  • Cookie:
    Cookie 存储的数据量比较小,所以一般不会存大量数据。当你存储的内容在每次请求后端的时候都需要的情况下才需要放到 Cookie 中。比如登录信息、设置信息之类的。
    登录信息不用我说吧?肯定每次请求都要带上。
    设置信息,一般比如网站语言(中文、英文之类的),或其他要求后端动态渲染的设置。
  • ***Storage:
    这个是新的 API,一般用于在前端缓存一些数据时使用,这些数据一般是只在前端使用,而后端不使用的,所以不用每次都往后端发送。(或是前端做统计,后端只要一个统计结果之类的)
    比如一些网站提供的编辑器,自带草稿功能,每隔几秒钟或几分钟自动保存当前编辑的内容,刷新页面,或是把浏览器关掉重新打开编辑页面可以自动恢复之前编辑的内容的。
    这种信息就适合存放在 ***Storage 中。

还有其他的比如 web SQL、IndexedDB 两个数据库,这一般是用来做 HTML 5 应用的时候才会用到的。比如 PWA 或是 HTML5 页游之类的。(贪玩蓝月跟这没关系(╯‵□′)╯︵┻━┻)

最后说一句:所有逻辑全部都放到前端判断是非常错误的决定!

因为:前端的一切都是可以手改的啊!
不管是 cookie、localStorage 还是 sessionStorage,只要用户按下 F12,分分钟手改啊!
这个网站要登录?打开 F12,输入 document.cookie='isLoggedIn=true'; 或者 localStorage.setItem('loggedIn', 'true');,你就,登录成功???喵喵喵?
要设置密码?同样打开 F12 设置点东西。
这些判断放在前端是完全没有意义的啊喂!因为你不管前端判断不判断,都要过一遍后端判断才行啊!!!

充分利用前端,也不能滥用啊喂!!!(╯‵□′)╯︵┻━┻

P.S. 当然,这里我就不提 CSP 之类的网站安全规则了。。。又扯远了。。。

4

你的需求可以拆成两个部分:认证 和 鉴权
认证识别这个用户的身份
鉴权确定这个用户访问首页之后是否需要跳转到绑定支付页面

cookie 天生就是最适合做认证,除了你提出的能否设置过期时间上的区别, cookie 和 localStorage 最大的区别是:每次 request 都会自动在 header 中带上所有的 cookie。所以如果你为了鉴权,设置很多 cookie ,还会引发一个问题就是,每次 request 的包都会很大。

我的解决方案很简单:鉴权就应该交给后端去做。无论你多么细心的在 client 中维护用户权限标识,都需要确保一点:用户的真实权限是否保持同步?所以你还是需要向后端查询用户权限设置,干嘛不简单些:
用户认证之后,后端鉴权,提示是否跳转,或者干脆在 header 头中设置跳转链接。

1

建议登陆信息用 cookie。即设置过期时间的cookie ,看法是 cookie 默认会发送回到后端,这样方便后端读取, 而localStorage的话必须你自己实现传送到后端(一般由前端js实现)。
另外, cookie有时效,而localStorage如果不手动清理则永久保存。
如果要设置关闭网页/标签就失效, 请用SessionStorage。 这个类似你设置“不带时间的cookie”。

1

你都列的那么清楚了,还有什么好纠结。
不过期:localStorage或cookie
有过期,区分下:

关闭即失效,用sessionStorage
关闭过段时间失效,用cookie,毕竟localStorage要自己手动删

没有什么最佳方案,只有最合适的方案,localStorage、sessionStorage、cookie各有所长

1

为什么不把支付设置页放到主页里面?

1

题主能否更新下目前使用的方案,就我看法:

如果需求希望用户关闭浏览器后,cookie(不设置过期时间)这个选择就直接pass了,剩下两个方案的选择得看题主,我也无法分析出大概。

但是无论选择什么方法,题主所说的问题是可以避免的,我觉得这个问题的原因是由于题主过于依赖浏览器保存的cookie或者说是localStorage,当重新打开链接后,首先要做的是跟后端确认是否登录,而不是仅仅前端确认是否登录过。

0

你要先分清客户端与服务端对“是否已登录”的判断是否一致? 如果不一致,就肯定会出现 “访问页面时是已登录,但提交请求到服务器又返回未登录”的情况。

如果这两者对“是否登录”的判断是一致的,那无论你是用cookie,localstore,甚至是其它你任何能想到的办法都不会有问题。

你说的情况,登录成功后关掉浏览器三天后再访问登录页显示已登录,而提交设置支付密码时服务端又提示未登录,实际上就是服务端的与客户端对登录是否有效的判断不一致,像这种情况, 你又不想提交业务请求后再返回未登录

简单的做法,就是打开任一页面之前(比如vue的路由before),先判断token是否有效(简单的判断就是服务端生成token时增加过期时间加到token中,如果要确保登录态一致,那就向服务端查询),无效就是未登录,跳到登录页。

0

我觉的还是看产品需求, 但是现在一般的大型网站, 你关闭浏览器都是不会清空登录信息的, 只有登录信息失效后才会提醒你从新登录, 但是登录信息的时效是后台设置的, 你根本不必要纠结, 产品需要哪个就用哪个

0

保存在cookie里面有一个很大的好处就是发送请求自动带上登录信息 如果你发现这个好处都不够的话 你可以考虑存ls

0

还有一个sessionStorage可以了解下,做一下对比

0

楼主的问题看得很晕!!

cookie最大的好处是过期时间比较好控制,且兼容性好,
而localStorage最大的好处的容易较大,但兼容性较差。

而楼主的问题,最根本的是业务逻辑有问题:
设置支付密码之前竟然可以不“登录”?
没有登录,怎么设置支付密码?

正确的逻辑是:
打开设置支付密码页面的时候就要判断一下是否已经登录,而不是设置完成后再判断~

0

localStorage慎用。类似于密码是否设置这类标识位,为什么会存到前端来啊?这个不是应该实时从后端去获取吗?假如首次登陆成功后是密码未设置的状态,然后关闭浏览器,但是客户从其他别的渠道设置了密码,存到了localStorage的话,客户再次进入的时候不是还会再跳转到密码设置的页面吗?

0

想要比较安全,可以使用ip和来源,如果发生改变,重新登陆

0

一般都会加盐,加解密敏感数据的。前端既然要敏感数据, 那你的数据就得用签名算法加密然后再加上盐。这样能大幅度的确保数据的安全。

0

这个不应该是前端考虑的问题,前端管理非常不安全

后端给你一个token(就是一个个字符串),你保存起来就是了(cookie 和 localStorage 随意 或者其他位置)。你每次请求把这个token发送给后端就完事。
至于验证这个token是否可用?是否过期?是后端的事情。
这个token的算法,token的表示的含义,也是后端的事情。
前端当成一个标示处理就好了。

0

或许可以在存token到localStorage的时候,也同时把过期时间tokenExpireTime=new Date().getTime() + 864000000000 存到localStorage里去。判断是否已登录时就变成了localStorage.token === '' || localStorage.tokenExpireTime < new Date().getTime(),这种情况下就需要重新登录。这里仅为举例,可能getTime()那个方法用得不好

撰写答案

推广链接