1. JWT生成token及过期处理方案
## 业务场景
在前后分离场景下,越来越多的项目使用token作为接口的安全机制,APP端或者WEB端(使用VUE、REACTJS等构建)使用token与后端接口交互,以达到安全的目的。本文结合stackoverflow以及本身项目实践,试图总结出一个通用的,可落地的方案。
## 基本思路
- 单个token
1. token(A)过期设置为15分钟
2. 前端发起请求,后端验证token(A)是否过期;如果过期,前端发起刷新token请求,后端设置已再次授权标记为true,请求成功
3. 前端发起请求,后端验证再次授权标记,如果已经再次授权,则拒绝刷新token的请求,请求成功
4. 如果前端每隔72小时,必须重新登录,后端检查用户最后一次登录日期,如超过72小时,则拒绝刷新token的请求,请求失败
- 授权token加上刷新token
用户仅登录一次,用户改变密码,则废除token,重新登录
## 1.0实现
1.登录成功,返回access\_token和refresh\_token,客户端缓存此两种token;
2.使用access_token请求接口资源,成功则调用成功;如果token超时,客户端
携带refresh\_token调用中间件接口获取新的access\_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,客户端收到该状态后,跳转到登录页;
如未过期,生成新的access\_token和refresh\_token并返回给客户端(如有可能,让旧的refresh\_token失效),客户端携带新的access\_token重新调用上面的资源接口。
4.客户端退出登录或修改密码后,调用中间件注销旧的token(使access\_token和refresh\_token失效),同时清空客户端的access\_token和refresh\_toke。
后端表
id user\_id client\_id client\_secret refresh\_token expire\_in create\_date del_flag
## 2.0实现
场景: access\_token访问资源 refresh\_token授权访问 设置固定时间X必须重新登录
1.登录成功,后台jwt生成access\_token(jwt有效期30分钟)和refresh\_token(jwt有效期15天),并缓存到redis(hash-key为token,sub-key为手机号,value为设备唯一编号(根据手机号码,可以人工废除全部token,也可以根据sub-key,废除部分设备的token。),设置过期时间为1个月,保证最终所有token都能删除),返回后,客户端缓存此两种token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果token超时,中间件删除access\_token(废除);客户端再次携带refresh\_token调用中间件接口获取新的access_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,删除refresh_token(废除); 客户端收到该状态后,跳转到登录页;
如未过期,检查缓存中是否有refresh\_token(是否被废除),如果有,则生成新的access\_token并返回给客户端,客户端接着携带新的access_token重新调用上面的资源接口。
4.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access\_token和refresh\_token(废除)),同时清空客户端侧的access\_token和refresh\_toke。
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
6.以上3刷新access_token可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:失效,长时间未登录,频繁刷新)
2.0 变动
1.登录
2.登录拦截器
3.增加刷新access_token接口
4.退出登录
5.修改密码
## 3.0实现
场景:自动续期 长时间未使用需重新登录
1.登录成功,后台jwt生成access\_token(jwt有效期30分钟),并缓存到redis(hash-key为access\_token,sub-key为手机号,value为设备唯一编号(根据手机号码,可以人工废除全部token),设置access_token过期时间为7天,保证最终所有token都能删除),返回后,客户端缓存此token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果token超时,中间件删除access\_token(废除),同时生成新的access\_token并返回。客户端收到新的access_token,
再次请求接口资源。
3.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access\_token(废除)),同时清空客户端侧的access\_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长时间未登录,频繁刷新)
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
3.0 变动
1.登录
2.登录拦截器
3.退出登录
4.修改密码
1.3 场景:token过期重新登录 长时间未使用需重新登录
1.登录成功,后台jwt生成access\_token(jwt有效期7天),并缓存到redis,key为 "user\_id:access\_token",value为access\_token(根据用户id,可以人工废除指定用户全部token),设置缓存过期时间为7天,保证最终所有token都能删除,请求返回后,客户端缓存此access_token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果token超时,中间件删除access\_token(废除),同时生成新的access\_token并返回。客户端收到新的access_token,
再次请求接口资源。
3.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access\_token(废除)),同时清空客户端侧的access\_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长时间未登录,频繁刷新)
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
1.3 变动
1.登录
2.登录拦截器
3.退出登录
4.修改密码
# 解决方案
2.0 场景: access\_token访问资源 refresh\_token授权访问 设置固定时间X必须重新登录
1.登录成功,后台jwt生成access\_token(jwt有效期30分钟)和refresh\_token(jwt有效期15天),并缓
存到redis(hash-key为token,sub-key为手机号,value为设备唯一编号(根据手机号码,可以人工废除全
部token,也可以根据sub-key,废除部分设备的token。),设置过期时间为1个月,保证最终所有token都
能删除),返回后,客户端缓存此两种token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果
token超时,中间件删除access\_token(废除);客户端再次携带refresh\_token调用中间件接口获取新的
access_token;
3.中间件接受刷新token的请求后,检查refresh_token是否过期。
如过期,拒绝刷新,删除refresh_token(废除); 客户端收到该状态后,跳转到登录页;
如未过期,检查缓存中是否有refresh\_token(是否被废除),如果有,则生成新的access\_token并返回给
客户端,客户端接着携带新的access_token重新调用上面的资源接口。
4.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access\_token和refresh\_token(
废除)),同时清空客户端侧的access\_token和refresh\_toke。
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
6.以上3刷新access_token可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(
拒绝的场景:失效,长时间未登录,频繁刷新)
2.0 变动
1.登录
2.登录拦截器
3.增加刷新access_token接口
4.退出登录
5.修改密码
3.0 场景:自动续期 长时间未使用需重新登录
1.登录成功,后台jwt生成access_token(jwt有效期30分钟),并缓存到redis(hash-key为
access_token,sub-key为手机号,value为设备唯一编号(根据手机号码,可以人工废除全部token,也可以
根据sub-key,废除部分设备的token。),设置access_token过期时间为1个月,保证最终所有token都能删
除),返回后,客户端缓存此token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果
token超时,中间件删除access\_token(废除),同时生成新的access\_token并返回。客户端收到新的
access_token,
再次请求接口资源。
3.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access_token(废除)),同时清
空客户端侧的access_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长
时间未登录,频繁刷新)
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
3.0 变动
1.登录
2.登录拦截器
3.退出登录
4.修改密码
4.0 场景:token过期重新登录 长时间未使用需重新登录
1.登录成功,后台jwt生成access_token(jwt有效期7天),并缓存到redis,key为
"user\_id:access\_token" + 用户id,value为access_token(根据用户id,可以人工废除指定用户全部
token),设置缓存过期时间为7天,保证最终所有token都能删除,请求返回后,客户端缓存此
access_token;
2.使用access\_token请求接口资源,校验成功且redis中存在该access\_token(未废除)则调用成功;如果
token超时,中间件删除access\_token(废除),同时生成新的access\_token并返回。客户端收到新的
access_token,
再次请求接口资源。
3.客户端退出登录或修改密码后,调用中间件注销旧的token(中间件删除access_token(废除)),同时清
空客户端侧的access_token。
4.以上2 可以增加根据登录时间判断最长X时间必须重新登录,此时则拒绝刷新token。(拒绝的场景:长
时间未登录,频繁刷新)
5.如手机丢失,可以根据手机号人工废除指定用户设备关联的token。
4.0 变动
1.登录
2.登录拦截器
3.退出登录
4.修改密码
## 最终实现
### 后端
1. 在登录接口中 如果校验账号密码成功 则根据用户id和用户类型创建jwt token(有效期设置为-1,即永不过期),得到A
2. 更新登录日期(当前时间new Date()即可)(业务上可选),得到B
3. 在redis中缓存key为ACCESS_TOKEN:userId:A(加上A是为了防止用户多个客户端登录 造成token覆盖),value为B的毫秒数(转换成字符串类型),过期时间为7天(7 * 24 * 60 * 60)
4. 在登录结果中返回json格式为{"result":"success","token": A}
5. 用户在接口请求header中携带token进行登录,后端在所有接口前置拦截器进行拦截,作用是解析token 拿到userId和用户类型(用户调用业务接口只需要传token即可),
如果解析失败(抛出SignatureException),则返回json(code = 0 ,info= Token验证不通过, errorCode = '1001');
此外如果解析成功,验证redis中key为ACCESS_TOKEN:userId:A 是否存在 如果不存在 则返回json(code = 0 ,info= 会话过期请重新登录, errorCode = '1002');
如果缓存key存在,则自动续7天超时时间(value不变),实现频繁登录用户免登陆。
6. 把userId和用户类型放入request参数中 接口方法中可以直接拿到登录用户信息
7. 如果是修改密码或退出登录 则废除access_tokens(删除key)
### 前端(VUE)
1. 用户登录成功,则把username存入cookie中,key为loginUser;把token存入cookie中,key为accessToken
把token存入Vuex全局状态中
2. 进入首页
2. 前端的token验证 (以vue为例)
1、第一次登录的时候,前端调后端的登陆接口,发送用户名和密码
2、后端收到请求,验证用户名和密码,验证成功,就给前端返回一个token
3、前端拿到token,将token存储到localStorage和vuex中,并跳转路由页面
4、前端每次跳转路由,就判断 localStroage 中有无 token ,没有就跳转到登录页面,有则跳转到对应路由页面
5、每次调后端接口,都要在请求头中加token
6、后端判断请求头中有无token,有token,就拿到token并验证token,验证成功就返回数据,验证失败(例如:token过期)就返回401,请求头中没有token也返回401
7、如果前端拿到状态码为401,就清除token信息并跳转到登录页面
3. NodeJS(Express框架)实现 Token 验证免密登录 (一)
看文章之前,强烈建议先把项目拉取下来!案例来自小弟的开源项目 “项目Github”
文章内容只是个人学习的一些总结经验,不具有权威性,这是 Node 服务端的实现,后面会写前端的实现
常见的 Token 验证方式种:
推荐阅读:
JWT 超详细分析
说一说几种常用的登录认证方式,你用的哪种
推荐阅读:
JSON Web Token 入门教程
JSON Web Token - 在Web应用间安全地传递信息
首先我们先安装 jsonwebtoken 和 express-jwt 这两个中间件
jsonwebtoken : 用于生成 Token 。它也有解析 Token 的功能
express-jwt : 用于解析 Token(比 jsonwebtoken 解决方便) , 它把解析之后的数据,存放到 requset.user 中
如果你看了上面 JWT 介绍的文章,就知道 JWT 是由三部分组成的,分别是 载荷(Payload) 、 头部(Header) 、 签名(Signature) 。
jsonwebtoken 给我们提供了 sign(payload, secretOrPrivateKey, [options, callback]) 方法。sign 方法对应的其实就是 JWT 签名(Signature) 的动作
payload:荷载 ,参数类型:对象secretOrPrivateKey:自定义的密钥,密钥属于敏感信息。参数类型:字符串options:可以配置 header 、荷载、指定算法类型。参数类型:对象callback:回调
眼尖的朋友应该发现, payload 和 options 两个 参数 都可以配置荷,下面有例子。根据自己的习惯选择就好
Payload 部分 JWT 规定了7个官方字段,这些字段都是可选字段。可直接以对象的形式传给 payload 参数。
options 中也可以接受以上七个字段,不过字段名称有所区别。
除此之后 options 提供了 algorithm 和 header ,分别对应使用的加密算法和 JWT 的 Header 部分,其实 algorithm 应该也是属于 Header 部分的。
说了这么多,其实我们一般常用的只有 exp(expiresIn) 和 algorithm 这两个字段,
例子一:
token 的有效时间是配置在 option 里
例子二:
我们也可以在 payload 里配置有效时间
jsonwebtoken 除了生成 token 外,还提供了解析验证 token 的方法, jwt.verify(token, secretOrPublicKey, [options, callback]) 。
这里就不演示了, 感兴趣的朋友可以参考文档: “JsonWebToken”
express-jwt 是针对 express 框架开发的 JWT Token 验证中间件。我先来简单说以下它的用法。
主要有两种方式,一种是哪些请求需要验证就往哪里加验证;另外一种是先给全部请求加上验证,再给不需要验证的请求配置 白名单 。
方式一:
看完上面的例子,很显然不符合我们的逾期,一个正常的项目有个几十个 api 是分分钟的事。我们不可能一个个给他加上检验
方式二:
这种方式是不是方便很多,而且更美观,维护起来也更方便
Token 解析出来的用户信息,默认存放在 req.user , 可以直接 req.user.userId 来使用生成 Token 时填进去的用户id
你也通过 requestProperty 和 resultProperty 来设置用户信息存放的对象。
这里就不展开,详细文档参考: express-jwt
可以使用 app.use() 来注册处理验证不通过的情况
到这里 Token 的生成、验证、检验不通过错误处理就完成了。 Token 生成一般是在登录之后生成,并返回给前端,前端拿到 Token ,并在每次请求 api 的时候携带上 Token , Token 就相当于这个用户的身份,不要轻易泄露。
Token一旦签发,不能主动让它失效,只能等待它有效期过才能失效。也就是说就算你修改了密码,之前的 Token 也还是有效的。你可以修改后端生成 Token 时使用的密钥,不让之前的 Token 检验通过,但是这就表示之前所有生成 Token 都失效了,做不到针对某个用户进行注销。这显然也不合适的。 所以用户修改密码时,前端一般都要清除之前保存的 Token,再重获取新的 Token
有朋友应该会想到在后端把 Token 储存起来,每一个用户对应一个 token。修改账号时,再生成一个新的 Token 覆盖之前的 Token,但这就违背了使用 Token 的目的,Token 的使用很大程度就为了减少服务器的压力。把尽可能多的信息存储在客户端而不是服务端。
使用 Token 可以防御 CSRF 攻击,之前写过一篇关于网络安全的文章,感兴趣的朋友可以看一下 “XSS 攻击、CSRF 攻击、SQL 注入、流量劫持(DNS 劫持、HTTP 劫持)—— 浏览器安全”
4. token什么意思 前端如何使用token
大家好,从网上找了很多关于token的文章,都是提到要生成一个token,然后前端每次请求的时候,要使用这个token,请问下如何在前端使用生成的token?
前端能就使用jQuery搞定,还是需要其他的前端框架配合?能有这方面的完整示例吗?
做后端的,对前端的东西有些不太懂,请见谅
先谢谢大家了!!
解决方案1:
一般是后端有个结构给你拿token的,然后你请求的时候,根据约定
把token
放在header中
放uri参数中
放body表单里
给后端
解决方案2:
因为http协议是无状态的 token是后台给你发的一个唯一标识 你再去访问后台时带上这个token 后台就知道你是谁了
同session的作用
解决方案3:
前台生成的token,可能会存在安全性问题吧
解决方案4:
你做后台应该很了解token才对呀。
用户登录后,生成一个session_id,即token,可以存在redis里。然后前端或客户端保存起来,存cookie或者LS都行,然后所有的请求作为基类参数带上(也有通过cookie带的),然后server端再取到后,验证你是不是你。
解决方案5:
使用领域很多,以表单为例子:
后台生成token.
前端打印表单,并且讲该token变成隐藏项。<input type="hide" value="{{token}}">
客户提交表单。
后台验证提交的token合法性。
验证成功,处理表单。验证失败,返回错误处理页面。
解决方案6:
token一般都是后端生成的,在登陆之后返回,前端保存token,之后每次请求都带上token来验证身份。
解决方案7:
问题是前端生成的token给后台有用吗
解决方案8:
一般token都是服务器端生成,做csrf的。我在补充下我见过前端生成的栗子,虽然没啥卵用,但让我废了好大的劲才发现。
譬如你有一个验证码的表单,你在传递验证码的时候,新增一个隐藏域,将验证码用你本地的js加密后,作为参数传递,这样在服务器端可以检测验证码是不是被篡改过。
但这样没啥卵用,因为在提交的时候用同样的js模拟即可。
解决方案9:
大多数情况下,token作为一种令牌,都是在服务器端生成,生成的方法很多,从简单点的对时间或者id或者两个混合起来进行哈希运算的值到自己设计更复杂的算法都可以,生成的目的是为了给前端下一次通信时使用这个token作为令牌,当作为一个请求资源的许可的标识,而服务器则会视这个token在一段时间内都是有效的,并且还可以额外看情况加上是否是同一个ip之类的其它的限制,从而防止某种资源被非法访问
偶有前端(包括本地客户端或者app)生成token的情况是已经约定好了一个好的加密机制,服务器可以信任客户端的这个输入的情况下可以由前端或者客户端生成
5. 鉴权操作流程(前端逻辑)
1.用户登录 调取接口 去获取对应的token,此时将token 存储在了sessionStorage中。项目的最开始是去获取当前用户的token。(base64加密),之后调用token有效时间和校验token是否失效。
2.公共请求方法 request 函数在请求头添加 token,即每次的相关请求都带有了当前用户的token信息,如果token在有效期内则可以正常请求。否则便会抛出异常。
3.假如token的有效时间是3600s,但是用户很久没有操作系统,会启动用户锁定状态,通过监控用户的操作时间差来判断锁定的状态。正常情况下token是不会过期的,因为在token的过期前几分钟内会进行token的更新操作,理论上token是不会过期的。所以当用户重新操作系统的时候,超过了一定时间之后需要用户重新登录系统来,其实也是调取的token的接口,去获取新的token,并替换之前的token。(但是这里没有考虑到的一种情况是如果项目一直在启动,但是服务重启了,或者其他原因导致前端的token在验证的时候不通过,这样就会导致页面的锁定状态无法打开,这时候前端做的处理是重新跳转到登录页,并删除token,就像第一次登录系统一样。)
6. 服务端如何校验token的是否正确
服务端第一次返回登录请求token的时候,服务端是校验了用户,并绑定了用户关系,之后才返回了这个token,也就说,服务端保存了token和登录用户账户的对应关系,不管之后的token怎么变,都是服务器校验上一次的token,并返回新的token,token和用户账户的对应关系一直在服务端更新并维护,对于客户端来讲,无需判断token是谁,服务器下发什么,就返回什么,检验是服务端处理
7. Token是什么和session、cookie相比,使用场景有什么区别
在Web开发领域,相信大家对于Cookie和Session都很熟悉,Cookie和Session都是会话保持技术的解决方案。随着技术的发展,Token机制出现在我们面前,不过很多开发者对于Token和Cookie、Session的区别及使用场景分辨不清。
Cookie和Session的用途 要知道我们访问网站都是通过HTTP协议或HTTPS协议来完成的, HTTP协议它本身是无状态的协议 (即:服务器无法分辨哪些请求是来源于同个客户)。而业务层面会涉及到客户端与服务器端的交互(同网站下多个页面间能共享数据),此时服务器端必须要保持会话状态,这样才能进行用户身份的鉴别。
由于HTTP无状态的特性,如果要实话客户端和服务器端的会话保持,那就需要其它机制来实现,于是Cookie和Session应运而生。
通常情况下, Session和Cookie是搭配在一起使用的 。
Token是什么上面说到的Session和Cookie机制来保持会话,会存在一个问题:客户端浏览器只要保存自己的SessionID即可,而 服务器却要保存所有用户的Session信息,这对于服务器来说开销较大,而且不利用服务器的扩展 (比如服务器集群时,Session如何同步存储就是个问题)!
于是有人思考,如果把Session信息让客户端来保管而且无法伪造不就可以解决这个问题了?进而有了Token机制。
Token俗称为“令牌” ,它的构成是:
Token机制下的认证流程 Token机制其实和Cookie机制极其相似 ,主要有以下流程:
1、用户登录进行身份认证,认证成功后服务器端生成Token返回给客户端;
2、客户端接收到Token后保存在客户端(可保存在Cookie、LocalStorage、SessionStorage中);
3、客户端再次请求服务器端时,将Token作为请求头放入Headers中;
4、服务器端接收请求头中的Token,将用户参数按照既定规则再进行一次签名,两次签名若一致则认为成功,反之数据存在篡改请求失败。
(生成签名示例图)
(验证签名示例图)
Token与Cookie+Session的区别Cookie其实也充当的是令牌作用,但它是“有状态”的; 而Token令牌是无状态的,更利于分布式部署。
session和cookie
在讲Token之前,先简单说说什么是session和cookie。
Token
但是这里会有个问题, 服务器要保存所有用户的session信息,开销会很大,如果在分布式的架构下,就需要考虑session共享的问题,需要做额外的设计和开发 ,例如把session中的信息保存到Redis中进行共享;所以因为这个原因,有人考虑这些信息是否可以让客户端保存,可以保存到任何地方,并且保证其安全性,于是就有了Token。
Token是服务端生成的一串字符串,可以看做客户端进行请求的一个令牌。
基于Token的认证流程
整体的流程是这样的:
总结 希望我的回答,能够帮助到你!我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。
Token顾名思义就是令牌、凭证、钥匙 。只有这把钥匙,你才能打开门。token一般都是服务端生成,比如一个web系统,用户登录的时候,服务端校验用户名密码通过以后,会生成一个token,同时会生成refreshToken和一个过期时间。然后将refreshToken和token返回给客户端。客户端会将token保存下来。后续所有的请求都会携带这个token。服务端会判断当前token是否存在已经是否过期。如果token不存在或者过期就会拒绝本次请求。如果token过期怎么办,就用refreshToken刷新时间。当然这里可能还有别的方案。比如只生成token,每次请求的时候都刷新过期时间。如果长时间没有刷新过期时间,那token就会过期。
session就是回话,这是服务端的一种操作。当你第一次访问一个web网站的时候,服务端会生成一个session,并有一个sessionid和他对应。这个session是存储到内存中的,你可以向这个session中写入信息,比如当前登录用户的信息。sessionid会被返回到客户端,客户端一般采用cookie来保存。当然这个cookie不用人为写入。用tomcat容器来举个例子。 当后端调用HttpServletRequest对象的getSession的方法的时候,tomcat内部会生成一个jsessonid(tomcat sessionid的叫法)。这个jsessonid会随本次请求返回给客户端。响应头信息
这个jessionid就会写到cookie中。之后jessionid就会通过cookie传递到服务端。
这里我们就会很清楚了, session的数据是存储到内存中。那问题就来了,如果我们的服务是分布式部署,有多台机器的话,可能我们第一次登陆的时候,我们把用户的信息存储到了session,但是后面的请求到了B机器上,那B机器是获取不到用户的session的。另外就是session存储在内存中,那服务器重启,session就丢失了,这就是他的弊端。现在有一些技术,例如session共享、iphash、session持久等也可以解决上述问题 。
cookie是浏览器的一种策略。上述讲到了sessionid就是存储在cookie中的。我们知道http协议是无状态的,cookie就是用来解决这个问题的。cookie中可以用来保存服务端返回的一些用户信息的,例如前文提到的token、sessionid。每一次的请求,都会携带这些cookie。服务端从请求头中取到cookie中的信息,就可以识别本次请求的来源,这样,http是不是就变成有状态的了。
这里说几点cookie注意事项。
1、cookie存放在客户端,所以是不安全的。人为可以清除
2、cookie有过期时间设定。如果不设置过期时间,说明这个cookie就是当前浏览器的会话时间,浏览器关了,cookie 就存在了。如果有过期时间,cookie就会存储到硬盘上,浏览器关闭不影响cookie。下次打开浏览器,cookie还存在
3、cookie有大小的限制,4KB。
这个问题,网上有很多的答案,相信都看过了,估计也没有看明白。所以我就不去网上复制了,用自己的话,尽量说通俗,说重点。
cookie和session实际上是同一套认证流程,相辅相成。session保存在服务器,cookie保存在客户端。最常见的做法就是客户端的cookie仅仅保存一个sessionID,这个sessionID是一个毫无规则的随机数,由服务器在客户端登录通过后随机生产的。往后,客户端每次访问该网站都要带上这个由sessionID组成的cookie。服务器收到请求,首先拿到客户端的sessionID,然后从服务器内存中查询它所代表的客户端(用户名,用户组,有哪些权限等)。
与token相比,这里的重点是,服务器必须保存sessionID以及该ID所代表的客户端信息。这些内容可以保存在内存,也可以保存到数据库(通常是内存数据库)。
而token则可以服务器完全不用保存任何登录信息。
token的流程是这样的。客户端登录通过后,服务器生成一堆客户端身份信息,包括用户名、用户组、有那些权限、过期时间等等。另外再对这些信息进行签名。之后把身份信息和签名作为一个整体传给客户端。这个整体就叫做token。之后,客户端负责保存该token,而服务器不再保存。客户端每次访问该网站都要带上这个token。服务器收到请求后,把它分割成身份信息和签名,然后验证签名,若验证成功,就直接使用身份信息(用户名、用户组、有哪些权限等等)。
可以看出,相对于cookie/session机制,token机制中,服务器根本不需要保存用户的身份信息(用户名、用户组、权限等等)。这样就减轻了服务器的负担。
我们举个例来说,假如目前有一千万个用户登录了,在访问不同的网页。如果用cookie/session,则服务器内存(或内存数据库)中要同时记录1千万个用户的信息。每次客户端访问一个页面,服务器都要从内存中查询出他的登录信息。而如果用token,则服务器内存中不记录用户登录信息。它只需要在收到请求后,直接使用客户端发过来的登录身份信息。
可以这么说, cookie/session是服务器说客户端是谁,客户端才是谁。而token是客户端说我(客户端)是谁,我就是谁 。当然了,token是有签名机制的。要是客户端伪造身份,签名通不过。这个签名算法很简单,就是将客户端的身份信息加上一个只有服务器知道的盐值(不能泄露),然后进行md5散列算法(这里只是简化,方便理解,实际细节要稍复杂一些)。
cookie/session在单服务器,单域名时比较简单,否则的话,就要考虑如何将客户端的session保存或同步到多个服务器。还要考虑一旦宕机,内存中的这些信息是否会丢失。token因为服务器不保存用户身份,就不存在这个问题。这是token的优点。
token因为服务器不保存用户身份信息,一切都依赖当初那个签名。所以存在被盗用的风险。也就是说一旦盗用,服务器可能毫无办法,因为它只认签名算法。而session机制,服务器看谁不爽,可以随时把他踢出(从内存中删掉)。正是因为如此,token高度依赖过期时间。过期时间不能太长。过期短,可以减少被盗用的风险。
除了上面所说的,我个人认为,如果开发的系统足够小,倾向于使用cookie/session。如果系统同时登录用户多,集群服务器多,有单点登录需求,则倾向于使用token。
万维网的发展 历史
Token, 令牌,代表执行某些操作的权利的对象。
token主要用于鉴权使用,主要有以下几类:
cookie主要是网站用于在浏览器临时存放的数据,包括浏览器缓存数据以及服务器设定的一些数据,主要存放在浏览器端。
session主要用于保存会话数据,一般存储在服务器端,同时每一条session对用一个sessionID,sessionID是存放在浏览器的cookie中。
传统上的会话登陆和鉴权主要用session加cookie实现,随着分布式系统的快速演进,尤其是微服务的应用,token+cookie的授权访问机制得到亲睐,通常在用户登录后,服务器生成访问令牌(Access token),浏览器存储cookie中,在每次请求资源时都会在请求头中带上token,用于服务器授权访问使用。
Token和session都是web网站的会话保持、认证的解决方案;
既然都一样为什么还有token的说法。
从token产生的背景说起1.移动端应用使得服务器端Session失效
2.分布式系统中Session无法共享
所以说session对于以上两种情况无效了,所以有了Token的说法
那么什么是token,token长什么样子?先给大家一个直观的感受
token:PC-3066014fa0b10792e4a762-23-20170531133947-4f6496
说白了token保存就是用户的信息(不能保存密码等敏感信息)
token的组成:
客户端标识-USERCODE-USERID-CREATIONDATE-RONDEM[6位]
USERCODE,RONDEM[6位]经过MD5加密就变成了以上字符串
token的请求流程
请求流程解析
1.前端用户发送登录信息至认证系统
2.验证用户登录信息,判断用户是否存在
3.如果用户存在,生成token信息(客户端标识-USERCODE-USERID-CREATIONDATE-RONDEM[6位]),并存储在redis中
4.并将该token返回前端,附加至header
验证token客户端
将token附加至header
服务端
最后总结一下
一般的垂直架构项目使用Session没有任何问题,但是分布式项目或涉及到移动端则考虑使用token。
session
session的中文翻译是“会话”,当用户打开某个web应用时,便与web服务器产生一次session。服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
cookie
cookie是保存在本地终端的数据。cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
cookie的组成有:名称(key)、值(value)、有效域(domain)、路径(域的路径,一般设置为全局:"")、失效时间、安全标志(指定后,cookie只有在使用SSL连接时才发送到服务器(https))。下面是一个简单的js使用cookie的例子:
用户登录时产生cookie:
document.cookie = "id="+result.data['id']+"; path=/";
document.cookie = "name="+result.data['name']+"; path=/";
document.cookie = "avatar="+result.data['avatar']+"; path=/";
使用到cookie时做如下解析:
var cookie = document.cookie;var cookieArr = cookie.split(";");var user_info = {};for(var i = 0; i < cookieArr.length; i++) {
user_info[cookieArr[i].split("=")[0]] = cookieArr[i].split("=")[1];
}
$('#user_name').text(user_info[' name']);
$('#user_avatar').attr("src", user_info[' avatar']);
$('#user_id').val(user_info[' id']);
token
token的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库
cookie 和session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、所以个人建议:
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中
token 和session 的区别
session 和 oauth token并不矛盾,作为身份认证 token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击,而session就必须靠链路层来保障通讯安全了。如上所说,如果你需要实现有状态的会话,仍然可以增加session来在服务器端保存一些状态
App通常用restful api跟server打交道。Rest是stateless的,也就是app不需要像browser那样用cookie来保存session,因此用session token来标示自己就够了,session/state由api server的逻辑处理。 如果你的后端不是stateless的rest api, 那么你可能需要在app里保存session.可以在app里嵌入webkit,用一个隐藏的browser来管理cookie session.
Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓Session 认证只是简单的把User 信息存储到Session 里,因为SID 的不可预测性,暂且认为是安全的。这是一种认证手段。 而Token ,如果指的是OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对App 。其目的是让 某App有权利访问 某用户 的信息。这里的 Token是唯一的。不可以转移到其它 App上,也不可以转到其它 用户 上。 转过来说Session 。Session只提供一种简单的认证,即有此 SID,即认为有此 User的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方App。 所以简单来说,如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了。
token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件;cookie就是写在客户端的一个txt文件,里面包括你登录信息之类的,这样你下次在登录某个网站,就会自动调用cookie自动登录用户名;session和cookie差不多,只是session是写在服务器端的文件,也需要在客户端写入cookie文件,但是文件里是你的浏览器编号.Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。
想要全面深入地掌握Token,我们需要先了解这些:Token的概念、身份验证过程、实现思路、使用场景,以及Cookie、Session、Token的区别。
Token是验证用户身份的一种方式,简称做“令牌”。最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐,以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库。
每一次请求都需要Token,Token应该在HTTP的头部发送,从而保证Http请求无状态。我们同样通过设置服务器属性Access-Control-Allow-Origin:* ,让服务器能接受到来自所有域的请求。
需要注意的是,在ACAO头部标明(designating)*时,不得带有像HTTP认证,客户端SSL证书和cookies的证书。
当我们在程序中认证了信息,并取得Token之后,我们便能通过这个Token做许多的事情。我们甚至能基于创建一个基于权限的token传给第三方应用程序,这些第三方程序能够获取到我们的数据(当然只有在我们允许的特定的token)。
Cookie和Session的区别
综合以上考量,建议方案:
Session和Cookie取长补短、配合使用,将登陆信息等重要信息存放为Session,其他信息如果需要保留,可以放在Cookie中。
Session和Token并不矛盾,作为身份认证,Token安全性比Session高,因为Token发送的每个请求都带有签名,能防止监听,以及重放攻击。而session就必须靠链路层来保障通讯安全。如上所说,如果需要实现有状态的会话,可以通过增加session,在服务器端保存一些状态。
App通常用Restful API跟server打交道。Rest是Stateless的,也就是App不需要像Browser那样用Cookie来保存Session,因此使用Session Token来标示就足够了。Session/state由API Server的逻辑处理。如果后端不是Stateless的rest API,那么可能需要在App里保存Session,可以在App里嵌入webkit,用一个隐藏的Browser来管理Cookie Session.
Session是一种HTTP存储机制,目的是为无状态的HTTP提供持久机制。 所谓的Session认证,只是简单的把User信息存储到Session里,因为SID的不可预测性,暂且认为是安全的,这是一种认证手段。
Session只提供一种简单的认证,即有此SID,以及User的全部权利。是需要严格保密的,这个数据只在使用站点保存,不可共享给其它网站或者第三方App。所以简单来说,如果你的用户数据可能需要和第三方共享,或者允许第三方调用API接口,则使用Token,如果只是自己的网站或App应用,使用什么都OK。
Token,指的是OAuth Token或类似的机制的话,提供的是认证和授权 ,认证是针对用户,授权是针对App。其目的是让某App有权利访问某用户的信息,这里的Token是唯一的,不可以转移到其它App上,也不可以转到其它用户上。
Token就是令牌,比如你授权(登录)一个程序时,他就是个依据,判断你是否已经授权该软件。Cookie就是写在客户端的一个txt文件,记录下用户的访问、登录等信息,下次用户再登录某个网站时,服务器接收到请求,就会自动调用Cookie,自动登录用户名。
Session和Cookie差不多,只是Session是写在服务器端的文件,也需要在客户端写入Cookie文件,但是文件里是用户的浏览器编号.Session的状态是存储在服务器端,客户端只有session id,而Token的状态是存储在客户端的。
以上,是关于Token、Session、Cookie的知识点介绍,更加深入的详解,感兴趣的童鞋,可查看我持续分享的【BAT架构技术专题合集500+】,回复【架构】,即可领取。
Token是什么?
Token是令牌、凭证、钥匙,在Web领域中进行 身份验证 ,关键点在验证!!,说验证就必须了解一下Web领域的发展史呢。
2. 随着人们需求的改变,比如在线购物系统,需要登陆的网站等,这时候需要进行 交互 性,服务器接收到请求的时候,要根据你是否登陆,以及判断你是谁,来给你响应,这时候问题就来了,怎么知道每次请求的谁呢,所以就出来了一个 会话标识(session id),就是一个随机的字符串 ,每个人登陆的时候,服务器都会返回一个会话标识,这是再请求的时候,只要带上会话标识,服务器就知道请求的是谁。
3. 这样子每个人只要保存自己的session id就可以啦,服务器要保存 所有人 的session id!!!如果有成千上万的人访问服务器,那对服务器是巨大的开销,严重限制了服务器端的扩展能力,比如机器A跟机器B组成了服务器集群,那么访问了机器A,会话标识在机器A上,如果转到机器B,就不能访问了,也许会说,那复制呢,机器A搬到机器B,也有说统一把标识放在一个机器上,但是万一这个机器挂了呢,那体验就很差了。
4. 这个时候就有人想,用户自己保存自己的标识,就是Token,访问的时候带上这个Token,这个 Token是用户id+签名 ,验证时,服务器只要相同的算法和服务器才知道的密钥进行签名,如果结果跟Token中的签名一样,那就可以证明是登陆过的用户。
这样一来,服务器不保存session id,只要生成Token,访问时,只要对Token进行判断,Token也是有有效期的,所以也要进行refreshToken的。
Token,Cookie,Session三者使用场景的区别?Token主要是Web领域的身份认证 ,最常见的就是Web API这个功能:
Cookie 就是饼干, 它是服务器生产,永久保存在浏览器的数据,以kv的形式 ,你可以打开你的浏览器(这里以win10 edge为例),点击上方的三个点的按钮,再点击更多工具,再点击开发人员工具,再点击网络,此时内容选择文档,然后刷新页面,找Cookie即可。
Session是会话标识,是服务器用来判断正在会话的用户是谁,服务器生产的随机数 ,保存在服务器中,用户端也要进行保存,虽然能实现会话的共能,但对服务器的扩展能力限制,同时当服务器是两台机器组成以上的时候,会导致两台机器以上保存的session同步问题,会导致用户体验极差。
Token跟Session最大的区别就是Token服务端不用保存,同时是通过签名等技术实现的,Session因为是随机数,导致服务器要进行保存。
8. Token 原理解读
上一篇文章我们了解了一下 cookie 与 session 的产生、作用与原理。尽管二者在历史中已经服役过很长一段时间,但不论什么技术,都会有后来的优秀者取而代之。
前面说到,cookie 因为是保存在客户端,所以有安全隐患,因而诞生了 session,session 保存在服务器端,相对安全很多。但 session 每次都要为用户开辟一个空间用于其身份校验,且每次浏览器的请求过来服务器都要进行校验请求,十分耗费服务器的空间与性能。
于是,另一种身份校验工具诞生了,这就是 token。
本质上还是用户身份验证的工具。但与 cookie、session 明文似的形式不同,token 是经过一系列加密手段加密过的,最后表现为一串“无意义”的字符串。但里面包含了许多信息,可能包括用户登录的终端的地址、用户身份 ID、时间戳以及一个签名。所谓签名就是信息发送者给这段信息进行签名,让信息接收方知道请求 token 是属于谁。可以理解为在你的身份证上签名字,证件加笔迹双重认证。
为了避免上述 CSRF 攻击,浏览器对网页资源的访问提出了限制,URL 请求必须是与页面一样来自同一 协议 、 域名 、 端口 才给予访问权限。这样三者相同的站点被认为是有相同的“源”,是一个独立的“域”。即使 “localhost” 与 “ip” 都指向了本机,但也会被认为是非同源。浏览器在某个“域”下不会执行其他“域”的脚本。因而这也产生了前端领域里一个重要的话题:跨域。
session 的产生来自于用户登录后服务器生成的一个 session 对象,保存在服务器端,这个 session 只适用于该“域”。但实际情况是,一个网站的请求大多数情况下都会跨域,每台服务器的端口不同,甚至是域名就不同,每当跨域时就会形成新的会话,生成新的 session,这是非常影响用户体验的,所以也会有许多保存、共享或中央存储 session 的方案。
但上述两种限制在 token 这里就不再是问题。
与 cookie 类似。
首先,用户输入账号密码,发起登录请求,服务器校验账号密码合法性,成功则返回 token 给客户端。
客户端收到响应后拿到 token,将其通过 localStorage 等本地存储方式进行保存。
当浏览器再次请求时,需要在请求头中添加 token,这样服务器在接收到请求后便可以识别该请求的身份是否合法,合法则返回响应数据。
在实际应用中,配合 axios 的请求拦截器使用起来会很方便:
这样,就不用每次请求都手动添加 token,axios 会自动帮助我们完成添加,十分方便。
其实前端程序员对 token 的涉及没有多深,只需要在需要授权的请求中携带 token 即可。token 的生成、加密等都是后端去处理。所以,这里也就不在赘述 token 的加密原理,以笔者的能力也很难去讲述清楚。
token 运用也不是上文中描述的那么简单,涉及到一些过期处理、refresh 等操作。这些日后有机会再详谈。
9. 前后端分离项目——登录Token校验思路
对token的校验分为前端和后端
前端: Vue-Cli 2.x + axios
后端:SpringBoot 2.3.4
这里的话,userToken和userId放到sessionStorage是关键步骤
后端主要是使用拦截器来进行请求的拦截和校验
解释一下思路:
这里的话,针对需要拦截的路径和需要放行的路径进行配置就行
关于redisTemple的引入这里就不再赘述。
到这里为止,前后端的token就都做完了,后面就再讲讲前端的一些其他思路吧
对于登录状态的判断,前端可以在router.foreach上对路由进行状态判定,从而实现页面程度的拦截(具体可以参考最后的参考文章2)
在使用拦截器后,会发现前端部分请求会无法正常到达后端,网络后发现是因为 axios发送正式请求前会先发送一个嗅探请求 ,而嗅探请求是不携带我们封装的header的,所以会导致部分请求会无法成功,解决的方式有很多种,这里的话是选择了在后端去直接处理
参考文章
1、SpringBoot加了拦截器后出现的跨域问题解析
https://blog.csdn.net/mrkorbin/article/details/104066979
2、Vue项目中实现用户登录及token验证
https://www.cnblogs.com/web-record/p/9876916.html
10. Vue项目中用户登录及token验证及流程图
在前后端完全分离的情况下,Vue项目中实现token验证大致思路如下:
简单举例说明:
① 调登录接口成功,在回调函数中将token存储到localStorage和vuex中(login.vue中)
② store文件夹下的index.js
③ 路由守卫(router文件夹下的index.js)
④ 请求头加token
⑤ 如果前端拿到状态码为401,就清除token信息并跳转到登录页面
流程图: