首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

聊聊登录那些事

前言

原来分享过一篇文章,

Java自定义注解及应用

,当时为了能突出重点,直接在url中传了用户的所属角色,并写了一般的做法。加上最近看了一些人的简历,发现神奇的相似,都有类似商城的项目,为了不至于问些特别Low的问题,便总结了一下登录这个模块所涉及的东西

单机Session

Http协议使用的是无状态连接,这样会造成什么问题呢?看如下Demo

测试

HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,当在一个请求中时HttpServletRequest中的信息可以共享,而在不同的请求中HttpServletRequest并不能共享,这样就会造成用户确实进行过登录操作,但是跳到购物车页面时发现并没有东西,因为应用并不知道访问这个页面的用户是谁

我们可以用一个HttpSession对象保存跨多个请求的会话状态,上面的例子就是保存用户名,看下图理解为什么HttpSession可以跨请求保存状态

对客户的第一个请求,容器会生成一个唯一的会话ID,并通过响应把它返回给客户。客户再在以后的每一个请求中发回这个会话ID。容器看到ID后,就会找到匹配的会话,并把这个会话与请求关联

将上面代码改成如下,再测试

果然能保存会话状态了,客户和容器如何交换会话ID信息呢?其实是通过cookie实现的

看上面能保存会话的代码,我们并没有对cookie进行操作啊,其实是容器几乎会做cookie的所有工作,从最开始的Servlet开始讲这些操作是如何实现的,先看一下Servlet执行过程

用户点击页面发送请求->Web服务器应用(如Apache)->Web容器应用(如tomcat)

容器创建两个对象HttpServletRequest和HttpServletResponse

根据URL找到servlet,并为请求创建或分配一个线程,将请求和响应对象传递给这个servlet线程

容器调用Servlet的service()方法,根据请求的不同类型,service()方法会调用doGet()和doPost()方法,假如请求是HTTP GET请求

doGet()方法生成动态页面,并把这个对象塞到响应对象里。容器有响应对象的一个引用

线程结束,容器把响应对象装换为一个HTTP请求,把它发回给客户,然后删除请求和响应对象

容器使用部署描述文件把URL映射到Servlet ,一个Servlet可以有3个名字,(1)用户知道的URL名,(2)部署人员知道的内部名,(3)实际的文件名

加入使用Spring MVC时要在web.xml中配置如下内容

根据url-pattern->servlet-name->servlet-class的三级映射关系,容器即可根据用户输入的URL找到对应的Servlet

从这个就可以看出其实Spring MVC框架其实在Servlet上面封装了一层,当我们自己用Servlet编写程序时,可以从HttpServletRequest中获取HttpSession,如下

在响应中发送一个会话cookie

我们只需要写上述一行代码即可,来看看容器帮我们做了哪些事情

建立一个新的HttpSession对象

生成唯一的会话ID

建立新的Cookie对象

把会话Id放到cookie中

在响应中设置cookie

从请求得到会话ID

与响应生成会话ID和cookie时用的方法一样

如上面用的方法,我们并没有直接从HttpServletRequest 中获取HttpSession

能直接获取到HttpSession,其实是框架帮我们执行了HttpSession session = req.getSession(),然后设置进来的。我们可以设置session的过期时间,以保证用户登录后长期不操作需要重新登录

分布式Session

当整个服务是分布式的该怎么处理呢?用户在服务器A上登录,结果在服务器B上查看购物车信息,因为在A上登录,HttpSession存在A服务器上,当访问B服务器上的购物车信息因为获取不到用户登录的HttpSession,就会认为用户没有登录,这种情况该怎么处理呢?

实现分布式Session有多种方式,这里就介绍一下用Redis实现分布式Session,其实Spring Session项目就使用Redis实现Session共享的

理解了单机Session,分布式Session也不难理解,主要步骤如下

用户登录以后,先生成类似于sessionId的唯一标识,我们把它叫token

new一个cookie,将token写到cookie当中传递到客户端,并将以key=token,value=用户信息的hash放到redis中,当然cookie和这个hash都可以设置过期时间

客户端在随后的访问中服务器从cookie中拿到这个token,根据这个token去Redis中取到用户信息

当用户登出时只要删除key为token的hash,并且将cookie的最长时间设置为0,重新放回HttpServletResponse即可,鉴于篇幅限制,就不写具体代码了

为什么要在密码中加盐

直接存储

以前系统存储密码时都是类似如下形式

user_id

username

password

1

root

123

假如用户信息泄露,用户的账号安全将受到威胁,参考CSDN密码泄露事件

加密存储

既然明文存储会有安全问题,那就加密存储,一般常用的加密算法是MD5和SHA,当用户注册时,数据库中保存的密码是加密后的密码,当用户登录时先对登录的密码进行MD5,然后和数据库中的密码比对,正确则登录成功,失败则登录失败

user_id

username

password

1

root

202cb962ac59075b964b07152d234b70

以为这样就足够安全了?其实远远不够,有的人将各种密码的MD5值都算出来,做成一个字典,前面说的泄露的CSDN的密码就是一个很好的素材,这样就可以通过

泄露密码的MD5值->MD5字典->原始的字符串的映射关系,得到泄露的密码,针对这种情况,有2种做法,一种是将密码多次进行MD5,即对加密后的MD5值再次进行MD5,另一种就是加盐

加盐存储

user_id

username

password

salt

1

root

202cb962ac59075b964b07152d234b70

7daf8ahfads

由于盐值时随机生成的,我们算一下破解一个用户的密码需要多长时间,假如数据库中密码是如此生成的MD5(明文密码+Salt),MD5的方式也被坏人知道了,假如坏人有600w个字典,得先对这些字典加Salt做一次MD5再匹配,而且还有可能匹配不出来,破解一个账号的成本就这么高,而且盐值和密码的方式进行MD5的方式也多种多样啊,Salt可以插中间,Salt倒序再进行MD5。当然还可以这样啊MD5(Salt[0] + 明文密码 + Salt[5])。如果还觉得不够安全,还可以对加盐生成的MD5值再次MD5啊,次数由你定,这样几乎是破解不了

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180804G1MMS800?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券