关于session:OB运维-连接-kill-中的-sessionid

作者:姚嵩 外星人... 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 背景:通过 obproxy 连贯 OB 后,发现: kill 命令使⽤ show processlist 中的 ID 能执⾏胜利, 使⽤ information_schema.processlist 或者 oceanbase.__all_virtual_processlist 中的ID进⾏kill是失败的。 于是就进⾏了各种连贯测试,解惑两个问题: kill中session_id的起源;是否能够⼀次性⼲掉⼀个租户的所有连贯;测试阐明:阐明:session_id 是 kill 语句的参数,session_id和下⽂中的ID是同⼀对象; 视图information_schema.processlist的数据来源于表oceanbase.__all_virtual_processlist 。 登陆命令阐明(以本⼈测试的环境为例):登陆observer: mysql -uroot@sys -p -P2881 -h ${oberver_ip} -c -A oceanbase 登陆obproxy: mysql -uroot@sys#yjn_test -p -P2883 -h ${obproxy_ip} -c -A oceanbase 测试案例登陆某个observer的节点:⽬标: 确认observer上 show processlist 表information_schema.processlist 表oceanbase.__all_virtual_processlist 获取的ID是否雷同? 执⾏语句: show processlist ;select * from information_schema.processlist ;select id,user,host,db,command,time,state,info from oceanbase.__all_virtual_processlist ;后果: 3个语句取得的ID是雷同的,能够通过上⾯3种⽅式获取session_id ; 登陆某个obproxy节点:⽬标: 确认obproxy上 ...

March 2, 2023 · 1 min · jiezi

关于session:了解-SessionLocatStorageCacheControlETag

cookie 与 session 有什么区别?因为 HTTP 协定是无状态的协定,所以服务端须要记录用户的状态时,就须要用某种机制来识具体的用户,这个机制就是 Session. 典型的场景比方购物车,当你点击下单按钮时,因为 HTTP 协定无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创立了特定的 Session,用用于标识这个用户,并且跟踪用户,这样才晓得购物车外面有几本书。这个 Session 是保留在服务端的,有一个惟一标识。在服务端保留 Session 的办法很多,内存、数据库、文件都有。集群的时候也要思考 Session 的转移,在大型的网站,个别会有专门的 Session 服务器集群,用来保留用户会话,这个时候 Session 信息都是放在内存的,应用一些缓存服务比方 Memcached 之类的来放 Session。 思考一下服务端如何辨认特定的客户?这个时候 Cookie 就退场了。每次 HTTP 申请的时候,客户端都会发送相应的 Cookie 信息到服务端。实际上大多数的利用都是用 Cookie 来实现 Session 跟踪的,第一次创立 Session 的时候,服务端会在 HTTP 协定中通知客户端,须要在 Cookie 外面记录一个 Session ID,当前每次申请把这个会话 ID 发送到服务器,我就晓得你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?个别这种状况下,会应用一种叫做 URL 重写的技术来进行会话跟踪,即每次 HTTP 交互,URL 前面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来辨认用户。 Cookie 其实还能够用在一些不便用户的场景下,构想你某次登陆过一个网站,下次登录的时候不想再次输出账号了,怎么办?这个信息能够写到 Cookie 外面,拜访网站的时候,网站页面的脚本能够读取这个信息,就主动帮你把用户名给填了,可能不便一下用户。这也是 Cookie 名称的由来,给用户的一点苦头。所以,总结一下:Session 是在服务端保留的一个数据结构,用来跟踪用户的状态,这个数据能够保留在集群、数据库、文件中;Cookie 是客户端保留用户信息的一种机制,用来记录用户的一些信息,也是实现 Session 的一种形式。 什么是 session?服务器通过 cookie 给用户一个 sessionID, ...

June 14, 2022 · 2 min · jiezi

关于session:cookitsession

April 10, 2022 · 0 min · jiezi

关于session:浅谈CookieSessionTokenJWT

什么是认证?简单明了的来说就是告知服务器你是谁(你的名字、性别...)互联网中的认证:用户名明码登录邮箱发送登录链接手机号接管短信验证码什么是受权?简单明了来说就是管理员赋予用户拜访的权限例如:手机下载新的app应用时会让你赋予它读取相干信息的权限,这就是受权实现受权的形式有:cookie、session、token、OAuth什么是凭证?凭证就是向服务器证实你本人自己而不是混充的,相似于身份证或者户口本Cookie是什么?HTTP 是无状态的协定(对于事务处理没有记忆能力,每次客户端和服务端会话实现时,服务端不会保留任何会话信息):每个申请都是齐全独立的,服务端无奈确认以后访问者的身份信息。所以服务器与浏览器为了进行会话跟踪(晓得是谁在拜访我),就必须被动的去保护一个状态,这个状态用于告知服务端前后两个申请是否来自同一浏览器。而这个状态须要通过 cookie 或者 session 去实现。每个 Web 站点能设置的 Cookie 总数不能超过 20 个cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保留在本地的一小块数据(不超过4KB)。cookie 是不可跨域的: 每个 cookie 都会绑定繁多的域名,一级域名和二级域名之间是容许共享应用的(靠的是 domain)。cookie时效性:目前有些 Cookie 是长期的,有些则是继续存在的。长期的 Cookie 只在浏览器上保留一段规定的工夫(个别是30分钟),一旦超过规定的工夫,该 Cookie 就会被革除Session是什么?在计算机中,尤其是在⽹络利用中,称为“会话管制”。Session是一种HTTP存储机制是有状态的协定,目标是为无状态的HTTP提供的长久机制。所谓Session认证只是简略的把User信息存储到Session里,因为SID的不可预测性,暂且认为是平安的。这是一种认证伎俩。Session是基于Cookie实现的,要记住session是存储在服务器的然而sessionID是存储在浏览器的cookie上的SessionID 是连贯 Cookie 和 Session 的一道桥梁Token是什么?token就是发送申请的凭证,他是怎么实现的呢?能够将它看作是一串加密的字符串,当用户第一次登录胜利时服务器会生成一串由uid(用户惟一的身份标识)、time(以后工夫的工夫戳)、sign(签名,token 的前几位以哈希算法压缩成的肯定长度的十六进制字符串)组成的字符串返回给前端,前端再发送每一次申请时将这一字符串带到申请头中,而后服务器会从数据库中查问此token是否非法并做出相应的响应。值得一提的是每次申请都会去校验此token的合法性想想如果用户多了申请质变大那么始终操作库会给库带来十分大的压力。那么如何解决呢?能够将每次生成的toekn存储在redis中并设置好过期工夫(这是一个解决办法然而也不太好,数据量大了也会造成压力),当然还有几个好的解决办法不过都是存储在缓存中,大同小异这里就不再一一叙述JWT是什么?Json Web Token 简称JWT 目前最风行的跨域认证解决方案JWT也是一串字符串,两头用点(.)分隔成三个局部,别离是: Header(头部)Payload(负载)Signature(签名)写成一行就是上面这样Header.Payload.Signature 接下来别离介绍着三个局部 Header: {"alg": "HS256","typ": "JWT"} 其中 alg:示意应用的算法默认是HS256 ,typ:示意token类型 而后再用Base64URL 算法进行秘密生成一串字符串Payload: 也是一个 JSON 对象,用来寄存理论须要传递的数据。JWT 规定了7个官网字段,供选用。{ iss (issuer):签发人 exp (expiration time):过期工夫 sub (subject):主题 aud (audience):受众 nbf (Not Before):失效工夫 iat (Issued At):签发工夫 jti (JWT ID):编号}当然也能够在这一部分存储本人定义的端Signature:这一部分就是 base64UrlEncode(header)+ base64UrlEncode(palyoad)+密钥应用alg的算法进行加密这三者在拼接成一个字符串就是jwt ...

March 22, 2022 · 1 min · jiezi

关于session:Session-解决了什么问题

一、服务器怎么判断用户的登录状态?1、简略分为这么几步:用户通过浏览器拜访网站,服务器承受到申请后,生成一个有时长限度的 机密口令,返回给用户,同时服务器也有备份了 机密口令;浏览器承受到 机密口令 并保留到本地;用户再次应用浏览器发出请求时,会取出 机密口令 一起发送给服务端;服务器承受到 机密口令 后,就开始在备份中寻找,有没有雷同的且没有过期的 机密口令 。如果有,那么阐明用户曾经登录。2、Session 与 CookieSession:是下面提到的 服务端 生成和存储 机密口令 的过程;Cookie:是下面提到的 浏览端 存储和发送 机密口令 的过程;二、具体实现过程1、浏览器 怎么接管 服务器 生成的 机密口令?浏览器 和 服务器 之间是通过 HTTP 或 HTTPS 协定进行传输数据的,那么就在 HTTP 协定的 Header 减少一个字段用来传输 机密口令,这个字段就是 Set-Cookie,浏览器会主动保留此字段的数据。 2、服务器 怎么接管 浏览器 回传的 机密口令?浏览器 会在 HTTP 协定的 Header 减少一个字段用来发送 机密口令,这个字段就是 Cookie,服务器通过此字段来接管 机密口令 并进行下一步操作。 3、怎么保障其传输的安全性?为了减少安全性,就给这个 明码口令 减少了很多属性,譬如:Secure 、 HttpOnly、Domain、Path等,通常咱们把 明码口令 + 属性 这个格局的数据称之为 Cookie。Cookie 应用有其相应的规定,详情看这里! 三、怎么应用 session 到我的项目中?能够通过现有的一些 库 来减少session到我的项目中,上面举荐几个不同场景下的 session 库: ...

November 26, 2021 · 1 min · jiezi

关于session:工具库系列之分布式-Session-Golang库

分布式 Session Golang库 English README 反对多个 Web 服务共享 Session 令牌 token,这样能够实现多个服务间共享状态。 当初 Session 令牌能够存储在: 单机模式的 Redis。哨兵模式的 Redis。什么是哨兵,咱们晓得 Redis 有主从复制的性能,主服务器提供服务,从服务器作为数据同步来进行备份。当主服务器挂掉时,哨兵能够将从服务器晋升到主角色。如何应用很简略,执行: go get -v github.com/hunterhug/gosession外围 API: // 分布式Session治理// Tokentype TokenManage interface { SetToken(id string, tokenValidTimes int64) (token string, err error) // 设置令牌,传入用户ID和令牌过期工夫,单位秒,会生成一个Token返回,登录时能够调用 RefreshToken(token string, tokenValidTimes int64) error // 刷新令牌过期工夫,令牌会持续存活 DeleteToken(token string) error // 删除令牌,退出登录时能够调用 CheckToken(token string) (user *User, exist bool, err error) // 查看令牌是否存在(查看会话是否存在) CheckTokenOrUpdateUser(token string, userInfoValidTimes int64) (user *User, exist bool, err error) // 查看令牌是否存在(查看会话是否存在),并缓存用户信息,如果有的话,默认不更新用户信息,可设置ConfigGetUserInfoFunc ListUserToken(id string) ([]string, error) // 列出用户的所有令牌 DeleteUserToken(id string) error // 删除用户的所有令牌 RefreshUser(id []string, userInfoValidTimes int64) error // 批量刷新用户信息,如果有的话,默认不缓存用户信息,可设置ConfigGetUserInfoFunc DeleteUser(id string) error // 删除用户信息,默认不缓存用户信息,可设置ConfigGetUserInfoFunc AddUser(id string, userInfoValidTimes int64) (user *User, exist bool, err error) // 新增缓存用户信息,默认不缓存用户信息,可设置ConfigGetUserInfoFunc ConfigTokenKeyPrefix(tokenKey string) TokenManage // 设置令牌前缀 ConfigUserKeyPrefix(userKey string) TokenManage // 设置用户信息前缀,默认不缓存用户信息,可设置ConfigGetUserInfoFunc ConfigDefaultExpireTime(second int64) TokenManage // 设置令牌默认过期工夫 ConfigGetUserInfoFunc(fn GetUserInfoFunc) TokenManage // 设置获取用户信息的函数 SetSingleMode() TokenManage // 是否独占单点登录,新生成一个令牌,会挤掉其余令牌}// 用户信息,存token在缓存里,比方redis// 如果有设置ConfigGetUserInfoFunc(fn GetUserInfoFunc),那么同时也会缓存该用户信息,你能够在函数 type GetUserInfoFunc func(id string) (*User, error) 里将业务用户信息存入 Detail 并返回。type User struct { Id string `json:"id"` // 用户标记,惟一 TokenRemainLiveTime int64 `json:"-"` // token还有多少秒就过期了 Detail interface{} `json:"detail"` // 能够寄存用户业务信息}例子: ...

September 1, 2021 · 3 min · jiezi

关于session:有图有真相带你实现现流行的权限验证

摘要:本文通过实例演示JWT实现登录受权流程。通过与传统的session、cookie和token机制进行比照,剖析其中的优缺点。JWT是什么JSON Web Token(缩写 JWT)是目前最风行的跨域认证解决方案。它是有三局部组成,示例如下,具体的解说如下(jwt是不会有空行的,上面只是为了显示,便应用了换行看着比拟不便)。 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjMfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c它是由一个"."号隔开、三局部组成。第一局部是header信息, { "alg": "HS256",// 加密的算法 "typ": "JWT"// 加密的形式,填写JWT}第二局部是Payload,有固定的六个局部和自定义数据组成,自定义数据看本人的状况须要来定义,是能够省去的。 'iss' => 'https://www.qqdeveloper.com',// 签发人'exp' => time() + 86400,// 过期工夫(这里的有效期工夫为1天)'sub' => '主题内容',// 主题'aud' => '受众内容',// 受众'nbf' => $time,// 失效工夫'iat' => $time,// 签发工夫'jti' => 123,// 编号第三局部是Signature(是对前两局部加密得来的)。因为前两局部是公开通明的数据,因而避免数据的篡改和泄露,咱们须要加密解决。首先,须要指定一个密钥(secret)。这个密钥只有服务器才晓得,不能泄露给用户。而后,应用 Header 外面指定的签名算法(默认是 HMAC SHA256),依照上面的公式产生签名。 第一局部的加密形式(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)最终生成的就是下面很长的一段字符串了。 为什么会应用JWT这就须要从咱们传统的认证模式来说了,传统的认证模式是基于session和cookie来实现用户的认证和鉴权。具体的流程模式如下图。 (图一)Session与Cookie认证与鉴权 1.客户端向服务端发送一个http申请。 2.服务端在收到客户端的申请时,生成一个惟一的sessionid, 这里须要将该生成的session存储在服务端,这个sessionid存储具体的session内容,默认的是文件存储,当然咱们能够批改具体的存储形式,例如数据库存储。 3.客户端在承受到这个sessionid时,存在cookie外面,每次申请时携带该sessionid。 4.服务端在接管到客户端的申请之后,依据客户端发送的sessionid来进行认证与受权。 这里也举荐一下本人之前分享的一篇无关session于cookie的知识点。session与cookie详解 (图二)传统的token受权 1.客户端向服务端发送一个http申请。 2.服务端在收到客户端的申请之后,生成一个惟一token, 这里须要将该生成的token存储在服务端,至于怎么存,能够和下面session与cookie的形式统一。也能够存在缓存数据库中,如redis,memcached。 3.服务端将该token返回给客户端,客户端存在本地,能够存申请头header中,也能够存在cookie中,同时也能够存在localstorage中。 4.向服务端发送申请时,携带该token,服务端进行认证或者受权。 (图三)JWT认证模式 1.客户端向服务端发送一个http申请。 2.服务端依据jwt的生成规定,生成一个token,并返回给客户端,这里服务端是不须要存储的。 3.客户端在承受到该token时,存在客户端。 4.客户端向服务端发送申请时,服务端对申请的token进行解析,如果发现解析进去的数据和生成的数据是统一的代表是一个非法的token,则进行相应的操作。 基于session和cookie的认证和鉴权模式有什么好与不好的中央呢?通过下面几张图,咱们也大抵能够看得出来,基于session都是须要服务端存储的,而JWT是不须要服务端来存储的。针对以上几点,总结如下: 一、毛病 1.容易遇到跨域问题。不同域名下是无奈通过session间接来做到认证和鉴权的。 2.分布式部署的零碎,须要应用共享session机制 3.容易呈现csrf问题。 ...

March 4, 2021 · 1 min · jiezi

关于session:Session的销毁方式到底有哪些

Session,作为咱们离不开的后盾的技术,它的呈现次要是为了解决 Http 协定的无状态特点,用于解决用户状态的存储问题,而往往对于存储来说都会波及到一个工夫问题,上面咱们来看看它的销毁形式到底有哪些。 销毁的形式默认工夫到期本人设定到期工夫立即生效敞开浏览器敞开服务器案例实操默认工夫到期当客户端第一次申请 servlet 并且操作 session 时,session 对象生成,以 Tomcat 为例,Tomcat 中 session 默认的存活工夫为 30min,即你不操作界面的工夫,一旦有操作,session 会从新计时。那么 session 的默认工夫能够改么?答案是必定的。能够在 Tomcat 中的 web.xml 文件中进行批改。如下图: 本人设定到期工夫当然除了以上的批改形式外,咱们也能够在程序中本人设定 session 的生命周期,通过 session.setMaxInactiveInterval(int); 来设定 session 的最大不流动工夫,单位为秒。 HttpSession session = req.getSession();session.setMaxInactiveInterval(5); 当然咱们也能够通过 getMaxInactiveInterval(); 办法来查看以后 Session 对象的最大不流动工夫。 立即生效或者咱们也能够通过 session.invalidate(); 办法让 session 立即生效。 session.invalidate(); 敞开浏览器session 的底层依赖 cookie 实现,因为不同用户拜访服务器要判断到底是应用哪个 session,所以当用户第一次拜访服务器的时候往往会把一个 session id 通过 cookie 存储到用户端,并且该 cookie 的无效工夫为敞开浏览器,从而 session 在浏览器敞开时也相当于生效了(因为没有 session id 再与之对应)。如下图,敞开后再关上,从新给浏览器调配了个 session id。 须要留神的是这里只是 cookie 生效了,你再拜访相当于服务器把你当成了新用户,又给你创立了一个 session,并没有把之前的 session 对象销毁。 ...

January 5, 2021 · 1 min · jiezi

关于session:每日一问Session的销毁方式到底有哪些

问题:Session的销毁形式到底有哪些?念安小姐姐在b站指拨啦~ Session,作为咱们离不开的后盾的技术,它的呈现次要是为了解决 Http 协定的无状态特点,用于解决用户状态的存储问题,而往往对于存储来说都会波及到一个工夫问题,上面咱们来看看它的销毁形式到底有哪些。 销毁的形式默认工夫到期本人设定到期工夫立即生效敞开浏览器敞开服务器案例实操默认工夫到期当客户端第一次申请 servlet 并且操作 session 时,session 对象生成,以 Tomcat 为例,Tomcat 中 session 默认的存活工夫为 30min,即你不操作界面的工夫,一旦有操作,session 会从新计时。那么 session 的默认工夫能够改么?答案是必定的。能够在 Tomcat 中的 web.xml 文件中进行批改。如下图: 本人设定到期工夫当然除了以上的批改形式外,咱们也能够在程序中本人设定 session 的生命周期,通过 session.setMaxInactiveInterval(int); 来设定 session 的最大不流动工夫,单位为秒。 HttpSession session = req.getSession();session.setMaxInactiveInterval(5); 当然咱们也能够通过 getMaxInactiveInterval(); 办法来查看以后 Session 对象的最大不流动工夫。 立即生效或者咱们也能够通过 session.invalidate(); 办法让 session 立即生效。 session.invalidate(); 念安小姐姐在b站指拨啦~ 敞开浏览器session 的底层依赖 cookie 实现,因为不同用户拜访服务器要判断到底是应用哪个 session,所以当用户第一次拜访服务器的时候往往会把一个 session id 通过 cookie 存储到用户端,并且该 cookie 的无效工夫为敞开浏览器,从而 session 在浏览器敞开时也相当于生效了(因为没有 session id 再与之对应)。如下图,敞开后再关上,从新给浏览器调配了个 session id。 ...

December 7, 2020 · 1 min · jiezi

关于session:Session与Cookie

会话(Session)跟踪是Web程序中罕用的技术,用来跟踪用户的整个会话。罕用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。 本章将系统地讲述Cookie与Session机制,并比拟阐明什么时候不能用Cookie,什么时候不能用Session。 1.1  Cookie机制 在程序中,会话跟踪是很重要的事件。实践上,一个用户的所有申请操作都应该属于同一个会话,而另一个用户的所有申请操作则应该属于另一个会话,二者不能混同。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么工夫购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。 而Web应用程序是应用HTTP协定传输数据的。HTTP协定是无状态的协定。一旦数据交换结束,客户端与服务器端的连贯就会敞开,再次替换数据须要建设新的连贯。这就意味着服务器无奈从连贯上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器曾经无奈判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。 Cookie就是这样的一种机制。它能够补救HTTP协定无状态的有余。在Session呈现之前,基本上所有的网站都采纳Cookie来跟踪会话。 1.1.1  什么是Cookie Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区倒退的一种机制。目前Cookie曾经成为规范,所有的支流浏览器如IE、Netscape、Firefox、Opera等都反对Cookie。 因为HTTP是一种无状态的协定,服务器单从网络连接上无从晓得客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁拜访都必须携带本人通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。 Cookie实际上是一小段的文本信息。客户端申请服务器,如果服务器须要记录该用户状态,就应用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再申请该网站时,浏览器把申请的网址连同该Cookie一起提交给服务器。服务器查看该Cookie,以此来识别用户状态。服务器还能够依据须要批改Cookie的内容。 查看某个网站颁发的Cookie很简略。在浏览器地址栏输出javascript:alert (document. cookie)就能够了(须要有网能力查看)。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容,如图1.1所示。 图1.1  Baidu网站颁发的Cookie 图1.1中弹出的对话框中显示的为Baidu网站的Cookie。其中第一行BAIDUID记录的就是笔者的身份helloweenvsfei,只是Baidu应用非凡的办法将Cookie信息加密了。 留神:Cookie性能须要浏览器的反对。 如果浏览器不反对Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie性能就会生效。 不同的浏览器采纳不同的形式保留Cookie。 IE浏览器会在“C:Documents and Settings你的用户名Cookies”文件夹下以文本文件模式保留,一个文本文件保留一个Cookie。 1.1.2  记录用户拜访次数 Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组模式返回),通过response.addCookie(Cookiecookie)向客户端设置Cookie。 Cookie对象应用key-value属性对的模式保留用户状态,一个Cookie对象保留一个属性对,一个request或者response同时应用多个Cookie。因为Cookie类位于包javax.servlet.http.*上面,所以JSP中不须要import该类。 1.1.3  Cookie的不可跨域名性 很多网站都会应用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器拜访Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能批改Baidu颁发的Cookie呢? 答案是否定的。Cookie具备不可跨域名性。依据Cookie标准,浏览器拜访Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。 Cookie在客户端是由浏览器来治理的。浏览器可能保障Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保障用户的隐衷平安。浏览器判断一个网站是否能操作另一个网站Cookie的根据是域名。Google与Baidu的域名不一样,因而Google不能操作Baidu的Cookie。 须要留神的是,尽管网站images.google.com与网站www.google.com同属于Google,然而域名不一样,二者同样不能相互操作彼此的Cookie。 留神:用户登录网站www.google.com之后会发现拜访images.google.com时登录信息依然无效,而一般的Cookie是做不到的。这是因为Google做了非凡解决。本章前面也会对Cookie做相似的解决。 1.1.4  Unicode编码:保留中文 中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中应用Unicode字符时须要对Unicode字符进行编码,否则会乱码。 提醒:Cookie中保留中文只能编码。个别应用UTF-8编码即可。不举荐应用GBK等中文编码,因为浏览器不肯定反对,而且JavaScript也不反对GBK编码。 1.1.5  BASE64编码:保留二进制图片 Cookie不仅能够应用ASCII字符与Unicode字符,还能够应用二进制数据。例如在Cookie中应用数字证书,提供平安度。应用二进制数据时也须要进行编码。 留神:本程序仅用于展现Cookie中能够存储二进制内容,并不实用。因为浏览器每次申请服务器都会携带Cookie,因而Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。 1.1.6  设置Cookie的所有属性 除了name与value之外,Cookie还具备其余几个罕用的属性。每个属性对应一个getter办法与一个setter办法。Cookie类的所有属性如表1.1所示。 表1.1  Cookie罕用属性 属  性  名 描    述 String name 该Cookie的名称。Cookie一旦创立,名称便不可更改 Object value 该Cookie的值。如果值为Unicode字符,须要为字符编码。如果值为二进制数据,则须要应用BASE64编码 int maxAge 该Cookie生效的工夫,单位秒。如果为负数,则该Cookie在maxAge秒之后生效。如果为正数,该Cookie为长期Cookie,敞开浏览器即生效,浏览器也不会以任何模式保留该Cookie。如果为0,示意删除该Cookie。默认为–1 boolean secure 该Cookie是否仅被应用平安协定传输。平安协定。平安协定有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false String path ...

November 19, 2020 · 5 min · jiezi

关于session:session一致性

单机模式浏览器第一次拜访服务器的时候,服务器会创立一个session并返回给客户端。浏览器第二次拜访session的时候,会携带session到服务器,服务器会判断这个session是否存在,如果不存在,则从新创立一个session。在单机模式中,只有浏览器不重启,在过期工夫内,都能够放弃会话。 集群模式此时浏览器可能拜访服务器A,也可能拜访服务器B,如果先拜访了A,那session是保留在A中的,此时再拜访B,session找不到,就要从新登录了。 session粘滞此时可能会想,通过session的粘滞,让浏览器固定在某个服务器上就好了,这样session会始终在对应的服务器上。当服务器A挂了,此时会故障转移到B,仍然要从新登录。 session同步既然粘滞不行,那把session同步可行吗,当用户登录服务器A的时候,把A的信息也同步到B。这样不管怎么轮询,都能找到session。问题有以下几个:1、每个服务器都寄存所有的session的信息,内存占用空间大。2、每次session变动都要同步,占用网络,网络的提早还会造成部分工夫的不一样,也就是以后零碎有状态了,比方A还没同步sessoin到B,用户拜访了B,又要从新登录。3、服务器间的session同步难度比拟高,如果没有服务发现,扩容的时候还须要晓得对应的ip和端口。4、服务器A重启后,session还没同步过去,拜访服务器A的用户要从新登录。 寄存redis同步是把session寄存在内存中的,那咱们能够提取进去,放在redis中。通过redis的计划,能够保障服务的无状态,便于扩容,保障了session的一致性。毛病:1、引入redis,还要保障redis的高可用,减少了零碎的复杂性。 JWT下面是session寄存服务端的计划,咱们也能够通过jwt形式存在客户端。毛病如下:1、生成的字符串长,每次传输占用带宽。2、服务器解析须要占用cpu。3、没方法登记。4、如果想扭转生效工夫,就要从新生成jwt。

November 5, 2020 · 1 min · jiezi

关于session:cookie和session的关系看这一篇就够了

定义Cookie,有时也用其复数模式 Cookies,指某些网站为了分别用户身份、进行 session 跟踪而贮存在用户本地终端上的数据(通常通过加密)Session:在计算机中,尤其是在网络应用中,称为“会话管制”。Session对象存储特定用户会话所需的属性及配置信息。 很简短的两段定义,然而曾经道出了cookie和session实质的区别,一个位于客户端,一个位于服务端。这个个性带着浓厚的色调,理论中的利用都离不开这个定义。 存储这里针对浏览器中的cookie来探讨,不要做过多遥想如果抛开其余个性来说,cookie实质上是浏览器(http申请)提供的一种客户端存储的数据,然而这个存储数据有本人的一些个性,比方:cookie长度的限度,跨域的限度(当然能够在服务端配合的状况下的冲破这种限度)等。就像所有的存储一样,cookie也能够保留在内存中,也能够保留在磁盘中,只不过保留在磁盘的时候是在浏览器的存储目录下,毕竟cookie是基于http的,http申请又基于浏览器。 session在很多状况下被称为会话,实质上是一种服务端的存储数据。诞生的次要起因是为了解决http无状态这种个性。既然是数据,其实就能够存储于任何介质中,像理论利用中,有存储于内存中的,也有存储于redis的。所以只有看透了它的实质,存储在哪里可能就只是一个驱动的问题了。其实齐全能够本人写一个程序把session的数据存储在txt中,只不过性能上可能须要多加思考。 有分割吗cookie当用户第一次拜访并登陆一个网站的时候,cookie的设置以及发送会经验以下4个步骤: 客户端发送一个申请到服务器 --》服务器发送一个HttpResponse响应到客户端,其中蕴含Set-Cookie的头部 --》客户端保留cookie,之后向服务器发送申请时,HttpRequest申请中会蕴含一个Cookie的头部 --》服务器返回响应数据set-cookie: session=4a0b9b1cce73c469b8a6b6a8aec294d5; domain=.xx.com; path=/; expires=Sun, 25 Aug 2019 08:21:27 -0000; secure; HttpOnly以上过程很显著是一个最常见的场景,cookie的个性以及值是由服务端来下发,然而不要遗记cookie实质上是一种客户端技术,所以客户端其实同样能操作cookie,比方:登录的时候服务端的返回后果中能够不蕴含set-cookie的头部,而是把值通过注释来返回,客户端脚本通过读取返回的注释解析出后果,而后写入cookie同样能达到雷同的成果。set-cookie只不过是http协定中曾经约定好的格局,服务端通知客户端须要设置cookie的协定而已。当然cookie还有其余很多个性(可能随着倒退有所增加或者缩小): 属性介绍namename字段为一个cookie的名称valuevalue字段为一个cookie的值domain能够拜访此cookie的域名path能够拜访此cookie的页面门路expires/Max-Age此cookie超时工夫。SizeSize字段 此cookie大小httpcookie的httponly属性secure设置是否只能通过https来传递此条cookie因为浏览器的安全策略,不同域名(何为不同域名,请百度)的cookie是不容许的,然而能够通过服务端的配置能够解决这个问题。 sessionsession的创立目标初衷就是为了让服务端记住会话,简而言之就是让服务端能辨认进去是哪个客户端,既然要记住,那服务端必须要存储每个会话的数据,比方:理论我的项目中最罕用的用户信息等。服务端存储这些用户数据没问题,最大的一个阻碍是怎么样辨认诸多申请中哪些是同一个会话。要解决这个问题,只依附服务端无奈解决,必须须要客户端来配合:须要上传会话的标识。 客户端上传会话的标识,必须是客户端和服务端都能反对的协定和数据,其实也能够看做是http申请反对的协定和数据,既然是基于http申请,最不便的就是利用cookie,cookie是一种key-value的数据存储格局,value的值正适宜作为session的标识(session也是一种key-value的存储),在这种状况下cookie终于和session有了肯定的分割。 session机制利用cookie来作为标识的传输机制,并不意味着只能用cookie,只有是服务端和客户端约定好了地位,session标识我能够放到http申请的任何地位(当然http申请必须得反对传输才能够)。你齐全能够把session的标识放到http头Authorization字段,只有服务端能正确的读到此值并且正确解析即可。 有些面试官喜爱问cookie和session的雷同和不同,甚至他们的分割,这样的发问在某种程度上是不太好的,容易让人谬误的认为cookie和session的分割很亲密,然而其实他们的分割很单纯,纯净的敌人利用关系。 此文篇幅属于5分钟系列,更能无效利用碎片化工夫,下一篇,咱们兴许能够讨论一下基于cookie和session的认证更多精彩文章 分布式大并发系列架构设计系列趣学算法和数据结构系列设计模式系列

September 1, 2020 · 1 min · jiezi

JSON-Web-Token-使用详解

JWT是什么?JSON Web Token(缩写 JWT)是目前最流行的<font color='red'>跨域</font>认证解决方案。它是有三部分组成,示例如下,具体的讲解如下(jwt是不会有空行的,下面只是为了显示,便使用了换行看着比较方便)。 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjMfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c它是由一个"."号隔开、三部分组成。第一部分是header信息, { "alg": "HS256",// 加密的算法 "typ": "JWT"// 加密的方式,填写JWT}第二部分是Payload,有固定的六个部分和自定义数据组成,自定义数据看自己的情况需要来定义,是可以省去的。 'iss' => 'https://www.qqdeveloper.com',// 签发人'exp' => time() + 86400,// 过期时间(这里的有效期时间为1天)'sub' => '主题内容',// 主题'aud' => '受众内容',// 受众'nbf' => $time,// 生效时间'iat' => $time,// 签发时间'jti' => 123,// 编号第三部分是Signature(是对前两部分加密得来的)。由于前两部分是公开透明的数据,因此防止数据的篡改和泄露,我们需要加密处理。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。 第一部分的加密方式(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)最终生成的就是上面很长的一段字符串了。 为什么会使用JWT这就需要从我们传统的认证模式来说了,传统的认证模式是基于session和cookie来实现用户的认证和鉴权。具体的流程模式如下图。<center>(图一)Session与Cookie认证与鉴权</center>1.客户端向服务端发送一个http请求。 2.服务端在收到客户端的请求时,生成一个唯一的sessionid,<font color='red'>这里需要将该生成的session存储在服务端</font>,这个sessionid存储具体的session内容,默认的是文件存储,当然我们可以修改具体的存储方式,例如数据库存储。3.客户端在接受到这个sessionid时,存在cookie里面,每次请求时携带该sessionid。4.服务端在接收到客户端的请求之后,根据客户端发送的sessionid来进行认证与授权。这里也推荐一下自己之前分享的一篇有关session于cookie的知识点。session与cookie详解<center>(图二)传统的token授权</center>1.客户端向服务端发送一个http请求。 2.服务端在收到客户端的请求之后,生成一个唯一token,<font color='red'>这里需要将该生成的token存储在服务端</font>,至于怎么存,可以和上面session与cookie的方式一致。也可以存在缓存数据库中,如redis,memcached。3.服务端将该token返回给客户端,客户端存在本地,可以存请求头header中,也可以存在cookie中,同时也可以存在localstorage中。4.向服务端发送请求时,携带该token,服务端进行认证或者授权。<center>(图三)JWT认证模式</center>1.客户端向服务端发送一个http请求。 2.服务端根据jwt的生成规则,生成一个token,并返回给客户端,<font color='red'>这里服务端是不需要存储的</font>。3.客户端在接受到该token时,存在客户端。4.客户端向服务端发送请求时,服务端对请求的token进行解析,如果发现解析出来的数据和生成的数据是一致的代表是一个合法的token,则进行相应的操作。 基于session和cookie的认证和鉴权模式有什么好与不好的地方呢?总结如下几点:通过上面几张图,我们也大致可以看得出来,基于session都是需要服务端存储的,而JWT是不需要服务端来存储的。针对以上几点,总结如下:一、缺点1.容易遇到跨域问题。不同域名下是无法通过session直接来做到认证和鉴权的。2.分布式部署的系统,需要使用共享session机制3.容易出现csrf问题。 二、优点1.方便灵活,服务器端直接创建一个sessionid,下发给客户端,客户端请求携带sessionid即可。2.session存储在服务端,更加安全。3.便于服务端清除session,让用户重新授权一次。 JWT与session有什么区别呢?JWT是基于客户端存储的一种认证方式,然而session是基于服务端存储的一种认证方式。JWT虽然不用服务端存储了,也可以避免跨域、csrf等情况。但也存在如下几个不太好的地方。1.无法清除认证token。由于JWT生成的token都是存储在客户端的,不能有服务端去主动清除,只有直到失效时间到了才能清除。除非服务端的逻辑做了改变。2.存储在客户端,相对服务端,安全性更低一些。当JWT生成的token被破解,我们不便于清除该token。 如何使用JWT这里推荐使用GitHub上面人家封装好的包,这里我使用的是firebase/php-jwt,在项目中直接使用即可安装成功。 composer require firebase/php-jwt接下来创建一个控制器,我这里使用的ThinkPHP5.1的框架 use think\Controller;use Firebase\JWT\JWT;class Test extends Controller{ private $key = 'jwtKey'; // 生成JWT public function createJwt() { $time = time(); $key = $this->key; $token = [ 'iss' => 'https://www.qqdeveloper.com',// 签发人 'exp' => $time + 86400,// 过期时间(这里的有效期时间为1天) 'sub' => '主题内容',// 主题 'aud' => '受众内容',// 受众 'nbf' => $time,// 生效时间 'iat' => $time,// 签发时间 'jti' => 123,// 编号 // 额外自定义的数据 'data' => [ 'userName' => '编程浪子走四方' ]]; // 调用生成加密方法('Payloadn内容','加密的键',['加密算法'],['加密的可以'],['JWT的header头']) $jwt = JWT::encode($token, $key); return json(['data' => $jwt]); } // 解析JWT public function analysisJwt() { try { $key = $this->key; $jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImV4cCI6MTU2ODA5NjE4MCwic3ViIjoiXHU0ZTNiXHU5ODk4XHU1MTg1XHU1YmI5IiwiYXVkIjoiXHU1M2Q3XHU0ZjE3XHU1MTg1XHU1YmI5IiwibmJmIjoxNTY4MDA5NzgwLCJpYXQiOjE1NjgwMDk3ODAsImp0aSI6MTIzLCJkYXRhIjp7InVzZXJOYW1lIjoiXHU3ZjE2XHU3YTBiXHU2ZDZhXHU1YjUwXHU4ZDcwXHU1NmRiXHU2NWI5In19.kHb_9Np0zjE25YE9czUEGvmFPYtqMJT9tuZzJTuMZl0'; // 调用解密方法('JWT内容','解密的键,和加密时的加密键一直','加密算法') $decoded = JWT::decode($jwt, $key, array('HS256')); return json(['message' => $decoded]); } catch (\Exception $exception) { return json(['message' => $exception->getMessage()]); } }}通过访问第一个方法,可以生成下图一段字符串我们将上图中的字符串复制到第二图中的$jwt变量,访问第二个方法即可解析出具体的数据。 ...

September 9, 2019 · 1 min · jiezi

高并发探测一分布式部署

前言这里不讨论服务器分布式部署好处,服务并发处理的首要问题是:处理用户登录状态的一致性。 1.场景一用户在浏览器登录记住密码7天,第一台机器的登录信息保存,期间可能会访问到多台机器,保持登录状态。 a.提出问题用户登录状态的需要保存,session如何同步? b.方案选择网上大神的实践方案很多,这里作选择测试。session是存储在服务器本地的,认为浏览器端cookie开启,不开起可以对环境设置session的url传参模式,但对于普通用户看来安全性降低了,原理是区别不大。参考《分布式系统session一致性的问题》,主要有:其一,session入库:把session信息添加到mysql表、redis中,如果本地不存在、可以通过cookie可以查询获取这些用户状态信息,但这会增加后端设计开发的复杂度;第二,代理分发:主动映射服务器ip,用户只会访问到指定的服务器,但部署起来有些麻烦。这里选择代理分发模式,服务器扩容的问题优先在部署方解决,整体逻辑上也比较简洁清晰。c.部署测试 vim config/nginx.conf:s%/logs/etc/g #把logs日志目录全部改到etc下[]:~/tmp/dk# docker stop $(docker ps -aq)#启动mysql和redis[]:~/tmp/dk# docker start mm ms rm rs

July 1, 2019 · 1 min · jiezi

SpringBoot-使用-Redis-实现-Session-共享

SpringBoot 使用 Redis 实现 Session 共享使用 Redis 实现 Session 共享1 什么是 Session由于 HTTP 协议是无状态的协议,因而服务端需要记录用户的状态时,就需要用某种机制来识具体的用户。Session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 Session 保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是 Session。客户端浏览器再次访问时只需要从该 Session 中查找该客户的状态就可以了。 2 为什么需要同步session ?当用户量比较大时候一个tomcat可能无法处理更多的请求,超过单个tomcat的承受能力,可能会出现用户等待,严重的导致tomcat宕机。 这时候我们后端可能会采用多个tomcat去处理请求,分派请求,不同请求让多个tomcat分担处理。登录的时候可能采用的是tomca1,下单的时候可能采用的是tomcat2 等等等。 若没有session共享同步,可能在tomcat1登录了,下一次请求被分派到tomcat2上,这时候用户就需要重新登录。 在实际工作中我们建议使用外部的缓存设备来共享 Session,避免单个节点挂掉而影响服务,使用外部缓存 Session 后,我们的共享数据都会放到外部缓存容器中,服务本身就会变成无状态的服务,可以随意的根据流量的大小增加或者减少负载的设备。目前主流的分布式 Session 管理有两种方案。 1 Session 复制部分 Web 服务器能够支持 Session 复制功能,如 Tomcat。用户可以通过修改 Web 服务器的配置文件,让 Web 服务器进行 Session 复制,保持每一个服务器节点的 Session 数据都能达到一致。 这种方案的实现依赖于 Web 服务器,需要 Web 服务器有 Session 复制功能。当 Web 应用中 Session 数量较多的时候,每个服务器节点都需要有一部分内存用来存放 Session,将会占用大量内存资源。同时大量的 Session 对象通过网络传输进行复制,不但占用了网络资源,还会因为复制同步出现延迟,导致程序运行错误。 在微服务架构中,往往需要 N 个服务端来共同支持服务,不建议采用这种方案。 ...

July 1, 2019 · 2 min · jiezi

API-交互中怎么做好图片验证码

前言在传统的 Web 开发过程中,处理图形验证码很简单,只需要在后台用随机字符串生成一个图片,将验证码内容放进 Session 即可,用户提交表单时从 Session[1] 取出判断即可。 但是现如今,越来越推崇 API 交互,无状态,在 Session 这一块,虽然默认配置是不支持了,但是还是有很多曲线救国的方法。 基于 Session 实现在 API 开发中,我们也可以给前端签发 SessionID ,并且通过 PHP 的内置方法,来实现这一切。比如 我们与前段约定,当在请求中包含有 X-Session-Id ,且不为空时,表示这个会话已经注册过 SessionID ,否则就颁布一个 SessionID 并返回在 Response Header 中的 X-Session-Id 让前段记录这个 SessionID ,下面简单实现一下。 // code_session.phpsession_start();// 这里假设已经通过 Header 获取到了 SessionID,并保存到了 $sessionId 变量中。// 当 SessionID 不存在,或者 为空 则创建新的 SessionID 。if(!isset($sessionId) || empty($sessionId)){ $sessionId = session_create_id(); // 因为前台还没有 SessionID ,所以下发一个,通知前端保存。 header('X-Session-Id: '.$sessionId);}// 设置当前会话的 SessionID 。session_id($sessionId);// 这里我们就可以自由的读写 Session 了。// 生成验证码$code = mt_rand(1e3 ,1e4-1);// create_image 请自行实现 或者使用现有的图形验证码库生成。$image = create_image($code);// 存储进去 Session$_SESSION['code'] = $code;// 输出一张图片$image->output();上面基本实现了生成图片,前端需要根据 只需要再提交表单时,在 headers 中带上 X-Session-ID 即可。 ...

June 15, 2019 · 2 min · jiezi

神奇的session-in-jpa

引言再回顾一下问题场景: Iterable<Teacher> teachers = teacherRepository.findAll();for (Teacher teacher : teachers) { logger.debug("教师: " + teacher.getName()); for (Klass klass : teacher.getKlasses()) { logger.debug("班级: " + klass.getName()); for (Student student : klass.getStudents()) { logger.debug("学生: " + student.getName()); } }}在单元测试中跑这段代码,是报错的,no Session,说明执行完teacherRepository.findAll()之后,session就已经关闭了。继续执行,session已经关闭,再去数据库查教师关联的班级信息,就错了。 然而呢?把这段代码再放到Service里,写一个接口,交给浏览器去调用,却正常执行,说明session还在。 然后就一直研究为什么不好使?如果能把这个原因分析明白,以后再遇到no session错误的时候就可以一劳永逸了。 探究调试调试最简单的方法就是中断,但是咱水平还不行,也不知道JPA内部去找Hibernate怎么调用的,中断哪个方法呢? 后台发现了另一种调试的方法,JPA的源码中也是像我们开发时经常写日志的,logger.debug()什么的。 slf4j中常用的日志级别就ERROR、WARN、INFO、DEBUG四种,我们可以将JPA的日志级别设置为DEBUG级别,这样我们就可以根据日志推测到JPA内部到底是怎么执行的了。 修改日志级别logging.level.org.springframework.orm.jpa=debug修改配置文件,将JPA的日志级别设置为DEBUG。 在单元测试中执行 完整日志: 2019-06-06 11:36:40.415 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly2019-06-06 11:36:40.416 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(1334204880<open>)] for JPA transaction2019-06-06 11:36:40.429 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@73b74615]2019-06-06 11:36:40.449 INFO 11391 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactoryHibernate: select teacher0_.id as id1_2_, teacher0_.name as name2_2_ from teacher teacher0_2019-06-06 11:36:40.598 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Initiating transaction commit2019-06-06 11:36:40.598 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Committing JPA transaction on EntityManager [SessionImpl(1334204880<open>)]2019-06-06 11:36:40.601 DEBUG 11391 --- [ main] o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(1334204880<open>)] after transaction2019-06-06 11:36:40.602 DEBUG 11391 --- [ main] com.yunzhiclub.jpa.JpaApplicationTests : 教师: 张三org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.yunzhiclub.jpa.entity.Teacher.klasses, could not initialize proxy - no Session分析Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnlyOpened new EntityManager [SessionImpl(1334204880<open>)] for JPA transactionExposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@73b74615]HHH000397: Using ASTQueryTranslatorFactoryHibernate: select teacher0_.id as id1_2_, teacher0_.name as name2_2_ from teacher teacher0_Initiating transaction commitCommitting JPA transaction on EntityManager [SessionImpl(1334204880<open>)]上来是先执行了一个事务,为什么会有事务呢? ...

June 6, 2019 · 5 min · jiezi

如何运用PHPREDIS解决负载均衡后的session共享问题

一、为什么要使用Session共享?稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个模块中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器通过统一接口对用户数据进行访问即可。但还存在一个问题,就是用户在这个服务器登录之后,进入另一个服务器的别的模块时,仍然需要重新登录,这就是一次登录,全部通行的问题,映射到技术上,其实就是各个服务器之间如何实现共享 SESSION 数据的问题。 二、了解session工作原理在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。 服务端通过客户端传递的session_id区分用户,用来标记用户的登录状态。 用户再次发送请求的时候,把服务端返回的session_id通过cookie[或者URL传参]的形式传递到服务端,这样服务端就可以区分出来具体操作的用户。 三、如何解决负载均衡之后的session共享问题?1.不使用session,换作cookie 把session改成cookie,就能避开session的一些弊端。【安全性较低】 2.数据库记录下session信息 使用数据库记录session信息,session的使用频率比较高,如果存在数据库中,频繁的读取会对数据库产生较大的压力,网站性能瓶颈一般都存在数据库. 3.负载均衡的时候使用ip_hash算法进行分发 使用ip_hash可能会导致某一台服务器负载较大。如果某段时间内服务器进入了很多固定IP代理的请求 [翻墙,代理] ,如果代理IP的负载过高就会导致ip_hash对应的服务器负载压力过大,这样ip_hash就失去了负载均衡的作用了。 4.对session文件进行同步 使用同步工具对session文件进行同步,保证负载服务器的session文件都是一致的,这种做法虽然可以解决session共享的问题,同样的内容会存在多个服务器上,而且部分服务器存在的session文件可能从开始到结束完全没有使用到,浪费了服务器的资源。 【rsync,inotify-tools等】 5.使用memcache或者redis保存session信息 [建议] 相比文件取信息,从内存取数据速度要快很多,而且在多个服务器需要共用 session 时会比较方便,将这些服务器都配置成使用同一组 memcached 服务器就可以,减少了额外的工作量。其缺点是 session 数据都保存在 memory 中,一旦宕机,数据将会丢失。但对 session 数据来说并不是严重的问题。 ...

June 3, 2019 · 1 min · jiezi

PHP使用Redis实现Session共享

Last-Modified: 2019年5月10日16:06:36 前言小型web服务, session数据基本是保存在本地(更多是本地磁盘文件), 但是当部署多台服务, 且需要共享session, 确保每个服务都能共享到同一份session数据. redis 数据存储在内存中, 性能好, 配合持久化可确保数据完整. 设计方案1. 通过php自身session配置实现# 使用 redis 作为存储方案session.save_handler = redissession.save_path = "tcp://127.0.0.1:6379"# 若设置了连接密码, 则使用如下session.save_path = "tcp://127.0.0.1:6379?auth=密码"测试代码 <?phpini_set("session.save_handler", "redis");ini_set("session.save_path", "tcp://127.0.0.1:6379");session_start();echo "<pre>";$_SESSION['usertest'.rand(1,5)]=1;var_dump($_SESSION);echo "</pre>";输出 ↓ array(2) { ["usertest1"]=> int(88) ["usertest3"]=> int(1)}usertest1|i:1;usertest3|i:1;评价 优点: 实现简单, 无需修改php代码缺点: 配置不支持多样化, 只能应用于简单场景2. 设置用户自定义会话存储函数通过 session_set_save_handler() 函数设置用户自定义会话函数. session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid [, callable $validate_sid [, callable $update_timestamp ]]] ) : bool # >= php5.4session_set_save_handler ( object $sessionhandler [, bool $register_shutdown = TRUE ] ) : bool在配置完会话存储函数后, 再执行 session_start() 即可. ...

May 10, 2019 · 2 min · jiezi

Nodejs中Koa2如何使用Session完成登录状态保持

项目要用到登录注册,就需要使用到Cookie和Session来保持登录状态,于是就简单研究了一下Cookie和Session的工作原理前面已经专门发过一篇帖子记录Cookie和Session的工作原理了,不明白的小伙伴可以看看Cookie、Session是如何保持登录状态的?。 使用Koa的Session中间件Koa是一个简洁的框架,把许多小功能都拆分成了中间件,用一个洋葱模型保证了中间件丰富的可拓展性,我们要使用Session来保持登录状态,就需要引用Session中间件。 安装Koa-Session中间件npm install koa-session --save如果需要使用TypeScript进行开发,则需要引入对应的TS类型声明 npm install @types/koa-session --save配置SessionKoa-Session需要做一些配置: const session_signed_key = ["some secret hurr"]; // 这个是配合signed属性的签名keyconst session_config = { key: 'koa:sess', /** cookie的key。 (默认是 koa:sess) */ maxAge: 4000, /** session 过期时间,以毫秒ms为单位计算 。*/ autoCommit: true, /** 自动提交到响应头。(默认是 true) */ overwrite: true, /** 是否允许重写 。(默认是 true) */ httpOnly: true, /** 是否设置HttpOnly,如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。 (默认 true) */ signed: true, /** 是否签名。(默认是 true) */ rolling: true, /** 是否每次响应时刷新Session的有效期。(默认是 false) */ renew: false, /** 是否在Session快过期时刷新Session的有效期。(默认是 false) */};我们需要关注这几个配置: ...

May 6, 2019 · 2 min · jiezi

CookieSession是如何保持登录状态的

无论是web应用还是原生app应用,只要涉及网络连接,基本就离不开登录注册,我们注册完成后登录,这个状态就被保持下来了,我们下次打开app,应用就已经处于登录状态了,不需要我们重复登录,非常人性化,感觉服务器好像会“记得”我们每一个人一样,“记得”你来过,就不再要求你登录了。但其实计算机在这方面是很傻的,他不是自发“记得”我们的,这篇文章就来聊聊登录状态的保持是怎么实现的。HTTP协议要弄明白下面说的东西,就得先了解一下HTTP协议,繁琐的概念就不多赘述了,这里主要注意一点,_HTTP协议是无状态的_ ,什么叫无状态?我们从一个故事讲起: 你去一家水果店买水果,你看到他们的桃子很鲜美,于是你大赞了老板的桃子后并买了一斤。你回家尝了桃子后觉得非常好吃,你决定第二天继续购买。当你第二天开开心心的过来找老板,跟他说“你家的水果真的很好吃,再来一斤我昨天买的那个水果”,但是发现老板并不知道你昨天买过什么,于是你非常生气,跟老板说“我昨天还夸了很久你的水果,你怎么就不记得了?”一顿理论后你发现老板始终不记得你昨天干过什么事,最后你只好跟老板表明要买一斤桃子,交易后灰溜溜地离开。这个故事中的老板就是无状态的,对他来说,他只知道某个人要买什么东西,给了多少钱,要买什么水果,要找多少钱,对于过程中的其它信息如是“谁”来买,他不会记得,他只会针对买卖本身进行处理。 HTTP为什么无状态?首先什么是无状态?无状态就意味着每个请求之间的不会直接地相互影响,对于每个请求,同样的请求参数就会得到同样的结果。 回到HTTP协议中:最初的需求是请求HTML界面显示出静态网站,并不如现在那么复杂丰富,用户A点击某个网址浏览到的页面和用户B点击同样网址浏览到的页面是完全一模一样的,也就是服务器并不会对每个不同的人有特殊处理,服务器只对请求负责,不对发起请求的人负责。因此在HTTP设计中,每个请求都是独立的,每个请求中都包含了请求的所有数据,服务器只对请求和请求中携带的信息进行处理后返回特定结果。就如上面的那个水果店,老板只根据要买什么水果,水果多少钱,给了多少钱,进行处理,如果你跟他说你昨天与他交谈如何,他无动于衷,因为他完全不会记得这些事情,他完全不记得你曾来过。 HTTP如何保存登录状态?前面说到,HTTP是无状态的,每个请求之间的不会直接地相互影响。当我第一次调用用户名密码验证接口的时候,我需要输入账号、密码,服务器收到请求之后,就会根据账号去数据库取你的密码和你输入的密码进行比对,然后返回一个“密码正确”或“密码错误”。而问题在于当我第二次访问这个接口的时候,服务器依旧会执行他的职能:收到我发送的账号和密码,然后去数据库取数据进行比对后返回比对结果,对于服务器来说,每个请求不过是做了类似1+1是否等于2的判断然后返回结果而已。 我想要服务器能够记住我已经调用过一次登录接口并且以及成功了这个状态,应该怎么办? 我们可以很自然的想到,服务器不知道我们登录过的原因是因为没有记下来,要保持登录状态,只要让服务器记下来就可以了。我们可以在服务器专门设置一个存储,每次只要我验证账号和密码成功,就在这个存储里面存下“JabinGP登录成功”(这个JabinGP是用户名),这样我们服务器就记得JabinGP登录过了。 现在服务器已经知道JabinGP登录过了,但是这就够了吗?不够,因为HTTP请求并不会自动标明“这是JabinGP发起的请求”,所以我们还要做点工作让服务器能知道“这是JabinGP发起的请求”,然后服务器才好去存储下来的登录状态里面找“JabinGP登录成功”这个标志。怎么做?我们可以在调用请求的时候把自己的用户名加进请求的参数中,比如Get请求的URl参数、Post请求的请求Body中,这样服务器就可以根据我们的用户名判断我们有没有登录过了。 这样我们就初步的把登录状态保存了下来,其实这样的验证非常粗糙,所以基于这个思想,产生了下面的技术。 什么是Cookie?有很多品牌的Cookie,比如说蓝罐,广州酒家......什么?哦哦不好意思我搞错了,这个才是Cookie:Cookie就是存储在客户端的一小段数据,它可以存在硬盘中(永久Cookie),也可以存在内存中(临时Cookie) 什么是Session?Session是指服务器为某个会话开启的一段独特的存储空间(会话是指一个终端用户与交互系统进行通讯的过程,比如说我先登录,再查看我的邮箱内容,这个过程就是一个会话),一个Session用唯一的SessionId对应一段存储空间。 Cookie和Session是怎么用的?首先从概念上,Cookie和Session都是用来存东西的,问题在于它们都用来存什么,以及它们都做了什么?结合前面分析: Cookie的出现,代替了手动设置标识的步骤,因为我们可以把标识设置在Cookie里面,设置了Cookie后,Cookie就存在了,下次请求Cookie就会自动发送给服务器,这样我们就不用给每个请求都很麻烦地手动设置一个标识(比如前面分析中的用户名)。Session其实就是代替了在服务器存储状态的步骤,SessionId可以对应一段存储空间,这段空间对每个会话都是唯一的(比如我登录后,就产生了一个会话,也产生了一段存储空间,这段存储空间只被我当前的登录状态下的活动所使用,别人是用不到的),这样就可以确保每个登录状态都有对应的一小段存储空间来写入一些中间过程的数据。Cookie和Session的关系?看了上面的Cookie和Session的解释,以及Cookie和Session的使用,就可以发现它们两个其实完全不冲突,甚至这两者是需要相互配合的,因为Cookie是在客户端的存储,Session是在服务端的存储,Session的存储需要SessionId来一一对应,这样才不会出现xxx获得了JabinGP的登录状态然后用JabinGP的钱买东西这样的情况,SessionId则需要通过Cookie保存在用户客户端中,客户端通过保存在Cookie的SessionId来标识自己,表明“我就是JabinGP”。 到这里差不多就简单介绍完Cookie和Session以及登录状态的保持了,以上都是个人理解,用于个人学习记录,如有错误,请一定评论指正!

May 4, 2019 · 1 min · jiezi

大话javascript 7期:Cookie、Session和Token的那些事儿

一、登录认证机制随着互联网的不断发展,无论是网站还是app,一般都会要求用户注册/登录。主要的登录方式有账户密码登录、第三方登录(微信登录、QQ登录、微博登录等)登录可分为三个阶段(登录验证、登录持续、退出登录);登录验证指客户端提供账号/密码(或第三方平台(微信、qq)获取openid/unionid)向服务器提出登录请求,服务器应答请求判断能否登录并返回相应数据;登录持续指客户端登录后, 服务器能够分辨出已登录的客户端,并为其持续提供登录权限的服务器。退出登录指客户端退出登录状态。二、保持登录持续状态的实现方式为什么要保持登录状态的持续?由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证方案:客户端登录成功后, 服务器为其分配一个唯一的凭证, 客户端每次请求资源时都带上这个凭证;实现方案cookie 会话机制session 会话机制token 会话机制三、Cookie、Session和TokenCookie(浏览器缓存)1.什么是CookieCookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。cookie其实是补充http协议的无状态性的缺点,底层是通过服务器端在http响应消息中增加set-cookie字段来将cookie信息发送给浏览器端,因为它只能存4k,一般用来存浏览器的身份信息,浏览器在访问服务器的某些资源的时候,会在http请求头中将cookie数据传给服务器,这样服务器就知道是谁请求的了,但是如果用户清除了cookie,那就啥都没有了2.Cookie的属性1、Expires:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效(持久级别Cookie)。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效(会话级别Cookie),浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1;2、Domain: 我们现在有二个域名。域名A:b.f.com,域名B:d.f.com;显然域名A和域名B都是f.com的子域名如果我们在域名A中的Cookie的domain设置为.f.com,那么.f.com及其子域名都可以获取这个Cookie,即域名A和域名B都可以获取这个Cookie如果域名A没有显式设置Cookie的domain方法,那么domain就为.b.f.com,不一样的是,这时,域名A的子域名将无法获取这个CookieHttpOnly: 这个属性是面试的时候常考的,如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击3.Cookie的操作封装cookie的常用操作方法设置cookie读取cookie删除cookievar cookieUtil = { getItem: function (name) { var cookieName = encodeURIComponent(name) + “=”, cookieStart = document.cookie.indexOf(cookieName), cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(’;’, cookieStart); if (cookieEnd == 1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)) } return cookieValue; }, setItem: function (name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + “=” + encodeURIComponent(value); if (expires) { cookieText += “;expires=” + expires.toGMTString(); } if (path) { cookieText += “;path=” + path; } if (domain) { cookieText += “;domain=” + domain; } if (secure) { cookieText += “;secure”; } document.cookie = cookieText; }, unset: function (name, path, domain, secure) { this.setItem(name, “”, new Date(0), path, domain, secure) }}CookieUtil.setItem(“name”, ’tom’); // 设置cookieconsole.log(CookieUtil.getItem(’name’));//读取cookieCookieUtil.unset(“name”)//删除cookie4.Cookie防篡改机制因为Cookie是存储在客户端,用户可以随意修改。所以,存在一定的安全隐患。防篡改签名:服务器为每个Cookie项生成签名。如果用户篡改Cookie,则与签名无法对应上。以此,来判断数据是否被篡改。原理如下:服务端提供一个签名生成算法secret根据方法生成签名secret(wall)=34Yult8i将生成的签名放入对应的Cookie项username=wall|34Yult8i。其中,内容和签名用|隔开。服务端根据接收到的内容和签名,校验内容是否被篡改。举个栗子:比如服务器接收到请求中的Cookie项username=pony|34Yult8i,然后使用签名生成算法secret(pony)=666。 算法得到的签名666和请求中数据的签名不一致,则证明数据被篡改。Session(会话)1.什么是sessionsession是一种服务器机制,是存储在服务器上的信息,主要配合cookie完成浏览器的身份认证和状态存储方式多种多样,可以是服务器的内存中,或者是mongo数据库,redis内存数据库中。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。Session相对于cookie较安全点,当用户请求服务器的时候,服务器会把数据临时存下来,如果退出网站后,session会被销毁。Session是基于cookie实现的,浏览器第一次访问服务器时,服务器创建一个Session,同时生成一个唯一的会话key,即sessionID。接着sessionID及session分别作为key和value保存到缓存中,也可以保存到数据库中,然后服务器把sessionID通过set-cookie的方式写入浏览器,浏览器下次访问服务器时直接携带上cookie中的sessionID,服务器再根据sessionID找到对应的session进行匹配,来判断用户是否登录2.session鉴权过程【1】 客户端发起登录请求,服务器端创建session,并通过set-cookie将生成的sessionID写入的客户端的cookie中。【2】 在发起其他需要权限的接口的时候,客户端的请求体的Header部分会携带sessionID发送给服务端。然后根据这个sessionId去找服务器端保存的该客户端的session,然后判断该请求是否合法。3.cookie和session的区别Token(身份令牌)1.什么是tokentoken的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库浏览器第一次访问服务器,根据传过来的唯一标识userId,服务端会通过一些算法,如常用的HMAC-SHA256算法,然后加一个密钥,生成一个token,然后通过BASE64编码一下之后将这个token发送给客户端;客户端将token保存起来,下次请求时,带着token,服务器收到请求后,然后会用相同的算法和密钥去验证token,如果通过,执行业务操作,不通过,返回不通过信息;2.token生成方式浏览器第一次访问服务器时,服务器根据传过来的唯一标识userId,通过一些算法,加一个密钥,生成一个token,接着通过base64编码将token返回给客户端。客户端将token保存起来,下次请求时需要带着token,服务器收到请求后,用相同的算法和密钥去验证token3.token和session的区别token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;session服务器会保存一份,可能保存到缓存,文件,数据库;同样,session和token都是有过期时间一说,都需要去管理过期时间;其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。 而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?sessionID是基于cookie实现的,而token不需要基于cookie。这就导致了sessionID只能用在浏览器上,对于原生的应用无法实现。原生的应用是不具备cookie的特性的。另外sessionID可以实现服务端注销会话,而token不能(当然你可以把用户登陆的token存入到redis中,但是不推荐token入库)4.token的优点Token作为用户认证的处理方式,有几个优点:无状态,可扩展:不会在服务端存储用户的登录状态,可以很容易的实现服务器的增减支持移动设备,对多类型客户端的支持良好支持跨程序调用,各个接口之间的调用更方便安全可靠5.什么是JSON Web TokenJSON web Token,简称JWT,本质是一个token,是一种紧凑的URL安全方法,用于在网络通信的双方之间传递。一般放在HTTP的headers参数里面的authorization里面,值的前面加Bearer关键字和空格。除此之外,也可以在url和request body中传递。Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。如果你觉得这篇文章对你有所帮助,那就顺便点个赞吧,点点关注不迷路~黑芝麻哇,白芝麻发,黑芝麻白芝麻哇发哈!前端哇发哈 ...

April 17, 2019 · 1 min · jiezi

Cookie和Session你不能不知道的秘密

1、cookie是什么由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。如下图:1.1、cookie的属性属性项属性项介绍Name一个唯一确定cookie的名称(cookie名称不区分大小写,实践中最好区分,因为一些服务器会区分),URL编码Value存储cookie的字符串值,值必须经过URL编码Expires过期时间,在这个时间点后Cookie失效Domain生成Cookie域名Path该Cookie是在当前那个路径下生成的Secure加密设置,设置他之后,只会在SSL连接时才会回传该Cookie这里我只要说二个点:1、Expires:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效(持久级别Cookie)。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效(会话级别Cookie),浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1;2、Domain: 我们现在有二个域名。域名A:b.f.com,域名B:d.f.com;显然域名A和域名B都是f.com的子域名如果我们在域名A中的Cookie的domain设置为.f.com,那么.f.com及其子域名都可以获取这个Cookie,即域名A和域名B都可以获取这个Cookie如果域名A没有显式设置Cookie的domain方法,那么domain就为.b.f.com,不一样的是,这时,域名A的子域名将无法获取这个CookieHttpOnly: 这个属性是面试的时候常考的,如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击1.2、cookie的操作由于js没有给原生的操作方法,我们可以简单地封装一下:var cookieUtil = { getItem: function (name) { var cookieName = encodeURIComponent(name) + “=”, cookieStart = document.cookie.indexOf(cookieName), cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(’;’, cookieStart); if (cookieEnd == 1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)) } return cookieValue; }, setItem: function (name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + “=” + encodeURIComponent(value); if (expires) { cookieText += “;expires=” + expires.toGMTString(); } if (path) { cookieText += “;path=” + path; } if (domain) { cookieText += “;domain=” + domain; } if (secure) { cookieText += “;secure”; } document.cookie = cookieText; }, unset: function (name, path, domain, secure) { this.setItem(name, “”, new Date(0), path, domain, secure) }}CookieUtil.setItem(“name”, ’tom’); // 设置cookieconsole.log(CookieUtil.getItem(’name’));//读取cookieCookieUtil.unset(“name”)//删除cookie1.3、Cookie防篡改机制因为Cookie是存储在客户端,用户可以随意修改。所以,存在一定的安全隐患。防篡改签名:服务器为每个Cookie项生成签名。如果用户篡改Cookie,则与签名无法对应上。以此,来判断数据是否被篡改。原理如下:服务端提供一个签名生成算法secret根据方法生成签名secret(wall)=34Yult8i将生成的签名放入对应的Cookie项username=wall|34Yult8i。其中,内容和签名用|隔开。服务端根据接收到的内容和签名,校验内容是否被篡改。举个栗子:比如服务器接收到请求中的Cookie项username=pony|34Yult8i,然后使用签名生成算法secret(pony)=666。 算法得到的签名666和请求中数据的签名不一致,则证明数据被篡改。2、SessionSession: 是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中.为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。当客户端请求创建一个session的时候,服务器会先检查这个客户端的请求里是否已包含了一个session标识 - sessionId,如果已包含这个sessionId,则说明以前已经为此客户端创建过session,服务器就按照sessionId把这个session检索出来使用(如果检索不到,可能会新建一个)如果客户端请求(一般是通过cookie携带)不包含sessionId,则为此客户端创建一个session并且生成一个与此session相关联的sessionIdsessionId的值一般是一个既不会重复,又不容易被仿造的字符串,这个sessionId将被在本次响应中返回给客户端保存。保存sessionId的方式大多情况下用的是cookie。session 的运行依赖 session id,而 session id 是存在 cookie中的如果客户端的浏览器禁用了 Cookie怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,(在 url 中传递 session_id)即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。3、cookie与session的区别 cookiesession存储位置客户端服务器端存取方式只能保管ASCII字符串够存取任何类型的数据有效期不同Cookie可以设置过期时间属性JSESSIONID的过期时间默许为–1,只需关闭了阅读器该Session就会失效服务器压力Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存跨域支持上的不同Cookie支持跨域名访问,例如将domain属性设置为“.biaodianfu.com”,则以“.biaodianfu.com”为后缀的一切域名均能够访问该Cookie。Session则不会支持跨域名访问。Session仅在他所在的域名内有效。区分路径cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star对作者也是一种鼓励。参考Cookie防篡改机制彻底理解cookie,session,token ...

April 1, 2019 · 1 min · jiezi

PHP面试常考之会话控制

你好,是我琉忆,欢迎您来到PHP面试专栏。本周(2019.2-25至3-1)的一三五更新的文章如下:周一:PHP面试常考之会话控制周三:PHP面试常考之网络协议周五:PHP面试常考题之会话控制和网络协议以下正文的内容来自《PHP程序员面试笔试宝典》书籍,如果转载请保留出处:最近是换工作潮自己刻意“汇总整理了11篇带解析的PHP面试题的文档”,已上传百度云,关注公众号:“琉忆编程库”,回复:“php”,下载链接我发给你。一、sessionPHP的会话也称为Session。PHP在操作Session时,当用户登录或访问一些初始页面时服务器会为客户端分配一个SessionID。SessionID是一个加密的随机数字,在Session的生命周期中保存在客户端。它可以保存在用户机器的Cookie中,也可以通过URL在网络中进行传输。用户通过SessionID可以注册一些特殊的变量,称为会话变量,这些变量的数据保存在服务器端。在一次特定的网站连接中,如果客户端可以通过Cookie或URL找到SessionID,那么服务器就可以根据客户端传来的SessionID访问会话保存在服务器端的会话变量。Session的生命周期只在一次特定的网站连接中有效,当关闭浏览器后,Session会自动失效,之前注册的会话变量也不能再使用。具体的使用步骤如下:1)初始化会话。在实现会话功能之前必须要初始化会话,初始化会话使用session_start()函数。bool session_start(void)该函数将检查SessionID是否存在,如果不存在,则创建一个,并且能够使用预定义数组$_SESSION进行访问。如果启动会话成功,则函数返回TRUE,否则返回FALSE。会话启动后就可以载入该会话已经注册的会话变量以便使用。2)注册会话变量。自PHP 4.1以后,会话变量保存在预定义数组$_SESSION中,所以可以以直接定义数组单元的方式来定义一个会话变量,格式如下:$_SESSION[“键名”]=“值”;会话变量定义后被记录在服务器中,并对该变量的值进行跟踪,直到会话结束或手动注销该变量。3)访问会话变量。要在一个脚本中访问会话变量,首先要使用session_start()函数启动一个会话。之后就可以使用$_SESSION数组访问该变量了。4)销毁会话变量。会话变量使用完后,删除已经注册的会话变量以减少对服务器资源的占用。删除会话变量使用unset()函数,语法格式如下:void unset(mixed $var [, mixed $var [, $… ]])说明:$var是要销毁的变量,可以销毁一个或多个变量。要一次销毁所有的会话变量,使用session_unset();。5)销毁会话。使用完一个会话后,要注销对应的会话变量,然后再调用session_destroy()函数销毁会话,语法格式如下:bool session_destroy ( void )该函数将删除会话的所有数据并清除SessionID,关闭该会话。最近是换工作潮自己刻意“汇总整理了11篇带解析的PHP面试题的文档”,已上传百度云,关注公众号:“琉忆编程库”,回复:“php”,下载链接我发给你。二、cookieCookie可以用来存储用户名、密码、访问该站点的次数等信息。在访问某个网站时,Cookie将html网页发送到浏览器中的小段信息以脚本的形式保存在客户端的计算机上。一般来说,Cookie通过HTTP Headers从服务器端返回浏览器。首先,服务器端在响应中利用Set Cookie Header来创建一个Cookie。然后浏览器在请求中通过Cookie Header包含这个已经创建的Cookie,并且将它返回至服务器,从而完成浏览器的验证。Cookie技术有很多局限性,例如:1)多人共用一台计算机,Cookie数据容易泄露。2)一个站点存储的Cookie信息有限。3)有些浏览器不支持Cookie。4)用户可以通过设置浏览器选项来禁用Cookie。正是由于以上Cookie的一些局限性,所以,在进行会话管理时,SessionID通常会选择Cookie和URL两种方式来保存,而不是只保存在Cookie中。具体而言,Cookie的使用步骤如下:1)创建Cookie。在PHP中创建Cookie使用setcookie()函数,语法格式如下:bool setcookie(string $name [, string $value [, int $expire [, string $path [, string $domain [, bool $secure [, bool $httponly ]]]]]])① $name:表示Cookie的名字。② $value:表示Cookie的值,该值保存在客户端,所以不要保存比较敏感的数据。③ $expire:表示Cookie过期的时间,这是一个UNIX时间戳,即从UNIX纪元开始的秒数。对于$expire的设置一般通过当前时间戳加上相应的秒数来决定。例如,time()+1200表示Cookie将在20min后失效。如果不设置则Cookie将在浏览器关闭之后失效。④ $path:表示Cookie在服务器上的有效路径。默认值为设定Cookie的当前目录。⑤ $domain:表示Cookie在服务器上的有效域名。例如,要使Cookie能在example.com域名下的所有子域都有效,该参数应设为".example.com"。2)访问Cookie。通过setcookie()函数创建的Cookie是作为数组的单元,存放在预定义变量$_COOKIE中。也就是说,直接对$_COOKIE数组单元进行赋值也可以创建Cookie。但$_COOKIE数组创建的Cookie在会话结束后就会失效。3)删除Cookie。Cookie在创建时指定了一个过期时间,如果到了过期时间,那么Cookie将自动被删除。在PHP中没有专门删除Cookie的函数。如果为了安全方面的考虑,在Cookie过期之前就想删除Cookie,那么可以使用setcookie()函数或$_COOKIE数组将已知Cookie的值设为空。示例代码如下:<?php $_COOKIE[“user”]=“administrator”; setcookie(“password”,“123456”,time()+3600); $_COOKIE[“user”]=""; //使用$_COOKIE清除Cookie setcookie(“password”,""); //使用setcookie()函数清除Cookie print_r($_COOKIE); //输出:Array ( [user] => )?>Cookie和Session都是用来实现会话机制的,由于HTTP协议是无状态的,所以要想跟踪一个用户在同一个网站之间不同页面的状态,需要有一个机制,称为会话机制。预告:本周三更新PHP面试常考之网络协议,敬请期待。以上内容摘自《PHP程序员面试笔试宝典》书籍,该书已在天猫、京东、当当等电商平台销售。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。

February 25, 2019 · 1 min · jiezi

Grails通过sessionId获取session对象

Grails通过sessionId获取session对象思路:自定义一个类用来监听session,所有session存入map中,sessionId作为读取的key创建监听类 SessionTrackerpackage com.sessionimport org.springframework.beans.BeansExceptionimport org.springframework.context.ApplicationContextimport org.springframework.context.ApplicationContextAwareimport org.springframework.web.context.WebApplicationContextimport javax.servlet.http.HttpSessionimport javax.servlet.http.HttpSessionEventimport javax.servlet.http.HttpSessionListenerimport java.util.concurrent.ConcurrentHashMapimport java.util.concurrent.ConcurrentMapclass SessionTracker implements HttpSessionListener, ApplicationContextAware { private static final ConcurrentMap<String, HttpSession> sessions = new ConcurrentHashMap<String, HttpSession>(); void setApplicationContext(ApplicationContext applicationContext) throws BeansException { def servletContext = ((WebApplicationContext) applicationContext).getServletContext() servletContext.addListener(this); } void sessionCreated(HttpSessionEvent httpSessionEvent) { sessions.putAt(httpSessionEvent.session.id, httpSessionEvent.session) } void sessionDestroyed(HttpSessionEvent httpSessionEvent) { sessions.remove(httpSessionEvent.session.id) } HttpSession getSessionById(id) { sessions.get(id) }}在 grails-app/conf/resoures.groovy 中注册import com.session.SessionTracker// Place your Spring DSL code herebeans = { // 自定义session监听器 sessionTracker(SessionTracker)}获取sessionpackage com.geneeimport org.springframework.web.context.request.RequestContextHolderimport javax.servlet.http.HttpSessionclass HiController { // 注入监听对象 def sessionTracker def index() { // 获取session def sessionId = RequestContextHolder.currentRequestAttributes().getSessionId() println “原sessionId:$sessionId” // 根据sessionId获取session对象 HttpSession httpSession = sessionTracker.getSessionById(sessionId).getId() println “获取到session后:"+httpSession.getId() // 使session立即失效 sessionTracker.getSessionById(sessionId).invalidate() render sessionId }} ...

February 22, 2019 · 1 min · jiezi

Tensorflow源码解析2 -- 前后端连接的桥梁 - Session

1 Session概述Session是TensorFlow前后端连接的桥梁。用户利用session使得client能够与master的执行引擎建立连接,并通过session.run()来触发一次计算。它建立了一套上下文环境,封装了operation计算以及tensor求值的环境。session创建时,系统会分配一些资源,比如graph引用、要连接的计算引擎的名称等。故计算完毕后,需要使用session.close()关闭session,避免引起内存泄漏,特别是graph无法释放的问题。可以显式调用session.close(),或利用with上下文管理器,或者直接使用InteractiveSession。session之间采用共享graph的方式来提高运行效率。一个session只能运行一个graph实例,但一个graph可以运行在多个session中。一般情况下,创建session时如果不指定Graph实例,则会使用系统默认Graph。常见情况下,我们都是使用一个graph,即默认graph。当session创建时,不会重新创建graph实例,而是默认graph引用计数加1。当session close时,引用计数减1。只有引用计数为0时,graph才会被回收。这种graph共享的方式,大大减少了graph创建和回收的资源消耗,优化了TensorFlow运行效率。2 默认sessionop运算和tensor求值时,如果没有指定运行在哪个session中,则会运行在默认session中。通过session.as_default()可以将自己设置为默认session。但个人建议最好还是通过session.run(operator)和session.run(tensor)来进行op运算和tensor求值。operation.run()operation.run()等价于tf.get_default_session().run(operation)@tf_export(“Operation”)class Operation(object): # 通过operation.run()调用,进行operation计算 def run(self, feed_dict=None, session=None): _run_using_default_session(self, feed_dict, self.graph, session) def _run_using_default_session(operation, feed_dict, graph, session=None): # 没有指定session,则获取默认session if session is None: session = get_default_session() # 最终还是通过session.run()进行运行的。tf中任何运算,都是通过session来run的。 # 通过session来建立client和master的连接,并将graph发送给master,master再进行执行 session.run(operation, feed_dict)tensor.eval()tensor.eval()等价于tf.get_default_session().run(tensor), 如下@tf_export(“Tensor”)class Tensor(_TensorLike): # 通过tensor.eval()调用,进行tensor运算 def eval(self, feed_dict=None, session=None): return _eval_using_default_session(self, feed_dict, self.graph, session) def _eval_using_default_session(tensors, feed_dict, graph, session=None): # 如果没有指定session,则获取默认session if session is None: session = get_default_session() return session.run(tensors, feed_dict)默认session的管理tf通过运行时维护的session本地线程栈,来管理默认session。故不同的线程会有不同的默认session,默认session是线程作用域的。# session栈_default_session_stack = _DefaultStack()# 获取默认session的接口@tf_export(“get_default_session”)def get_default_session(): return _default_session_stack.get_default()# _DefaultStack默认session栈是线程相关的class _DefaultStack(threading.local): # 默认session栈的创建,其实就是一个list def init(self): super(_DefaultStack, self).init() self._enforce_nesting = True self.stack = [] # 获取默认session def get_default(self): return self.stack[-1] if len(self.stack) >= 1 else None3 前端Session类型session类图会话Session的UML类图如下分为两种类型,普通Session和交互式InteractiveSession。InteractiveSession和Session基本相同,区别在于InteractiveSession创建后,会将自己替换为默认session。使得之后operation.run()和tensor.eval()的执行通过这个默认session来进行。特别适合Python交互式环境。InteractiveSession自带with上下文管理器。它在创建时和关闭时会调用上下文管理器的enter和exit方法,从而进行资源的申请和释放,避免内存泄漏问题。这同样很适合Python交互式环境。Session和InteractiveSession的代码逻辑不多,主要逻辑均在其父类BaseSession中。主要代码如下@tf_export(‘Session’)class Session(BaseSession): def init(self, target=’’, graph=None, config=None): # session创建的主要逻辑都在其父类BaseSession中 super(Session, self).init(target, graph, config=config) self._default_graph_context_manager = None self._default_session_context_manager = None@tf_export(‘InteractiveSession’)class InteractiveSession(BaseSession): def init(self, target=’’, graph=None, config=None): self._explicitly_closed = False # 将自己设置为default session self.default_session = self.as_default() self.default_session.enforce_nesting = False # 自动调用上下文管理器的__enter()方法 self.default_session.enter() self.explicit_graph = graph def close(self): super(InteractiveSession, self).close() ## 省略无关代码 ## 自动调用上下文管理器的__exit()方法,避免内存泄漏 self._default_session.exit(None, None, None) self._default_session = NoneBaseSessionBaseSession基本包含了所有的会话实现逻辑。包括会话的整个生命周期,也就是创建 执行 关闭和销毁四个阶段。生命周期后面详细分析。BaseSession包含的主要成员变量有graph引用,序列化的graph_def, 要连接的tf引擎target,session配置信息config等。4 后端Session类型在后端master中,根据前端client调用tf.Session(target=’’, graph=None, config=None)时指定的target,来创建不同的Session。target为要连接的tf后端执行引擎,默认为空字符串。Session创建采用了抽象工厂模式,如果为空字符串,则创建本地DirectSession,如果以grpc://开头,则创建分布式GrpcSession。类图如下DirectSession只能利用本地设备,将任务创建到本地的CPU GPU上。而GrpcSession则可以利用远端分布式设备,将任务创建到其他机器的CPU GPU上,然后通过grpc协议进行通信。grpc协议是谷歌发明并开源的远程通信协议。5 Session生命周期Session作为前后端连接的桥梁,以及上下文运行环境,其生命周期尤其关键。大致分为4个阶段创建:通过tf.Session()创建session实例,进行系统资源分配,特别是graph引用计数加1运行:通过session.run()触发计算的执行,client会将整图graph传递给master,由master进行执行关闭:通过session.close()来关闭,会进行系统资源的回收,特别是graph引用计数减1.销毁:Python垃圾回收器进行GC时,调用session.del()进行回收。生命周期方法入口基本都在前端Python的BaseSession中,它会通过swig自动生成的函数符号映射关系,调用C层的实现。5.1 创建先从BaseSession类的init方法看起,只保留了主要代码。def init(self, target=’’, graph=None, config=None): # graph表示构建的图。TensorFlow的一个session会对应一个图。这个图包含了所有涉及到的算子 # graph如果没有设置(通常都不会设置),则使用默认graph if graph is None: self._graph = ops.get_default_graph() else: self._graph = graph self._opened = False self._closed = False self._current_version = 0 self._extend_lock = threading.Lock() # target为要连接的tf执行引擎 if target is not None: self._target = compat.as_bytes(target) else: self._target = None self._delete_lock = threading.Lock() self._dead_handles = [] # config为session的配置信息 if config is not None: self._config = config self._add_shapes = config.graph_options.infer_shapes else: self._config = None self.add_shapes = False self.created_with_new_api = ops.USE_C_API # 调用C层来创建session self.session = None opts = tf_session.TF_NewSessionOptions(target=self.target, config=config) self.session = tf_session.TF_NewSession(self.graph.c_graph, opts, status)BaseSession先进行成员变量的赋值,然后调用TF_NewSession来创建session。TF_NewSession()方法由swig自动生成,在bazel-bin/tensorflow/python/pywrap_tensorflow_internal.py中def TF_NewSession(graph, opts, status): return pywrap_tensorflow_internal.TF_NewSession(graph, opts, status)pywrap_tensorflow_internal包含了C层函数的符号表。在swig模块import时,会加载pywrap_tensorflow_internal.so动态链接库,从而得到符号表。在pywrap_tensorflow_internal.cc中,注册了供Python调用的函数的符号表,从而实现Python到C的函数映射和调用。// c++函数调用的符号表,Python通过它可以调用到C层代码。符号表和动态链接库由swig自动生成static PyMethodDef SwigMethods[] = { // .. 省略其他函数定义 // TF_NewSession的符号表,通过这个映射,Python中就可以调用到C层代码了。 { (char )“TF_NewSession”, _wrap_TF_NewSession, METH_VARARGS, NULL}, // … 省略其他函数定义}最终调用到c_api.c中的TF_NewSession()// TF_NewSession创建session的新实现,在C层后端代码中TF_Session TF_NewSession(TF_Graph* graph, const TF_SessionOptions* opt, TF_Status* status) { Session* session; // 创建session status->status = NewSession(opt->options, &session); if (status->status.ok()) { TF_Session* new_session = new TF_Session(session, graph); if (graph != nullptr) { // 采用了引用计数方式,多个session共享一个图实例,效率更高。 // session创建时,引用计数加1。session close时引用计数减1。引用计数为0时,graph才会被回收。 mutex_lock l(graph->mu); graph->sessions[new_session] = Status::OK(); } return new_session; } else { DCHECK_EQ(nullptr, session); return nullptr; }}session创建时,并创建graph,而是采用共享方式,只是引用计数加1了。这种方式减少了session创建和关闭时的资源消耗,提高了运行效率。NewSession()根据前端传递的target,使用sessionFactory创建对应的TensorFlow::Session实例。Status NewSession(const SessionOptions& options, Session** out_session) { SessionFactory* factory; const Status s = SessionFactory::GetFactory(options, &factory); // 通过sessionFactory创建多态的Session。本地session为DirectSession,分布式为GRPCSession out_session = factory->NewSession(options); if (!out_session) { return errors::Internal(“Failed to create session.”); } return Status::OK();}创建session采用了抽象工厂模式。根据client传递的target,来创建不同的session。如果target为空字符串,则创建本地DirectSession。如果以grpc://开头,则创建分布式GrpcSession。TensorFlow包含本地运行时和分布式运行时两种运行模式。下面来看DirectSessionFactory的NewSession()方法class DirectSessionFactory : public SessionFactory { public: Session NewSession(const SessionOptions& options) override { std::vector<Device> devices; // job在本地执行 const Status s = DeviceFactory::AddDevices( options, “/job:localhost/replica:0/task:0”, &devices); if (!s.ok()) { LOG(ERROR) << s; return nullptr; } DirectSession* session = new DirectSession(options, new DeviceMgr(devices), this); { mutex_lock l(sessions_lock); sessions.push_back(session); } return session; }GrpcSessionFactory的NewSession()方法就不详细分析了,它会将job任务创建在分布式设备上,各job通过grpc协议通信。5.2 运行通过session.run()可以启动graph的执行。入口在BaseSession的run()方法中, 同样只列出关键代码class BaseSession(SessionInterface): def run(self, fetches, feed_dict=None, options=None, run_metadata=None): # fetches可以为单个变量,或者数组,或者元组。它是图的一部分,可以是操作operation,也可以是数据tensor,或者他们的名字String # feed_dict为对应placeholder的实际训练数据,它的类型为字典 result = self.run(None, fetches, feed_dict, options_ptr,run_metadata_ptr) return result def run(self, handle, fetches, feed_dict, options, run_metadata): # 创建fetch处理器fetch_handler fetch_handler = FetchHandler( self.graph, fetches, feed_dict_tensor, feed_handles=feed_handles) # 经过不同类型的fetch_handler处理,得到最终的fetches和targets # targets为要执行的operation,fetches为要执行的tensor _ = self.update_with_movers(feed_dict_tensor, feed_map) final_fetches = fetch_handler.fetches() final_targets = fetch_handler.targets() # 开始运行 if final_fetches or final_targets or (handle and feed_dict_tensor): results = self.do_run(handle, final_targets, final_fetches, feed_dict_tensor, options, run_metadata) else: results = [] # 输出结果到results中 return fetch_handler.build_results(self, results) def do_run(self, handle, target_list, fetch_list, feed_dict, options, run_metadata): # 将要运行的operation添加到graph中 self.extend_graph() # 执行一次运行run,会调用底层C来实现 return tf_session.TF_SessionPRunSetup_wrapper( session, feed_list, fetch_list, target_list, status) # 将要运行的operation添加到graph中 def extend_graph(self): with self.extend_lock: if self.graph.version > self.current_version: # 生成graph_def对象,它是graph的序列化表示 graph_def, self.current_version = self.graph.as_graph_def( from_version=self.current_version, add_shapes=self.add_shapes) # 通过TF_ExtendGraph将序列化后的graph,也就是graph_def传递给后端 with errors.raise_exception_on_not_ok_status() as status: tf_session.TF_ExtendGraph(self.session, graph_def.SerializeToString(), status) self.opened = True逻辑还是十分复杂的,主要有一下几步入参处理,创建fetch处理器fetch_handler,得到最终要执行的operation和tensor对graph进行序列化,生成graph_def对象将序列化后的grap_def对象传递给后端master。通过后端master来run。我们分别来看extend和run。5.2.1 extend添加节点到graph中TF_ExtendGraph()会调用到c_api中,这个逻辑同样通过swig工具自动生成。下面看c_api.cc中的TF_ExtendGraph()方法// 增加节点到graph中,proto为序列化后的graphvoid TF_ExtendGraph(TF_DeprecatedSession* s, const void* proto, size_t proto_len, TF_Status* status) { GraphDef g; // 先将proto反序列化,得到client传递的graph,放入g中 if (!tensorflow::ParseProtoUnlimited(&g, proto, proto_len)) { status->status = InvalidArgument(“Invalid GraphDef”); return; } // 再调用session的extend方法。根据创建的不同session类型,多态调用不同方法。 status->status = s->session->Extend(g);}后端系统根据生成的Session类型,多态的调用Extend方法。如果是本地session,则调用DirectSession的Extend()方法。如果是分布式session,则调用GrpcSession的相关方法。下面来看GrpcSession的Extend方法。Status GrpcSession::Extend(const GraphDef& graph) { CallOptions call_options; call_options.SetTimeout(options.config.operation_timeout_in_ms()); return ExtendImpl(&call_options, graph);}Status GrpcSession::ExtendImpl(CallOptions* call_options, const GraphDef& graph) { bool handle_is_empty; { mutex_lock l(mu); handle_is_empty = handle.empty(); } if (handle_is_empty) { // 如果graph句柄为空,则表明graph还没有创建好,此时extend就等同于create return Create(graph); } mutex_lock l(mu); ExtendSessionRequest req; req.set_session_handle(handle); *req.mutable_graph_def() = graph; req.set_current_graph_version(current_graph_version); ExtendSessionResponse resp; // 调用底层实现,来添加节点到graph中 Status s = master->ExtendSession(call_options, &req, &resp); if (s.ok()) { current_graph_version = resp.new_graph_version(); } return s;}Extend()方法中要注意的一点是,如果是首次执行Extend(), 则要先调用Create()方法进行graph的注册。否则才是执行添加节点到graph中。5.2.2 run执行图的计算同样,Python通过swig自动生成的代码,来实现对C API的调用。C层实现在c_api.cc的TF_Run()中。// session.run()的C层实现void TF_Run(TF_DeprecatedSession* s, const TF_Buffer* run_options, // Input tensors,输入的数据tensor const char** c_input_names, TF_Tensor** c_inputs, int ninputs, // Output tensors,运行计算后输出的数据tensor const char** c_output_names, TF_Tensor** c_outputs, int noutputs, // Target nodes,要运行的节点 const char** c_target_oper_names, int ntargets, TF_Buffer* run_metadata, TF_Status* status) { // 省略一段代码 TF_Run_Helper(s->session, nullptr, run_options, input_pairs, output_names, c_outputs, target_oper_names, run_metadata, status);}// 真正的实现了session.run()static void TF_Run_Helper() { RunMetadata run_metadata_proto; // 调用不同的session实现类的run方法,来执行 result = session->Run(run_options_proto, input_pairs, output_tensor_names, target_oper_names, &outputs, &run_metadata_proto); // 省略代码}最终会调用创建的session来执行run方法。DirectSession和GrpcSession的Run()方法会有所不同。后面很复杂,就不接着分析了。5.3 关闭session通过session.close()来关闭session,释放相关资源,防止内存泄漏。class BaseSession(SessionInterface): def close(self): tf_session.TF_CloseSession(self.session, status)会调用到C API的TF_CloseSession()方法。void TF_CloseSession(TF_Session* s, TF_Status* status) { status->status = s->session->Close();}最终根据创建的session,多态的调用其Close()方法。同样分为DirectSession和GrpcSession两种。::tensorflow::Status DirectSession::Close() { cancellation_manager->StartCancel(); { mutex_lock l(closed_lock); if (closed) return ::tensorflow::Status::OK(); closed = true; } // 注销session if (factory != nullptr) factory->Deregister(this); return ::tensorflow::Status::OK();}DirectSessionFactory中的Deregister()方法如下void Deregister(const DirectSession* session) { mutex_lock l(sessions_lock); // 释放相关资源 sessions.erase(std::remove(sessions.begin(), sessions.end(), session), sessions.end()); }5.4 销毁sessionsession的销毁是由Python的GC自动执行的。python通过引用计数方法来判断是否回收对象。当对象的引用计数为0,且虚拟机触发了GC时,会调用对象的__del()方法来销毁对象。引用计数法有个很致命的问题,就是无法解决循环引用问题,故会存在内存泄漏。Java虚拟机采用了调用链分析的方式来决定哪些对象会被回收。class BaseSession(SessionInterface): def del(self): # 先close,防止用户没有调用close() try: self.close() # 再调用c api的TF_DeleteSession来销毁session if self.session is not None: try: status = c_api_util.ScopedTFStatus() if self.created_with_new_api: tf_session.TF_DeleteSession(self.session, status)c_api.cc中的相关逻辑如下void TF_DeleteSession(TF_Session* s, TF_Status* status) { status->status = Status::OK(); TF_Graph* const graph = s->graph; if (graph != nullptr) { graph->mu.lock(); graph->sessions.erase(s); // 如果graph的引用计数为0,也就是graph没有被任何session持有,则考虑销毁graph对象 const bool del = graph->delete_requested && graph->sessions.empty(); graph->mu.unlock(); // 销毁graph对象 if (del) delete graph; } // 销毁session和TF_Session delete s->session; delete s;}TF_DeleteSession()会判断graph的引用计数是否为0,如果为0,则会销毁graph。然后销毁session和TF_Session对象。通过Session实现类的析构函数,来销毁session,释放线程池Executor,资源管理器ResourceManager等资源。DirectSession::~DirectSession() { for (auto& it : partial_runs) { it.second.reset(nullptr); } // 释放线程池Executor for (auto& it : executors) { it.second.reset(); } for (auto d : device_mgr->ListDevices()) { d->op_segment()->RemoveHold(session_handle); } // 释放ResourceManager for (auto d : device_mgr->ListDevices()) { d->ClearResourceMgr(); } // 释放CancellationManager实例 functions.clear(); delete cancellation_manager; // 释放ThreadPool for (const auto& p_and_owned : thread_pools) { if (p_and_owned.second) delete p_and_owned.first; } execution_state.reset(nullptr); flib_def.reset(nullptr);}6 总结Session是TensorFlow的client和master连接的桥梁,client任何运算也是通过session来run。它是client端最重要的对象。在Python层和C++层,均有不同的session实现。session生命周期会经历四个阶段,create run close和del。四个阶段均由Python前端开始,最终调用到C层后端实现。由此也可以看到,TensorFlow框架的前后端分离和模块化设计是多么的精巧。本文作者:扬易阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 21, 2019 · 5 min · jiezi

219. 单页应用 会话管理(session、cookie、jwt)

原文链接:https://github.com/ly525/blog…关键字:http-only, cookie,sessionid, vue-router, react-router, 安全,localStorage, jwt需求描述内部管理平台,需要用户登录之后才能访问。现在将 该平台地址(www.xxx.com/home) 直接发给新来的运营同学前端需要检测该用户是否已登录,如果未登录,则将其 redirect 到登录页面因为该页面为单页应用,路由跳转不涉及后端的 302 跳转,使用前端路由跳转实现思路实现代码// 以 vue-router 为例// 登录中间验证,页面需要登录而没有登录的情况直接跳转登录router.beforeEach((to, from, next) => { const hasToken = document.cookie.includes(‘sessionid’); // 如果采用 jwt,则同样 hasToken = localStorage.jwt const pathNeedAuth = to.matched.some(record => record.meta.requiresAuth); // 用户本地没有后端返回的 cookie 数据 && 前往的页面需要权限 // if (pathNeedAuth && !hasToken ) { next({ path: ‘/login’, query: { redirect: to.fullPath }, }); } else if (hasToken && to.name === ’login’) { // 已登录 && 前往登录页面, 则不被允许,会重定向到首页 next({ path: ‘/’, }); } else { next(); }});应该在进入任何页面之前,判断:该页面是否需要权限才能访问:登录、注册页面不需要权限用户是否已经登录:本地 cookie (或者 localStorage)包含 session 相关信息 Cookie: csrftoken=YaHb…; sessionid=v40ld3x….如果 A页面需要权限 且 本地 cookie中包含了 sessionid 字段,则允许访问A页面,否则跳转到登录页面备注:sessionid 该字段由用户在登录之后,由后端框架通过 response.setCookie 写入前端 ,因此该字段需要和后端同学确认需要后端同学在 response header 中配置cookie中该字段的 http-only属性,允许前端读取 cookie。否则前端通过 document.cookie 读取到的 cookie 将不包含 sessionid 字段这个时候,可能会存在 js 读取cookie 导致不安全的情况,后端同学可以把 cookie 中的某个字段设置为 允许读取,其他 cookie 设置不允许读取,这样即使被第三方不安全脚本获取,也无法产生负面影响。如果用户已登录 && 在浏览器中输入了登录页地址,则将其重定向到首页更多说明这样做,前端就不必再向后端发起 API 做权限鉴定了(后端返回401,前端跳转到 401),减少不必要的API 请求,特别是如果在API响应时间过长的情况下,体验不太友好。用户修改 cookie,伪造 sessionid这样的话,前端就无能为力了,前端鉴权此时认为该用户合法。此时访问首页,将会调用获取数据的API。浏览器会将 用户伪造的 sessionid 带给后端,这时候就需要后端对 sessionid 进行较验了。比如校验前端带来的 sessionid 与数据库中的 sessionid 是否一致用户伪造的数据 sessionid 和 后端数据库中 sessionid 的概率 非常非常低,可以忽略不计,因为 sessionid 的位数一般在 32 位以上,因为里面包含了字母和数字,也就由 32 ^ 36 种可能结论:伪造没有意义,即使用户可以看到页面的样子,但是看不到数据采用 localStorage 而不是 sessionStorage 的原因(SessionStorage 失效场景)原因:sessionStorage 无法跨 Tab 共享用户在新打开一个 Tab,输入已经登录之后的某个页面通过target="_blank" 来打开新页面的时候,会导致会话失效在当前页面执行 Duplicate (复制 Tab),sessionStorage 失效 ...

February 20, 2019 · 1 min · jiezi

都9102年了,还问Session和Cookie的区别

1 前言最近看了一些同学的面经,发现无论什么技术岗位,还是会问到 Session 和 Cookie 的区别。所有学技术的同学都知道 Session 和 Cookie 函数怎么用,知道 Session 和 Cookie 的区别就是 Session 是储存在服务端的,Cookie 是存储在浏览器的。但是实际上是什么东西,一些刚学习技术的同学估计还是模糊,我刚学 PHP 的时候,这种感觉特别明显。PHP 中 Session 和 Cookie 的操作只要操作 $_COOKIE 和 $_SESSION 数组就可以了,而且操作方式和功能一模一样,搞得我一脸懵逼。最后,还是自己实现了一个 Session 操作类才恍然大悟,实质上就是两个不同的存储对象嘛。2 CookieCookie 的诞生是为了能让无状态的 HTTP 报文带上一些特殊的数据,让服务端能够辨识请求的身份。对于 Cookie 的概念就不多说了,Cookie 说简单点就是浏览器上的一个 key-value 存储对象,通过开发者工具直接看到 Cookie 的内容(F12)写入数据方式Cookie 写入数据的方式是通过 HTTP 返回报文 Header 部分 Set-Cookie 字段来设置,一个带有写 Cookie 指令的的 HTTP 返回报文如下HTTP/1.1 200 OKSet-Cookie: SESSIONID=e13179a6-2378-11e9-ac30-fa163eeeaea1; Path=/Transfer-Encoding: chunkedDate: Tue, 29 Jan 2019 07:12:09 GMTServer: localhost上述报文 Set-Cookie 指示浏览器设置 key 为 SESSIONID,value 为 e13179a6-2378-11e9-ac30-fa163eeeaea1 的 Cookie获取数据方式浏览器在发送请求的时候会检查当前域已经设置的 Cookie,在 HTTP 请求报文 Header 部分的 Cookie 字段里面带上 Cookie 的信息。下面捉取了一段 HTTP 报文GET http://10.0.1.24:23333/ HTTP/1.1Host: 10.0.1.24:23333Connection: keep-alivePragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Cookie: SESSIONID=e13179a6-2378-11e9-ac30-fa163eeeaea1从最后的 Cookie 字段看到,浏览器请求时带上了 key 为 SESSIONID,value 为 e13179a6-2378-11e9-ac30-fa163eeeaea1 的数据,后端直接解析 HTTP 报文就能获取 Cookie 的内容。3 SessionSession 在代码里面的语意是记录客户端状态的一个存储对象,是同一个客户端请求共享的数组。这个存储对象可以是文件、缓存系统、数据库。现在假设要使用 redis 来实现 Session 功能,那么就要求浏览器每次请求都要带一个相同的字符串作为身份信息,对应 redis 的 key,redis value 则为 Session 数组序列化的内容。那么如何让浏览器每次请求都带一个身份信息呢,这就是 Session 和 Cookie 的关系,通过 Cookie 传递这个身份信息。流程如下客户端请求服务端检查 Header,发现没有 Cookie,于是生成一个 UUID服务端处理数据,把部分数据(登录信息)存储到 redis 里面,UUID 为 key,用户 id 为 value返回报文中,增加 Set-Cookie 字段,内容带上 UUID浏览器收到报文,把 UUID 写进浏览器存储里面浏览器再次请求,带上当前的域的 Cookie,就是这个 UUID服务端通过 Cookie 字段获取到该 UUID,去 redis 里面获取用户的信息…4 手动实现 Session既然知道了 Session 的原理,我们手动实现一个 Session 操作类,采用文件保存的方式。http 框架采用 web.py,安装方式如下pip install web.pySession类我们要实现的这个类就叫 Sessionclass Session: def init(self): self.session_id = None # session 数组 self._items = dict() self._load()我们所有 session 文件放在 sessions 目录下,文件名为对应的 session id,内容为 Session 数组序列化的字符串。在初始化对象的时候通过获取名为 SESSIONID 的 Cookie,如果没有就生成一个新的。def _load(self): SESSIONID = web.cookies().get(‘SESSIONID’, None) if not SESSIONID: SESSIONID = uuid.uuid() self.session_id = SESSIONID self._loadFromDisk()获取到 SESSIONID 后,检查 sessions 目录下有没有对应的文件,如果有就读取并反序列化def _loadFromDisk(self): """ 从文件加载 SESSION """ file = ‘./sessions/%s’ % self.session_id if os.path.exists(file): f = open(file, ‘rb’) self._items = pickle.load(f) f.close()获取 Session 部分完成了,接下来就是保存 Session,我们要把 SESSIONID 写进 Cookie 里面,这样才能在下次请求时获取到对应的 SESSIONIDdef _setSessionCookie(self): """ Session id 写入 cookie """ web.setcookie(‘SESSIONID’, self.session_id)最后把 Session 的内容保存到文件里面def _saveToDisk(self): """ 保存 SESSION 到文件 """ f = open(’./sessions/%s’ % self.session_id, ‘wb’) pickle.dump(self._items, f) f.close()功能测试我们新建一个文件,叫 server.py,写入测试代码#!/usr/bin/env python# -- coding: utf-8 --# 测试 sessionimport webimport timefrom session import Sessionurls = ( ‘/’, ‘index’)app = web.application(urls, globals())class index: def GET(self): session = Session() if ’login_time’ not in session: session[’login_time’] = int(time.time()) return ’login time: %s’ % session[’login_time’]if name == “main”: app.run()在同一目录新建 session.py 文件,写入 Session 类代码#!/usr/bin/env python# -- coding: utf-8 --# Sessionimport osimport webimport uuidtry: import cPickle as pickleexcept ImportError: import pickleclass Session: __instance = None def new(cls): """ 单例模式 """ if cls.__instance is None: cls.__instance = object.new(cls) return cls.__instance else: return cls.__instance def init(self): self.session_id = None # session 数组 self._items = dict() self._load() def contains(self, key): return key in self._items def getitem(self, key): return self._items.get(key, None) def setitem(self, key, value): self._items[key] = value return True def delitem(self, key): if key in self._items: del self._items[key] return True def del(self): """ 析构函数,结束请求时执行 """ self._setSessionCookie() self._saveToDisk() def _load(self): SESSIONID = web.cookies().get(‘SESSIONID’, None) if not SESSIONID or SESSIONID is None: SESSIONID = uuid.uuid() self.session_id = SESSIONID self._loadFromDisk() def _loadFromDisk(self): """ 从文件加载 SESSION """ file = ‘./sessions/%s’ % self.session_id if os.path.exists(file): f = open(file, ‘rb’) self._items = pickle.load(f) f.close() def _setSessionCookie(self): """ Session id 写入 cookie """ web.setcookie(‘SESSIONID’, self.session_id) def _saveToDisk(self): """ 保存 SESSION 到文件 """ f = open(’./sessions/%s’ % self.session_id, ‘wb’) pickle.dump(self._items, f) f.close()再在同一目录新建 sessions 目录,存放我们的 Session 文件mkdir sessions启动服务[service@chengqm mysession]$ python server.py 23333http://0.0.0.0:23333/浏览器发起请求查看 Cookie 查看 Session 文件内容[service@chengqm mysession]$ cat sessions/e13179a6-2378-11e9-ac30-fa163eeeaea1(dp1S’login_time’p2I1548749002s.可以多次刷新和更换浏览器测试,测试结果是符合我们对 Session 的预期,简陋版 Session 类功能就算实现了。5 如果禁止 Cookie 是否可以获取 Session这是一道面试题,当年竟然能用这个问题问倒过一些朋友,还是有些意思的从前面可以知道,SESSIONID 是通过 Cookie 来传递,如果 Cookie 禁止了,还能获取 SESSIONID 吗? 答案是可以的既然 Cookie 禁止了,那么我们就可以用参数的方法传递 SESSIONID,后端返回的时候,增加一个返回参数,叫 SESSIONID,然后前端存储到 localstorage 里面前端请求的时候,去 localstorage 获取SESSIONID,在请求参数里面增加这一个参数后端 Session 处理,先尝试从 Cookie 中获取 SESSIONID,如果获取不到,再尝试从请求参数中获取 SESSIONID这样,就算禁止 Cookie 也是能获取 Session 的。6 总结最后,我们得出 Session 和 Cookie 区别和联系区别Cookie 是浏览器端的存储对象,有容量限制,通过 HTTP 报文与后端交互Session 是服务端的存储对象,实现的方式可以有文件系统、缓存系统、数据库联系Session 和 Cookie 都是为了实现 HTTP 请求带上客户端状态的方法Session 大多数情况下都是依赖 Cookie 来传递 Session Id ...

January 29, 2019 · 3 min · jiezi

彻底弄懂session,cookie,token

session,cookie和token究竟是什么简述我在写之前看了很多篇session,cookie的文章,有的人说先有了cookie,后有了session。也有人说先有session,后有cookie。感觉都没有讲的很清楚,泛泛而谈。希望本篇文章对大家有所帮助注:本文需要读者有cookie,session,token的相关基础知识。http是一个无状态协议什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。这种无状态的的好处是快速。坏处是假如我们想要把www.zhihu.com/login.html和www.zhihu.com/index.html关联起来,必须使用某些手段和工具cookie和session由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。客户端访问服务器的流程如下首先,客户端会发送一个http请求到服务器端。服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下,具体请看Cookie详解Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端注意cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大小结简而言之, session 有如用户信息档案表, 里面包含了用户的认证信息和登录状态等信息. 而 cookie 就是用户通行证tokentoken 也称作令牌,由uid+time+sign[+固定参数]token 的认证方式类似于临时的证书签名, 并且是一种服务端无状态的认证方式, 非常适合于 REST API 的场景. 所谓无状态就是服务端并不会保存身份认证相关的数据。组成uid: 用户唯一身份标识time: 当前时间的时间戳sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库存放token在客户端一般存放于localStorage,cookie,或sessionStorage中。在服务器一般存于数据库中token认证流程token 的认证流程与cookie很相似用户登录,成功后服务器返回Token给客户端。客户端收到数据后保存在客户端客户端再次访问服务器,将token放入headers中服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码token可以抵抗csrf,cookie+session不行假如用户正在登陆银行网页,该网页未对csrf攻击进行防护。攻击者就可以注入一张图片,该图片src为http://www.bank.com/api/transfer?count=1000&to=Tom。倘若是session+cookie,用户打开网页的时候就已经转给Tom1000元了。因为session一旦建立,当前域页面以及该页面路径以下所有页面都共享cookie。在img请求的瞬间,cookie会被浏览器自动添加到请求头中。但token不同,开发者在每次发起请求时手动将 Token 添加到请求中。即打开页面请求img时,该请求头中没有token分布式情况下的session和token我们已经知道session时有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session就会面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享seesion。这个问题也可以将session存在一个服务器中来解决,但是就不能完全达到负载均衡的效果。当今的几种解决session负载均衡的方法。而token是无状态的,token字符串里就保存了所有的用户信息客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将token存放于localStroage等容器中。客户端每次访问都传递token,服务端解密token,就知道这个用户是谁了。通过cpu加解密,服务端就不需要存储session占用存储空间,就很好的解决负载均衡多服务器的问题了。这个方法叫做JWT(Json Web Token)总结session存储于服务器,可以理解为一个状态列表,拥有一个唯一识别符号sessionId,通常存放于cookie中。服务器收到cookie后解析出sessionId,再去session列表中查找,才能找到相应session。依赖cookiecookie类似一个令牌,装有sessionId,存储在客户端,浏览器通常会自动添加。token也类似一个令牌,无状态,用户信息都被加密到token中,服务器收到token后解密就可知道是哪个用户。需要开发者手动添加。jwt只是一个跨域认证的方案参考文章Cookie、Session、Token那点事儿cookie,token验证的区别有了cookie为什么需要sessionCSRF Token的设计是否有其必要性cookie,token,session三者的问题和解决方案负载均衡集群中的session解决方案JWT介绍Json Web Token 入门教程谢谢本文如有错误,欢迎留言讨论

January 9, 2019 · 1 min · jiezi

从koa-session源码解读session本质

前言Session,又称为“会话控制”,存储特定用户会话所需的属性及配置信息。存于服务器,在整个用户会话中一直存在。然而:session 到底是什么?session 是存在服务器内存里,还是web服务器原生支持?http请求是无状态的,为什么每次服务器能取到你的 session 呢?关闭浏览器会过期吗?本文将从 koa-session(koa官方维护的session中间件) 的源码详细解读 session 的机制原理。希望大家读完后,会对 session 的本质,以及 session 和 cookie 的区别有个更清晰的认识。基础知识相信大家都知道一些关于 cookie 和 session 的概念,最通常的解释是 cookie 存于浏览器,session 存于服务器。cookie 是由浏览器支持,并且http请求会在请求头中携带 cookie 给服务器。也就是说,浏览器每次访问页面,服务器都能获取到这次访问者的 cookie 。但对于 session 存在服务器哪里,以及服务器是通过什么对应到本次访问者的 session ,其实问过一些后端同学,解释得也都比较模糊。因为一般都是服务框架自带就有这功能,都是直接用。背后的原理是什么,并不一定会去关注。如果我们使用过koa框架,就知道koa自身是无法使用 session 的,这就似乎说明了 session 并不是服务器原生支持,必须由 koa-session 中间件去支持实现。那它到底是怎么个实现机制呢,接下来我们就进入源码解读。源码解读koa-session:https://github.com/koajs/session建议感兴趣的同学可以下载代码先看一眼解读过程中贴出的代码,部分有精简koa-session结构来看 koa-session 的目录结构,非常简单;主要逻辑集中在 context.js 。├── index.js // 入口├── lib│ ├── context.js│ ├── session.js│ └── util.js└── package.json先给出一个 koa-session 主要模块的脑图,可以先看个大概:屡一下流程我们从 koa-session 的初始化,来一步步看下它的执行流程:先看下 koa-sessin 的使用方法:const session = require(‘koa-session’);const Koa = require(‘koa’);const app = new Koa();app.keys = [‘some secret hurr’];const CONFIG = { key: ‘koa:sess’, // 默认值,自定义cookie中的key maxAge: 86400000};app.use(session(CONFIG, app)); // 初始化koa-session中间件app.use(ctx => { let n = ctx.session.views || 0; // 每次都可以取到当前用户的session ctx.session.views = ++n; ctx.body = n + ’ views’;});app.listen(3000);初始化初始化 koa-session 时,会要求传入一个app实例。实际上,正是在初始化的时候,往 app.context 上挂载了session对象,并且 session 对象是由 lib/context.js 实例化而来,所以我们使用的 ctx.session 就是 koa-session 自己构造的一个类。我们打开koa-session/index.js:module.exports = function(opts, app) { opts = formatOpts(opts); // 格式化配置项,设置一些默认值 extendContext(app.context, opts); // 划重点,给 app.ctx 定义了 session对象 return async function session(ctx, next) { const sess = ctx[CONTEXT_SESSION]; if (sess.store) await sess.initFromExternal(); await next(); if (opts.autoCommit) { await sess.commit(); } };};通过内部的一次初始化,返回一个koa中间件函数。一步一步的来看,formatOpts 是用来做一些默认参数处理,extendContext 的主要任务是对 ctx 做一个拦截器,如下:function extendContext(context, opts) { Object.defineProperties(context, { [CONTEXT_SESSION]: { get() { if (this[_CONTEXT_SESSION]) return this[_CONTEXT_SESSION]; this[_CONTEXT_SESSION] = new ContextSession(this, opts); return this[_CONTEXT_SESSION]; }, }, session: { get() { return this[CONTEXT_SESSION].get(); }, set(val) { this[CONTEXT_SESSION].set(val); }, configurable: true, } });}走到上面这段代码时,事实上就是给 app.context 下挂载了一个“私有”的 ContextSession 对象 ctx[CONTEXT_SESSION] ,有一些方法用来初始化它(如initFromExternal、initFromCookie)。然后又挂载了一个“公共”的 session 对象。为什么说到“私有”、“公共”呢,这里比较细节。用到了 Symbol 类型,使得外部不可访问到 ctx[CONTEXT_SESSION] 。只通过 ctx.session 对外暴露了 (get/set) 方法。再来看下 index.js 导出的中间件函数return async function session(ctx, next) { const sess = ctx[CONTEXT_SESSION]; if (sess.store) await sess.initFromExternal(); await next(); if (opts.autoCommit) { await sess.commit(); }};这里,将 ctx[CONTEXT_SESSION] 实例赋值给了 sess ,然后根据是否有 opts.store ,调用了 sess.initFromExternal ,字面意思是每次经过中间件,都会去调一个外部的东西来初始化 session ,我们后面会提到。接着看是执行了如下代码,也即执行我们的业务逻辑。await next()然后就是下面这个了,看样子应该是类似保存 session 的操作。sess.commit();经过上面的代码分析,我们看到了 koa-session 中间件的主流程以及保存操作。那么 session 在什么时候被创建呢?回到上面提到的拦截器 extendContext ,它会在接到http请求的时候,从 ContextSession类 实例化出 session 对象。也就是说,session 是中间件自己创建并管理的,并非由web服务器产生。我们接着看核心功能 ContextSession 。ContextSession类先看构造函数:constructor(ctx, opts) { this.ctx = ctx; this.app = ctx.app; this.opts = Object.assign({}, opts); this.store = this.opts.ContextStore ? new this.opts.ContextStore(ctx) : this.opts.store;}居然啥屁事都没干。往下看 get() 方法:get() { const session = this.session; // already retrieved if (session) return session; // unset if (session === false) return null; // cookie session store if (!this.store) this.initFromCookie(); return this.session;}噢,原来是一个单例模式(等到使用时候再生成对象,多次调用会直接使用第一次的对象)。这里有个判断,是否传入了 opts.store 参数,如果没有则是用 initFromCookie() 来生成 session 对象。那如果传了 opts.store 呢,又啥都不干吗,WTF?显然不是,还记得初始化里提到的那句 initFromExternal 函数调用么。if (sess.store) await sess.initFromExternal();所以,这里是根据是否有 opts.store ,来选择两种方式不同的生成 session 方式。问:store是什么呢?答:store可以在initFromExternal中看到,它其实是一个外部存储。问:什么外部存储,存哪里的?答:同学莫急,先往后看。initFromCookieinitFromCookie() { const ctx = this.ctx; const opts = this.opts; const cookie = ctx.cookies.get(opts.key, opts); if (!cookie) { this.create(); return; } let json = opts.decode(cookie); // 打印json的话,会发现居然就是你的session对象! if (!this.valid(json)) { // 判断cookie过期等 this.create(); return; } this.create(json);}在这里,我们发现了一个很重要的信息,session 居然是加密后直接存在 cookie 中的。我们 console.log 一下 json 变量,来验证下:initFromeExternalasync initFromExternal() { const ctx = this.ctx; const opts = this.opts; let externalKey; if (opts.externalKey) { externalKey = opts.externalKey.get(ctx); } else { externalKey = ctx.cookies.get(opts.key, opts); } if (!externalKey) { // create a new externalKey this.create(); return; } const json = await this.store.get(externalKey, opts.maxAge, { rolling: opts.rolling }); if (!this.valid(json, externalKey)) { // create a new externalKey this.create(); return; } // create with original externalKey this.create(json, externalKey);}可以看到 store.get() ,有一串信息是存在 store 中,可以 get 到的。而且也是在不断地要求调用 create() 。createcreate()到底做了什么呢?create(val, externalKey) { if (this.store) this.externalKey = externalKey || this.opts.genid(); this.session = new Session(this, val);}它判断了 store ,如果有 store ,就会设置上 externalKey ,或者生成一个随机id。基本可以看出,是在 sotre 中存储一些信息,并且可以通过 externalKey 去用来获取。由此基本得出推断,session 并不是服务器原生支持,而是由web服务程序自己创建管理。存放在哪里呢?不一定要在服务器,可以像 koa-session 一样骚气地放在 cookie 中!接着看最后一个 Session 类。Session类老规矩,先看构造函数:constructor(sessionContext, obj) { this._sessCtx = sessionContext; this._ctx = sessionContext.ctx; if (!obj) { this.isNew = true; } else { for (const k in obj) { // restore maxAge from store if (k === ‘_maxAge’) this._ctx.sessionOptions.maxAge = obj._maxAge; else if (k === ‘_session’) this._ctx.sessionOptions.maxAge = ‘session’; else this[k] = obj[k]; } }}接收了 ContextSession 实例传来 sessionContext 和 obj ,其他没有做什么。Session 类仅仅是用于存储 session 的值,以及_maxAge,并且提供了toJSON方法用来获取过滤了_maxAge等字段的,session对象的值。session如何持久化保存看完以上代码,我们大致知道了 session 可以从外部或者 cookie 中取值,那它是如何保存的呢,我们回到 koa-session/index.js 中提到的 commit 方法,可以看到:await next();if (opts.autoCommit) { await sess.commit();}思路立马就清晰了,它是在中间件结束 next() 后,进行了一次 commit() 。commit()方法,可以在 lib/context.js 中找到:async commit() { // …省略n个判断,包括是否有变更,是否需要删除session等 await this.save(changed);}再来看save()方法:async save(changed) { const opts = this.opts; const key = opts.key; const externalKey = this.externalKey; let json = this.session.toJSON(); // save to external store if (externalKey) { await this.store.set(externalKey, json, maxAge, { changed, rolling: opts.rolling, }); if (opts.externalKey) { opts.externalKey.set(this.ctx, externalKey); } else { this.ctx.cookies.set(key, externalKey, opts); } return; } json = opts.encode(json); this.ctx.cookies.set(key, json, opts);}豁然开朗了,实际就是默认把数据 json ,塞进了 cookie ,即 cookie 来存储加密后的 session 信息。然后,如果设置了外部 store ,会调用 store.set() 去保存 session 。具体的保存逻辑,保存到哪里,由 store 对象自己决定!小结koa-session 的做法说明了,session 仅仅是一个对象信息,可以存到 cookie ,也可以存到任何地方(如内存,数据库)。存到哪,可以开发者自己决定,只要实现一个 store 对象,提供 set,get 方法即可。延伸扩展通过以上源码分析,我们已经得到了我们文章开头那些疑问的答案。koa-session 中还有哪些值得我们思考呢?插件设计不得不说,store 的插件式设计非常优秀。koa-session 不必关心数据具体是如何存储的,只要插件提供它所需的存取方法。这种插件式架构,反转了模块间的依赖关系,使得 koa-session 非常容易扩展。koa-session对安全的考虑这种默认把用户信息存储在 cookie 中的方式,始终是不安全的。所以,现在我们知道使用的时候要做一些其他措施了。比如实现自己的 store ,把 session 存到 redis 等。这种session的登录方式,和token有什么区别呢这其实要从 token 的使用方式来说了,用途会更灵活,这里就先不多说了。后面会写一下各种登录策略的原理和比较,有兴趣的同学可以关注我一下。总结回顾下文章开头的几个问题,我们已经有了明确的答案。session 是一个概念,是一个数据对象,用来存储访问者的信息。session 的存储方式由开发者自己定义,可存于内存,redis,mysql,甚至是 cookie 中。用户第一次访问的时候,我们就会给用户创建一个他的 session ,并在 cookie 中塞一个他的 “钥匙key” 。所以即使 http请求 是无状态的,但通过 cookie 我们就可以拿到访问者的 “钥匙key” ,便可以从所有访问者的 session 集合中取出对应访问者的 session。关闭浏览器,服务端的 session 是不会马上过期的。session 中间件自己实现了一套管理方式,当访问间隔超过 maxAge 的时候,session 便会失效。那么除了 koa-session 这种方式来实现用户登录,还有其他方法吗?其实还有很多,可以存储 cookie 实现,也可以用 token 方式。另外关于登录还有单点登录,第三方登录等。如果大家有兴趣,可以在后面的文章继续给大家剖析。 ...

December 17, 2018 · 4 min · jiezi

老旧话题:重新看看当年感觉很难的session

原文地址:https://t.ti-node.com/thread/…这基本上算是个老旧的话题了,几乎所有phper在第一次面试的时候都会被问到关于session的问题,如果不出意外,往往是如下三板斧:php的session是什么东西php的session存在什么地方、时候过期php的session和cookie有什么区别这三个问题堪称是关于php session三大基础问题了,要是掌握不好,直接导致面试挂掉,令人唏嘘不已。就以上三个问题简单回顾一下:session翻译成中文,大抵就是会话。我们知道http协议是一个无状态的协议,用极为粗暴的土话表示无状态就是说“你压根不知道这个http请求是哪个犊子发起的”或者“你永远不知道这个http请求的那头到底是上次那个哈士奇还是上上次那个胖子”,所以为了解决这个问题,php引入session来额外标记“到底谁发起的这个http请求”。在php中,php会为每个不同的用户生成一个随机的session id,每个人拥有的session id都是不同的。用户在与服务器产生的每一次交互中,都是利用session id来辨别的用户。让php产生session是一件很容易的事情,直接调用session_start()函数就可以了,如下图就是产生的session文件:其中sess是文件前缀,后面的“njjf8l3lh**ff6”就是你的session id了。但是现在session文件内容是空的,如果我们用如下代码,就可以产生session文件的内容:<?php// 开启sessionsession_start();$_SESSION[‘hello’] = ‘world’;刷新一下网页,然后在再次查看原来的session文件,其中就会有如下内容:就是说,你往全局变量$_SESSION保存的内容本质上都是PHP用文本形式给你存储到服务器上了。服务器根据你的session id读取相应的session文件然后把其中内容读出来,你就会得到你的$_SESSION数据。php的session默认是以文件的形式存储的系统磁盘中,在运行于ubuntu 16.04系统的php 7.0.28中,session是存储于/var/lib/php/sessions文件夹下,可以通过php.ini配置文件中的session.save_path来查看确定。php的过期时间是由php配置项session.gc_maxlifetime来确定的,值的单位是秒钟,默认是1440,也就是说当这个session文件具体上次修改时间超过了1440秒后这个session文件就算是过期了。值得注意的是,过期了不代表这个session文件会马上被垃圾回收机制删除掉,还是有可能会残存一段时间的。那么,究竟何时会被删除?这取决于session.gc_probability和session.gc_divisor两个配置项了。这两个选项的比值 ( session.gc_probability / session.gc_divisor ) 就是触发垃圾回收机制的概率,比如 ( 1 / 100 ) 就可以简单粗暴的理解为“每产生100个请求,就有1次会触发php垃圾回收机制去删除过期的session文件”,所以你记住了:在php中如果你想要一个精确过期的session文件,最起码默认的session配置是绝对不可能的。话说回来,还不都是因为php并没有启动一个单独的线程或者进程去扫描垃圾,所以,也只能用这个“概率”这种粗暴的方式来解决这个问题,又不是不能用。开篇说了,为了搞明白“到底是哈士奇还是胖子”的问题,不得不引入额外标记数据才行,所以实际上,先有的cookie而有后的session,都是为了解决这个问题而产生的。二者的恩怨情仇在于:cookie存在于客户端,而session存在于服务器端,所以session相对更安全服务器可以读取session和cookie,但是客户端(也就是浏览器)只能读取cookie默认情况下,session是离不开cookie的,说到底“安全的session使用方式”必须依靠cookie才能混口饭吃。因为session id就是依靠cookie保存起来的,客户端浏览器每次发送请求都会携带上该cookie,该cookie默认名称是PHPSESSID,数值就是session id。如果说就是用不了cookie,那么session也并不是真的不能用。禁用cookie的情况下,session可以通过配置利用URL来传递,也就是query string直接暴露在网址中,非常不安全非常吓人,严重不推荐这种方式,甚至应该直接禁用!cookie大小稍微有限制(据说考虑用localstorage代替?),session相对宽松大概就这些,不再赘述,我是建议大家配合php.ini文件去研究上面三个问题。如果说真的只回顾一下这三个问题,那岂不是真的应了“一看标题猛如虎,打开内容1-5”?我说过了的,我这里是个正经的博客网站,是个真正的有些内涵的php文化网站,不能只讲些个初级的内容,是个话题都都要无论如何强塞点儿看起来高端的玩意进去撑场面。刚叨叨过了,默认配置下session是以文本文件形保存在服务器的某个文件夹中的,有心的人应该知道“一个目录中文件过多是会降低读取效率的”,所以,在用一些PC软件的时候可以看到这些软件会把TA需要的数据分散开来到不同的次级目录中去。php的session文件也可以这么干,总体来说是比较简单粗暴的。我们需要关注下两个php配置项:一个是session.save_handler,默认这货的值是“files”,也就是文件一个是session.save_path,默认这货的值是一个目录路径,比如/var/lib/php/session。现在我们将这个值改成类似于session.save_path = “N;/path"这样的,其中N是一个正整数,这个数值的含义就是指将目录分成几个层次,比如我们修改成session.save_path = “2;/var/lib/php/sessions”,然后重启一下apache或者fpm进程管理器,然后执行如下代码:<?phpecho “let rock session”;session_start();刷新网页,如下图所示:错误原因相比大家看到了,大概意思就是说“/var/lib/php/sessions/n/j/”这个文件夹不存在,那么切换到这个目录下看看,如下图:果然是空的,也就说没有/n/j这个子目录,看来得手工创建了。然而,真的不能去手工创建,因为你哪儿知道文件夹的名字是啥?回到配置文件一顿研究,在session.save_path配置项附近发现如下英文字样:; NOTE 1: PHP will not create this directory structure automatically.; You can use the script in the ext/session dir for that purpose.; NOTE 2: See the section on garbage collection below if you choose to; use subdirectories for session storage英文比较蹩脚昂,大概翻译一下,多包涵:; NOTE 1: PHP压根不会帮你创建这些文件夹,您自己个儿下载php源码包,到ext目录的session目中去找那个脚本去创建; NOTE 2: 如果你要用子目录存储session的话,记得看下垃圾回收,不看就有坑。(坑在这里直接告诉大家吧,大概就是说你要自己搞子目录存session,那我那个靠信仰和概率才能触发的垃圾回收机制就压根就不触发了,你自己想办法搞定你的过期session,我不管了)所以呢,我们下载一个php源码包,最好是和你运行环境版本一样的php源码包并解压,命令行切到ext/session目录下,如下图:看到那个mod_files.sh没?Linux下就这脚本。mod_files.bat就是给windows用的。给这个脚本chmod +x mod_files.sh加个执行权限,然后查看下使用方式:为了帮助眼近视的读者,友情翻译一下使用方式:./mod_files.sh ‘session文件根目录’ 目录深度 哈希函数比特量对应我的php开发环境就是:./mod_files.sh /var/lib/php/sessions/ 2 5其中第一项就是你存储session的根目录,第二项就是那个N,第三项查看session.hash_bits_per_character配置项然后执行,如下图所示:此时到/var/lib/php/sessions中查看下,果然有目录了,那么,再次刷新网页,本以为很顺利的你可能依然会遇到错误,如下:session_start(): open(/var/lib/php/sessions/n/j/sess_njjf8l3lhfrpq8nrlnl1d9qff6, O_RDWR) failed: Permission denied (13)模模糊糊认得Permission denied这几个字母,好像是权限的问题,难道是因为当前apache进程用户或者fpm进程用户没有权限往这些目录写数据吗?改下这些目录的拥有者撒,改成www-data(我系统中fpm的运行用户),再试试,果然好了!总有刁民以为这就可以解决很大的问题了,然而很悲剧的是:并不是。当前这个方案一定程度可以解决session文件过多的问题,但是依然有两个问题没有得到解决:依然是文件存储,如果访问量太大的话,session文件从硬盘的读取IO或许会成为程序的瓶颈,当然SSD速度一定会好很多如果网站分别部署到了两台服务器上,session无法共享,出现故障。什么意思呢?就是为了保证高可用,网站程序分别在A服务器上和B服务器上,然后最外面使用一台nginx挡在最前面做负载均衡,路人甲的某次http请求可能会被分配到A上,也可能会被分配到B上。路人甲在A上产生的session文件会被保存到A服务器硬盘上,但是服务器B上却没有,如果该用户请求被打到B上的时候,很不幸,session丢失了,一些数据也就会丢失,路人甲八成会骂娘骂客服。也就说,A服务器和B服务器需要共享同一套session!借此,就引入一个问题,就是分布式web部署中,如何解决session共享的问题!关子我就不卖了,没意思,首先想到的是redis,为A和B提供一台C redis服务器就可以了,这样可以“ 多快好省 ”地一举解决问题!按照预想,引入redis后可以顺利解决三个问题:内存级的读写速度,唰唰唰!session轻松easy实现了共享,哪怕以后业务服务器继续横向扩展到服务器Dsession的过期终于可以精确到秒了,说没就没,不用再靠信仰和概率了将session存入redis需要修正如下两处php配置,首先设置session.save_handler = redis,其次是设置ession.save_path = “tcp://127.0.0.1:6379”,然后重启apache或者fpm,刷新一下网页,如果网页不报什么错误,理论上session数据就已经到redis中去了,连接redis查看下key,如下图:从上至下我一共执行了五次redis命令,分别表示:查看所有keys从而获取php分配给我的session文件名称获取这个key的剩余时间,过期后redis该key算失效了查看这个key的数据类型,可以看出是string类型使用get直接获取string的值过了一段时间,我又刷新了一下网页,然后再次用ttl看该key的剩余时间,再次被延长到1440秒了除了redis外(memcache我就不举例了,和redis类似),还有一种方案就是通过nfs共享来实现,大概原理就是弄一台服务器,通过内网共享对所有php业务服务器开放读写,大家知道linux下磁盘是可以挂载在某个文件夹下的,所以将nfs挂载到各个php业务服务器的某个目录下,然后按照上述文章修改响应配置就可以了。这个,我也没有尝试过也懒得自己去尝试了,所以偷个懒直接给大家抛个连接吧,是老叶博客上的一篇文章,《iMySQL | 老叶茶馆,PHP实现多服务器session共享之NFS共享》。装逼完毕,如有问题,火速留言指正! ...

December 3, 2018 · 1 min · jiezi

Session

SessionCookie 和 Session区别与联系由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session。典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几件物品。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件、集群等。服务端如何识别特定的客户?第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,就可以依据此来识别不同客户端了。如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。总结:Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。来源链接:https://www.zhihu.com/questio…什么是session?服务器通过Cookie发送给客户端一个sessionIDsessionID对应服务器里的一小块内存,这里保存着用户的信息,例如登录信息,购物车信息等。每次用户访问服务器的时候,服务器通过浏览器发送来的cookie里的sessionID去读取对应的内存里的信息,以此来知道用户的隐私信息。注意session的好处是防止用户随意篡改cookie,获取别人的信息。如果用户随意篡改了sessionID,那么只能重新登录。因为sessionID是随机数,或者随机数夹杂着一些字母,所以没有可能暴力破解sessionID,获取别的用户的信息。类比:session相当于发会员卡,会员卡上只有卡号(sessionID)。下次去健身房的时候,只要看卡号上,就能确定你本人的去他信息。而cookie相当于把信息都写在会员卡上了。关于session的实现代码演示(nodejs)总结Session 与 Cookie 的关系一般来说,Session 基于 Cookie 来实现。Cookie服务器通过 Set-Cookie 头给客户端一串字符串客户端每次访问相同域名的网页时,必须带上这段字符串客户端要在一段时间内保存这个CookieCookie 默认在用户关闭页面后就失效,后台代码可以任意设置 Cookie 的过期时间大小大概在 4kb 以内SessionSession(不翻译,或翻译为会话)将 SessionID(随机数)通过 Cookie 发给客户端客户端访问服务器时,服务器读取 SessionID服务器有一块内存(哈希表)保存了所有 session通过 SessionID 我们可以得到对应用户的隐私信息,如 id、email这块内存(哈希表)就是服务器上的所有 session

October 16, 2018 · 1 min · jiezi

LocalStorage、SessionStorage

window.sessionStorage和window.localStorage接口用于脚本在浏览器保存数据。LocalStorage基本使用设置window.sessionStorage.setItem(‘key’, ‘value’);window.localStorage.setItem(‘key’, ‘value’);获取window.sessionStorage.getItem(‘key’)window.localStorage.getItem(‘key’)清除localStorage.removeItem(‘key’);window.localStorage.clear()完整用法注意点localStorage是html5技术提供的API,html5中新增加的标签,技术(包括promise,localStorage等),统称为html5session是服务器上存的hash,但localStorage实质也是一个hash,只不过是浏览器上的hashlocalStorage只能存String类型的字符串存函数会转化成字符串。存对象的时候会变成"[object Object]",因为({1:‘xxx’}).toString()//"[object Object]“解决方法:用json来存。例如:localStorage.setItem(‘jsonString’, JSON.stringify({name: ‘mtt’}))使用注意localStorage里的数据和js变量有什么区别?当一个js变量被从新赋值,变量的值当即被改变,但当页面刷新时,变量又回到最初的状态。而localStorage的变量不存在页面里,windows系统存在客户端本地的C盘的一个文件里。简单的使用实例想要只提示用户一次,当下次用户进入这个网站上的时候,不跳出提示框。<script> let already = localStrorage.getItem(‘已经提示了’) if(!already){ alert(‘你好,我们的网站即将改版了’) localStorage.setItrm(‘已经提示了’, true) }</script>总结LocalStorage 跟 HTTP 无关(而cookie是http的一个头)发送HTTP请求时 不会带上 LocalStorage 的值只有相同域名的页面才能互相读取 LocalStorage(没有同源那么严格)每个域名 localStorage 最大存储量为 5Mb 左右(每个浏览器不一样)常用场景:记录有没有提示过用户(没有用的信息,不能记录密码)LocalStorage 永久有效,除非用户主动清理缓存SessionStoragesessionStorage保存的数据用于浏览器的一次会话(session),当会话结束(通常是窗口关闭),数据被清空;localStorage保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。除了保存期限的长短不同,这两个对象的其他方面都一致。总结:SessionStorage 在用户关闭页面(会话结束)后就失效。其余的和localstorage一样

October 16, 2018 · 1 min · jiezi