Spring Security前置知识1--Session与Http请求的状态化

一、Http协议的无状态性与Http请求的有状态化需求

  Http协议本身是无状态的,每次请求都是独立的,并没有“上下文”关系。 但是事实上我们在网站上的操作往往是有状态的。

  1. 我在某个网站购物,或者在某个论坛留言,需要先登录该网站,在登录之后,我希望能够在相当一段时间内,保持登录状态。
  2. 我可能登入之后,没有做登出,有人趁我不在的时候,偷偷用我的电脑买东西,或者去论坛乱发帖子,或者看到我私有邮箱内的信息。所以我希望当我不操作网站的时候,过一段时间后会自动下线。
  3. 我在浏览购物网站时,在添加商品到购物车后,又打开了新的tab页面。在新页面点开的购物车视图中,应该同样能看到已经添加的商品。
  4. 我希望对于某个网站,可以有记忆密码的功能,不用每次都输入密码。

二、会话

  为了维持有状态化,发明了“会话”这种机制,在服务器端和浏览器端各开辟空间,用来存储会话信息,以保持会话。
  从翻译上来讲,Session翻译为会话,而Cookie是完全不相干的。但事实上,他们都是泛指意义上的会话。Session为会话在服务器端的表现形式,Cookie为会话在客户端(浏览器)的表现形式。

1、如何维持会话

  当用户成功登录的时候,创建新会话,用来保存用户的登录状态,将“会话ID/会话”键值对,保存到一个Map中。然后将会话ID传递给浏览器保存。
  浏览器每次都带着会话ID(Request Header中的Cookie)来请求,服务器根据会话ID,到会话Map中找到对应的会话。会话会在被使用的时候自动续期,并在一定时间不操作之后自动过期。

2、什么时候创建会话

  并不是收到请求就会产生Session,只有在调用HttpServletRequest.getSession()时,才会产生Session。紧接着会将sessionId放入Cookie,通过response传给浏览器。
sessionId在Cookie中的key值在不同的语言中叫法不同,java中叫做JSESSIONID

3、是否有sessionId就一定意味着用户已经登录

  只要调用HttpServletRequest.getSession()就会产生会话并返回会话id,但并不能证明已经登录,我们需要其它的信息,比如可以将当前userId存入session。判断能够从session中获取userId,才认为已经登录。

4、如何判断用户已经登录

  我们会准备一个叫做Filter的东西,它拦截每一次的请求,判断请求(请求中的sessionId)是否能够获取会话,并根据会话中的内容来判断用户是否登录,如果已经登录则继续访问用户请求的页面,如果未登录,则跳转到登录页面。

5、session如何计算过期

  在有web.xml的项目中,可以通过如下方式设置Session过期时间,如果不设置也有默认的Session过期时间30分钟。

<session-config>
<session-timeout>120</session-timeout> 
</session-config>

  session从创建就开始计时,每次使用session的时候,都会将计时器刷新,重新计时。如果我们使用了Filter来判断登录,那么每次请求都会使用session,都会将计时器重置。也就是从我们不操作系统开始,超过设置的过期时间,session就会过期,由服务器自动从map中移除。

6、谁来将sessionId发往客户端

  服务器会在创建session之后,将sessionid自动放入response的Cookie,发往前端。
  这个源代码我没找到。。。可能是使用监听之类的松耦合方式放到response中

7、谁来将sessionId发往后端

  浏览器会自动收集当前域下的cookie,将所有的cookie带着,发往后端,里面就有JSESSIONID

8、我们需要在哪些地方控制session

  1. 我们需要在web.xml中配置session过期时间,如果有需要的话。当然,如果你用的是Spring Boot ,开发代码中是没有web.xml的,我们可以通过诸如server.session.timeout=7200来设置session过期时间。我们后面会讲到,这两种其实是一样的。
  2. 我们需要在用户登录成功之后,保存登录凭证(如用户id,现在我们一般不保存用户整体信息),当然我们也可以在任何时候,在Session中保存我们需要的任何数据。
    比如在用户登录的时候,可以将当前时间保存起来,然后可以根据当前时间减去session中的登录时间,统计在线时长。
  3. 我们需要在使用session的时候,通过诸如request.getSession().get(“userId”)来获取保存的Session,从而能够获取当前登录用户。
  4. 在用户登出的时候,我们需要手动调用session.invalidate(),来让当前Session失效。
  5. 在线人数统计。
    我们对Session进行监听,当有session创建的时候,在线人数+1,有session.invalidate()的时候,在线人数-1。
  6. 不建议在Session中存放过多的信息
    Session会占用系统内存,如果在线用户很多,会占用很大的用户内存。
    Session中应该只存方当前登录用户的userId,其它信息全部放到缓存中。每次根据userId到缓存中获取其它的业务数据。

9、Session共享问题

9.1、不同应用间的Session共享问题

  单点登录,我们会在其它篇章里展开讲。

9.2、相同应用间的Session共享问题

  如果我们使用分布式系统,浏览器发送的请求不确定会到哪一个应用服务器。登录请求只会发到某一台服务器并在那个服务器产生Session。其它的请求如果发到了别的服务器上,根据请求中的sessionId将无法找到Session。
  为了解决这个问题,需要Session共享,Session共享其实思路很简单,我们简单列一下,具体实现细节,我们哪天有用到再细细讲

  1. 粘性Session
    负载均衡做一层特殊逻辑,将拥有同一个sessionId的请求每次转发给同一台服务器,这。。。我。。。。
  2. Session复制
    每次产生Session的时候,给同一组机器发kafka消息(携带Session),其它机器将Session本地化保存起来。
  3. 使用缓存存储Session
    所有的机器都不实际存储Session,将Session存到另一个统一的地方,你不是有“多个”服务器让你无法决定是哪台机器吗,那就哪台也不存,存到另一个“单个”的地方。比如缓存,甚至DB(有可能会是分布式缓存/存储,但它们实现了操作的原子性,我们将它们看成一个“单个整体”)。
  4. 单点登录
    有的文章称单点登录能够解决相同应用不同服务器间的Session共享问题。我觉得是不合适的,相同的应用域名是完全相同的,假设ServerA传回了sessionId,第二次访问了ServerB,又传回了新的sessionId,由于域名相同,会将前面的sessionId覆盖,每次只要切换了服务器,都需要重新验证登录,并重新生成Session,很显然是不合适的。

三、我们没有展开的问题

  1. Cookie如何存储,浏览器怎么就能拿到当前网站的Cookie
  2. Cookie存储在哪里,可以跨浏览器吗
  3. 单点登录的展开
    前两个问题会在Cookie篇单独讲,单点登录会在单点登录篇单独讲。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值