用户问题:
家里无线网络突然想登录“干货”
出色的回答:
本文为小编原创文章,首发于原创性高、收藏量高、干货多的微信官方账号Java知识堂。让我们一起成长,一起进步。欢迎关注。
序
最初分享了一篇文章,Java自定义注释和应用。当时为了突出重点,把用户的角色直接传到了url里,写了一般的做法。另外最近看了一些人的简历,发现他们神奇的相似,都有类似商场的物品。为了不问一些特别Low的问题,我总结了一下登录这个模块涉及到的事情。
单机会话
Http协议使用无状态连接。这会造成什么问题?见下文,演示
试验
HttpServletRequest对象表示客户端的请求。当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中。当在一个请求中时,可以共享httpersvletrequest中的信息,但是在不同的请求中,不能共享httpersvletrequest。这将导致用户登录,但是当跳转到购物车页面时,什么都没有,因为应用程序不知道谁在访问这个页面。
我们可以使用一个HttpSession对象来保存多个请求的会话状态。上面的例子是保存用户名。请参见下图,了解为什么HttpSession可以跨请求保存状态。
对于客户的第一个请求,容器将生成一个唯一的会话标识,并通过响应将其返回给客户。客户端在每个后续请求中发回这个会话标识。当容器看到该标识时,它将找到匹配的会话,并将该会话与请求相关联。
将上面的代码改为下面的代码,然后进行测试。
果然,会话状态可以保存。客户和容器如何交换会话ID信息?其实是靠饼干实现的。
看看上面可以保存会话的代码。我们没有操作饼干。事实上,容器几乎可以完成饼干的所有工作。让我们从Servlet的最开始就谈谈这些操作是如何实现的。让我们先来看看Servlet的执行过程。
用户点击页面发送请求——Web服务器应用(如Apache)——Web容器应用(如tomcat)容器创建两个对象:HttpServletRequest和HttpServletResponse。根据URL找到servlet,并为请求创建或分配一个线程。将请求和响应对象传递给servlet线程容器,以调用Servlet的service()方法。根据请求的不同类型,service()方法将调用doGet()和doPost()方法。如果请求是一个HTTP GET请求,doGet()方法将生成一个动态页面,并将这个对象插入到响应对象中。当带有响应对象的容器的引用线程结束时,容器用HTTP请求替换响应对象并将其发送回客户,然后删除请求和响应对象。容器使用部署描述文件将URL映射到Servlet。一个Servlet可以有三个名称,(1)用户已知的URL名称,(2)部署者已知的内部名称,以及(3)实际的文件名。
加入Spring MVC时,应该在web.xml中配置以下内容
根据URL-模式-Servlet-名称-Servlet-类的三级映射关系,容器可以根据用户输入的URL找到对应的Servlet。
由此,我们可以看到Spring MVC框架实际上封装了Servlet上的一层。当我们自己用Servlet编写一个程序时,我们可以从HttpServletRequest中获得HttpSession,如下所示
公共类LoginServlet扩展HttpServlet { @ Override protected void doGet(HttpServletrequest req,HttpServletResponse resp)引发ServletException,IOException { HttpSession session=req . getsession();} @ Override protected void doPost(httpersvletrequest req,HttpServletResponse resp)抛出ServletException,IOException { super.doPost(req,resp);}}在响应中发送会话cookie。
http session session=req . GetSession();我们只需要编写上面的一行代码,就可以看到容器为我们做了什么。
创建一个新的HttpSession对象来生成一个唯一的会话ID,创建一个新的cookie对象,将会话ID放入cookie中,在响应中设置Cookie,并从请求中获取会话Id
http session session=req . GetSession();与响应时生成会话标识和cookie的方式相同。
如果(请求包含一个会话标识cookie){找到了一个与此标识匹配的会话}否则如果(没有会话标识cookie或没有当前会话与此会话标识匹配){创建一个新的会话}与上述方法一样,我们没有直接从HttpServletRequest获取HttpSessi。
on
public String login(HttpSession session, @RequestParam("username") String username)
能直接获取到HttpSession,其实是框架帮我们执行了HttpSession session = req.getSession(),然后设置进来的。我们可以设置session的过期时间,以保证用户登录后长期不操作需要重新登录
分布式Session
当整个服务是分布式的该怎么处理呢?用户在服务器A上登录,结果在服务器B上查看购物车信息,因为在A上登录,HttpSession存在A服务器上,当访问B服务器上的购物车信息因为获取不到用户登录的HttpSession,就会认为用户没有登录,这种情况该怎么处理呢?
实现分布式Session有多种方式,这里就介绍一下用Redis实现分布式Session,其实Spring Session项目就使用Redis实现Session共享的
理解了单机Session,分布式Session也不难理解,主要步骤如下
用户登录以后,先生成类似于sessionId的唯一标识,我们把它叫tokennew一个cookie,将token写到cookie当中传递到客户端,并将以key=token,value=用户信息的hash放到redis中,当然cookie和这个hash都可以设置过期时间客户端在随后的访问中服务器从cookie中拿到这个token,根据这个token去Redis中取到用户信息
当用户登出时只要删除key为token的hash,并且将cookie的最长时间设置为0,重新放回HttpServletResponse即可,鉴于篇幅限制,就不写具体代码了
为什么要在密码中加盐
直接存储
以前系统存储密码时都是类似如下形式
假如用户信息泄露,用户的账号安全将受到威胁,参考CSDN密码泄露事件
加密存储
既然明文存储会有安全问题,那就加密存储,一般常用的加密算法是MD5和SHA,当用户注册时,数据库中保存的密码是加密后的密码,当用户登录时先对登录的密码进行MD5,然后和数据库中的密码比对,正确则登录成功,失败则登录失败
以为这样就足够安全了?其实远远不够,有的人将各种密码的MD5值都算出来,做成一个字典,前面说的泄露的CSDN的密码就是一个很好的素材,这样就可以通过
泄露密码的MD5值->MD5字典->原始的字符串的映射关系,得到泄露的密码,针对这种情况,有2种做法,一种是将密码多次进行MD5,即对加密后的MD5值再次进行MD5,另一种就是加盐
加盐存储
由于盐值时随机生成的,我们算一下破解一个用户的密码需要多长时间,假如数据库中密码是如此生成的MD5(明文密码+Salt),MD5的方式也被坏人知道了,假如坏人有600w个字典,得先对这些字典加Salt做一次MD5再匹配,而且还有可能匹配不出来,破解一个账号的成本就这么高,而且盐值和密码的方式进行MD5的方式也多种多样啊,Salt可以插中间,Salt倒序再进行MD5。当然还可以这样啊MD5(Salt[0] + 明文密码 + Salt[5])。如果还觉得不够安全,还可以对加盐生成的MD5值再次MD5啊,次数由你定,这样几乎是破解不了
<!--
图说天下
-->
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/147833.html
