会话技术学习

1、自定义MD5工具类加密

什么是MD5算法

之前学习MySQL数据库的时候,接触到了MD5加密,所谓的MD5加密,简单来说,就是将用户输入的明文密码加密为密文,并且这样的加密是不可逆的,也就是不能通过密文得到明文,针对这个转换的过程有一套专门的算法——MD5算法;

什么是加盐加密

对于同一密码,同一加密算法会产生相同的hash值。这样,当用户进行身份验证时,对用户输入的明文密码应用相同的hash加密算法,得出一个hash值,然后使用该hash值和之前存储好的密文值进行对照,如果两个值相同,则密码认证成功,否则密码认证失败。

出于更安全的考虑,即使两个用户输入的是相同的密码,也应该要保存为不同的密文,即使用户输入的是弱密码,也需要考虑进行增强,从而增加密码被攻破的难度。因此出现了加盐加密。

加盐加密过程
在这里插入图片描述
校验过程
在这里插入图片描述
Java实现MD5

用户在注册信息的时候,他填写的密码是以密文被存入数据库,将来用户登陆的时候,我们必须将他再次填写的密码进行MD5加密,然后与数据库中的密文比对,因此需要自定义一个工具类,将普通的字符串转为密文;

  • 在Java中实现MD5是很简单的,在包 java.security有个类 MessageDigest,官方文档如下:

在这里插入图片描述

JAVA代码如下:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
    public static String getMd5Str(String str) throws NoSuchAlgorithmException {
        //MD5 单向加密,无法破解  MD5对数据加密完之后,都是32位

        //获取MD5加密的这个类的对象
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] bytes = digest.digest(str.getBytes());
        StringBuffer sb = new StringBuffer();
        for (byte aByte : bytes) {
            // System.out.println(aByte);
            //  &:有0则0
            //  |:有1则1
            int by = aByte & 0xff; // |  &

            //再把int转换成16进制  字符串
            String string = Integer.toHexString(by);
            if (string.length() < 2) {
                string = "0" + string;
            }
            sb.append(string);
        }

        String md5Str = sb.toString();
        return md5Str;
    }
}

2、JSP简介

JSP详解链接,以下是学习该文章后的一些总结;

  • 上古时代,后台给前台响应数据,通常是美工写好HTML静态页面后,丢给Java程序员。Java程序猿在Servlet中调用Service()拿到数据后,逐句复制HTML静态页面上的HTML语句到Servlet中,根据情况将后端的数据与HTML片段拼接在一起,然后以诸如:out.println("<span>用户名是:"+user.age+<"/span>");的方式疯狂输出;

  • 这样下来,没有几百上前行的输出语句是搞不定的,而同时期的PHP、ASP.NET就优秀多了,它们选择在HTML页面中嵌入相应语言来引入动态数据,避免了手动拷贝HTML片段输出的尴尬局面;

  • 于是,JSP就产生了,JSP是什么?

JSP简介:Java Server Pages,是运行在服务端的页面,是一种动态网页开发技术,它使用JSP标签在HTML网页中插入Java代码,标签通常以<%开头以%>结束

也就是说,我们既可以在JSP里面写Java代码,也可以写HTML代码;但他不是HTML,因为JSP可以嵌入Java代码,这一点HTML打死都做不到;

当有人请求JSP的时候,服务器首先会查找该页面是否存在,不存在就404了,如果存在,服务器内部会经历一次动态资源JSP到静态页面HTML的转化,服务器会自动帮我们把JSP中的HTML片段和数据拼接成静态资源响应给浏览器。也就是说,JSP运行在服务端,但最终通过响应体发给客户端的都是已经转换好的HTML静态页面;

在这里插入图片描述

  • JSP的本质

以下是IDEA自动生成的JSP代码,你可能会觉得除了最上面一行代码(JSP标签,后面介绍),它和HTML页面是一样的;

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
</html>

但其实,JSP和HTML相似只是表象,JSP本质上是一个Java类,他的底层是一个Servlet,是在服务器混的,只不过它的输出结果是HTML,什么是Servlet?

  • 一个Java类,运行在Servlet容器中(Tomcat);
  • 负责接收请求
  • 调用Service处理数据
  • 负责响应数据

在这里插入图片描述

  • 为什么说JSP是一个servlet?

1、自定义一个web工程,创建JSP文件,启动服务器并访问该JSP页面:

my.jsp文件:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>这是一个JSP页面</h1>
</body>
</html>

2、通过服务器启动之后的输出日志中,找到项目编译后的路径,可以查看访问的JSP页面最终编译后的结果:
在这里插入图片描述
在这里插入图片描述

可以看到,my.jsp文件本质上转换为.java文件,最终被编译为一个.class文件;

3、查看源码:
在这里插入图片描述

查看源码可以发现,最终JSP转换成为一个servlet,而页面上的HTML代码最终都被响应对象通过流的方式写出去;

在这里插入图片描述

也就是说,程序帮你做了"逐行复制粘贴HTML代码到servlet"这一步;

  • JSP的功能

JSP标签有多种功能,比如访问数据库、记录用户选择信息、访问JavaBeans组件等,还可以在不同的网页中传递控制信息和共享信息;

JSP与AJAX+HTML

  • 请求与响应这一来一回,就是数据+HTML骨架:

JSP:服务端组装好一切返回给客户端;
AJAX+HTML:服务端返回数据,客户端自己拆分数据组装起来;

  • JSP的局限性在于数据必须在返回给客户端的时候就全部组装好;而有了AJAX,,就可以实现"零件数据"发送;
  • 注意:虽然我们在浏览器的地址栏中键入…….jsp,但是显示出来的页面其实是HTML页面;JSP是服务端的东西,服务端的东西永远不可能直接在浏览器中运行,浏览器只接受静态页面;

JSP早期的3种脚本

  • JSP脚本:用于标识Java代码
<%! %>——————定义成员内容
<% %>——————定义service方法中的局部内容
<%= %>——————定义service方法中的out对象的输入内容
  • <%! %>:定义成员内容
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<%!
    String str = "好好学习,天天向上!";
%>
</body>
</html>

在这里插入图片描述

  • <% %>:定义service方法中的局部内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<%
    String str = "好好学习,天天向上!";
%>
</body>
</html>

在这里插入图片描述

也就是意味着在正常的service()中可以写的代码以及能获取到的对象,在这个脚本中都可以写;

  • <%= %>:定义service方法中的out对象的输入内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<%--out.write("这个是在页面上看到的")--%>
<%="这个是在页面上看到的"%>
</body>
</html>

下面那行脚本的作用和上面是一样的;

在这里插入图片描述

JSP的内置对象out:在JSP页面中无需创建就可以使用,它的作用是用来向客户端输出。

3、会话技术简介

  • 基本概述:

会话是浏览器和服务器之间的多次请求和响应,也就是说,从浏览器访问服务器开始,到访问服务器结束,浏览器关闭为止的这段时间内容产生的多次请求和响应,合起来叫做浏览器和服务器之间的一次会话;在web中指,浏览器和服务器的一次通信,包含多次请求,和多次响应。可以在一次会话的多次请求中共享数据。

  • 为什么要使用会话技术呢?

实际上会话问题解决的还是客户端与服务器之间的通信问题,通过一些会话技术,可以将每个用户的数据以例如cookie/session的形式存储,方便以后用户访问web资源的时候使用;

【假定场景】:A和B两人在某个网上购物商场登陆账号后,A买了一个键盘,而B则购买了一把民谣吉他,这些信息都会被保存下来。用途是:保存账户信息,登录时询问日后是否自动登录,或者根据之前浏览,购买过的商品,分析用户喜欢什么类型的商品,做出精准推送。那么能不能用我们之前学过的HttpServletRequest 对象ServletContext 对象来保存这些数据呢?答案是否定的:

不能用 HttpServletRequest的原因:我们的一次会话中,存在多次请求和响应,而浏览器客户端的每一次请求都会产生一个HttpServletRequest 对象,它只会保存此次请求的信息,例如放入购物车与购买付款是不同的请求,很显然数据没有得到很好的保存处理;

不能用 ServletContext的原因:ServletContext对象是被整个web应用所共享的,将数据都存到这里,无疑会无法区分具体信息的归属;

  • 分类
客户端会话技术 —— Cookie
服务器会话技术 —— Session

4、客户端会话技术Cookie

【故事引入】:

张三家门口新开了一家奶茶店,奶茶店有这样一个优惠,在这里购买奶茶的客户都可以选择免费办理一张会员卡,如果买奶茶的次数满5次,就可以免费获取一杯奶茶;这张会员卡在张三每次前去购买奶茶的时候都会给上面盖个章做一个记录,会员卡保存在哪呢?可以保存在用户手中,但是这样一来存在安全问题,如果用户篡改数据,奶茶店就会亏损;如果将这张会员卡保存在奶茶店,每次客人来了,都需要消耗时间查找该客户的会员卡,也有弊端;

  • Cookies:小甜点,小饼干,理解为存储在客户端的数据;服务器暂存在浏览器中的一些信息文件,他将你在网站输入的一些内容或者一些选项记录下来,当下一次你访问同一个网站的时候,服务器就会主动查询这个Cookie资料,比如我们经常在登陆网站之后,可以选择记住密码;

cookie机制就类似于将会员卡交由用户保存;

  • 为什么存在cookie?

网页之间的交互是通过HTTP协议传输数据的,而Http协议是无状态的协议 (数据提交后,浏览器和服务器的链接就会关闭,在此交互的时候,需要重新建立新的连接) 服务器无法确认用户的信息,于是给每一个用户发一个通行证,通过此确认用户的信息;

  • 具体流程

浏览器向服务器请求一个从未请求过的web页面;

GET /index.html HTTP/1.1
Host: www.example.org
...

浏览器会在接下来的HTTP请求的header中把以上cookie原样返回给服务器;

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: theme=light
Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2020 10:18:14 GMT
...

服务器返回给浏览器此页面,并在返回HTTP包的header中设置set-cookie属性;

GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: theme=light; sessionToken=abc123
…

浏览器再次访问http://www.example.org/spec.html时,会把上一步的cookie设置在header中原样返回给服务器,当然不需要再指定cookie的其他属性,只需要把key-value返回即可;服务器根据设置的cookies就能知道这个客户,把无状态的HTTP请求变成了有状态的请求;

在这里插入图片描述

  • 规范

1、Cookie大小上限为4KB;
2、一个服务器最多在客户端浏览器上保存20个Cookie;
3、一个浏览器最多保存300个Cookie;
4、上面的数据是HTTP对Cookie的规范,但是现在一些浏览器可能会对Cookie规范做了一些扩展,例如每个Cookie的大小为8KB,最多可保存500个Cookie等,不同的浏览器之间是不共享Cookie的。

  • 常用API
//用于在其响应头中增加一个相应的Set-Cookie头字段
addCookie

//用于获取客户端提交的Cookie
GetCookie
public Cookie(String name,String value)

//该方法设置与 cookie 关联的值。
setValue

//该方法获取与 cookie 关联的值。
getValue

//该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie只会在当前 session 会话中持续有效。
setMaxAge

//该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭
getMaxAge

//该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。
setPath

//该方法获取 cookie 适用的路径。
getPath

//该方法设置 cookie 适用的域
setDomain

//该方法获取 cookie 适用的域
getDomain

方法的使用:

------------存储数据
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "MyServlet5", value = "/demo5")
public class MyServlet5 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String num = "100";
        Cookie cookie = new Cookie("num", num);
        Cookie cookie1 = new Cookie("name", "张小");

        //设置Cookie的存活周期,单位是秒
        cookie.setMaxAge(60 * 60);
        cookie1.setMaxAge(60 * 60);

        //响应cookie
        response.addCookie(cookie);
        response.addCookie(cookie1);
    }
}
--------------取出数据
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "MyServlet6", value = "/demo6")
public class MyServlet6 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //取出浏览器所携带的cookie头的数据
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName());
                if(cookie.getName().equals("num")){
                    String value = cookie.getValue();
                    System.out.println(value);
                }
                if(cookie.getName().equals("name")){
                    String value = cookie.getValue();
                    System.out.println(value);
                }
            }
        }

        /*
        100
        张小*/
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
  • 关于cookie需要注意的问题

1、cookie不跨浏览器,也就是说:如果第一次发送请求的时候使用的是火狐浏览器,后台返回来的cookie数据也是存储在火狐浏览器中的,如果第二次请求后台使用的是谷歌浏览器,你想要在后台拿到第一次请求后台存放的数据是不可能的,因为数据在谷歌浏览器中没有保存过;

2、如果服务器版本较低的话,可能会出现cookie保存中文乱码问题,中文属于Unicode字符,英文数据ASCII字符,中文占4个字符或者3个字符,英文占2个字符,这时候cookie使用Unicode字符时需要对Unicode字符进行编码:

Cookie cookie = new Cookie("xxx",URLEncoder.encode(name,"UTF-8"));

3、有效期:通过setMaxAge()方法可以设置Cookie的有效期

  • 如果MaxAge为正数,浏览器会把Cookie写到硬盘中,只要还在MaxAge秒之前,登录网站时该Cookie就有效【不论关闭了浏览器还是服务器】;
  • 如果MaxAge为负数,Cookie是临时性的,也就是本浏览器内有效,关闭浏览器 Cookie 就失效了,Cookie不会写到硬盘中,Cookie默认值就是 -1;
  • 如果MaxAge为0,则表示删除该Cookie。

4、删除和修改:

Cookie存储的方式类似于Map集合,分为名字和值,只不过两者都是String类型的;

修改:
String name = "刮风这天";
Cookie cookie = new Cookie("country",URLEncoder.encode(name,"UTF-8"));
删除:
String name = "刮风这天";
Cookie cookie = new Cookie("country",URLEncoder.encode(name,"UTF-8"));
cookie.setMaxAge(0);
response.addCookie(cookie);

5、Cookie的域名

Cookie的domain属性决定运行访问Cookie的域名,Deomain的值规定为“.域名”,Cookie的隐私安全机制决定Cookie是不可跨域名的。即使是同一级域名,不同的二级域名也不能交接,eg:www.ideal.com 和 www.image.com。如果我希望一级域名相同的网页之间的Cookie之间可以互相访问,需要使用到domain方法

一级域名、二级域名详解

Cookie cookie = new Cookie("name","admin");
cookie.setMaxAge(1000);
cookie.setDomain("ideal.com");
response.addCookie(cookie);
printWriter.writer("使用www.ideal.com域名添加了一个Cookie,只要一级域名是ideal.com即可访问")

6、Cookie的路径

Cookie的path属性决定允许访问Cookie的路径,一般来说,Cookie发布出来,整个网页的资源都可以使用,但是如果只需要某一个Servlet可以获取到Cookie,其他的资源不能或不需要获取;

Cookie cookie = new Cookie("name","admin");
cookie.setPath("/Servlet);
cookie.setMaxAge(1000);
response.addCookie(cookie);
printWriter.writer("该Cookie只能在Servlet1中可以访问到")

7、Cookie的安全属性

HTTP协议不仅是无状态的,而且是不安全的 !如果不希望Cookie在非安全协议中传输,可以设置Cookie的secure属性为true,浏览器只会在HTTPS和SSL等安全协议中传输该Cookie,设置secure属性不会将Cookie的内容加密,如果想保证安全,最好使用md5算法加密;

记住用户名和密码

我们在登陆一些网站的时候,经常会看到一个勾选记住密码的按钮,这个功能可以使用cookie实现,前台勾选了记住密码,下次用户登录的时候,文本框中已经填入用户名和密码,即使用户把浏览器关了;

在这里插入图片描述

代码实现:

思路:
前台的代码:页面一加载,写JSP脚本,对cookie中是否存在用户名和密码进行判断,如果存在,说明用户已经登陆过,并选择了记住密码,取出里面存放的值,填入文本框;
如果不存在,说明用户没有登录过或者登陆了但是没有选择记住密码,这时候展示在文本框内的是空串;
后台的代码:表单提交之后,后台请求处理的servlet中,首先获取用户的信息以及是否勾选了记住密码,如果勾选了记住密码就将用户信息保存在cookie中,并设置存活周期默认为一周;如果没有勾选记住密码,删除cookie;
这样的话,只要勾选了记住密码,用户即使关闭了浏览器,再次打开的时候,用户信息还是保存了的;

JSP代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>用户登陆</title>
    <link rel="icon" href="/LoginAndRegister_war_exploded/aa.ico" mce_href="/LoginAndRegister_war_exploded/aa.ico"
          type="image/x-icon">
    <link rel="shortcut icon" href="/LoginAndRegister_war_exploded/aa.ico"
          mce_href="/LoginAndRegister_war_exploded/aa.ico" type="image/x-icon">
    <style type="text/css">
        input[type="submit"] {
            background-color: #0d65d0;
            color: white;
            border: 0px;
            width: 100px;
            height: 40px;
            font-size: 18px;
        }

        input {
            height: 30px;
        }

        td {
            font-family: Microsoft YaHei;
        }

        span {
            font-size: 14px;
        }
    </style>

    <script>
        /*onblur 事件处理程序:当元素或窗口失去焦点时触发该事件
        onchange事件处理程序:当表单元素获取焦点,并且内容发生改变时,触发该事件*/
        function checkName() {
            var uname = document.getElementById("username");
            var span1 = document.getElementById("myspan1");
            if (!uname.value.trim()) {
                span1.innerText = "用户名不能为空!";
                uname.focus();
                return false;
            } else {
                span1.innerText = "";
            }
            return true;
        }

        function checkPwd() {
            var pwd = document.getElementById("pwd");
            var span2 = document.getElementById("myspan2");
            if (!pwd.value.trim()) {
                span2.innerText = "密码不能为空!";
                pwd.focus();
                return false;
            } else {
                span2.innerText = "";
            }
            return true;
        }

        function check() {
            return checkName() && checkPwd();
        }

        function writeNameAndPwd() {
            <%
            String valueName = "";
            String valuePwd = "";
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (Cookie cookie : cookies) {
                    if(cookie.getName().equals("username")){
                        valueName = cookie.getValue();
                    }
                    if(cookie.getName().equals("pwd")){
                        valuePwd = cookie.getValue();
                    }
                }
            }
            %>
        }
    </script>
</head>
<body onload="writeNameAndPwd()">
<center>
    <h1>登陆页面</h1>
    <form action="/myServlet2_war_exploded/login" method="post" onsubmit="return check()">
        <table border="0" cellspacing="0" cellpadding="0" width="500px" height="200px">
            <tr>
                <td style="text-align: right;">用户名:</td>
                <td><input type="text" name="username" placeholder="请输入用户名" style="width: 60%;" onchange="checkName()"
                           id="username"
                           value="<%=valueName%>"/><span
                        id="myspan1" style="color: red"></span></td>
            </tr>
            <tr>
                <td style="text-align: right;">密码:&nbsp;&nbsp;&nbsp;</td>
                <td><input type="password" name="pwd" placeholder="请输入密码" style="width: 60%;" id="pwd"
                           onchange="checkPwd()"
                           value="<%=valuePwd%>"/><span
                        id="myspan2" style="color: red"></span></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="checkbox" name="remember" value="yes" id="remember"
                           style="text-align:right;vertical-align: middle;margin-left: 70%;">
                    <label style="font-size: 14px;cursor: pointer" for="remember">记住密码</label></td>
                <td></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="登陆" style="margin-left: 40%"/>
                    <a href="#" style="font-size: 13px;margin-left: 10px;">没有账号,请先注册</a>
                </td>
                <td></td>
            </tr>
        </table>
    </form>
</center>
</body>
</html>

后台代码:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "LoginServlet", value = "/login")
public class LoginServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //后台省略了与数据库交互的代码,只做响应前台记住密码

        String username = request.getParameter("username");
        String pwd = request.getParameter("pwd");
        String remember = request.getParameter("remember");

        if (remember != null) {
            if (remember.equals("yes")) {
                //如果用户勾选了记住密码,将用户名和密码存储在Cookie中

                Cookie cookie = new Cookie("username", username);
                Cookie cookie1 = new Cookie("pwd", pwd);

                //默认为用户保存信息为一周
                cookie.setMaxAge(60 * 60 * 24 * 7);
                cookie1.setMaxAge(60 * 60 * 24 * 7);

                //给前台响应cookie数据
                response.addCookie(cookie);
                response.addCookie(cookie1);
            }
        } else {
            //删除cookie
            Cookie cookie = new Cookie("username", username);
            Cookie cookie1 = new Cookie("pwd", pwd);

            //默认为用户保存信息为一周
            cookie.setMaxAge(0);
            cookie1.setMaxAge(0);

            //给前台响应cookie数据
            response.addCookie(cookie);
            response.addCookie(cookie1);
        }
    }
}

在这里插入图片描述

5、服务端会话技术Session

  • Session是另一种记录浏览器状态的机制,Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息,以某种形式记录在服务器,这就是Session;

  • session的流程

1、Session 是个域对象,会话域,浏览器和服务器建立的一次连接,在这次会话中可以包含多次请求和多次响应。

2、浏览器第一次请求服务器的时候,服务器会创建Session对象,你可以在Session对象中存数据,然后把Session对象的ID通过set-cookie头带回去;

3、等浏览器第二次来请求时,会通过cookie头session的ID再携带过来,服务器会根据这个session的id拿出session对象然后从中取出数据。

4、服务端会话技术,要依赖Cookie

  • 为何使用Session? 因为Session可以存储对象,Cookie只能存储字符串,可以解决很多Cookie解决不了的问题;

  • 常用API

//获取Session被创建时间
long getCreationTime()

//获取Session的id
String getId()

//返回Session最后活跃的时间
long getLastAccessedTime()

//获取ServletContext对象
ServletContext getServletContext()

//设置Session超时时间
void setMaxInactiveInterval(int var1)

//获取Session超时时间
int getMaxInactiveInterval()

//获取Session属性
Object getAttribute(String var1)

//获取Session所有的属性名
Enumeration getAttributeNames()

//设置Session属性
void setAttribute(String var1, Object var2)

//移除Session属性
void removeAttribute(String var1)

//销毁该Session
void invalidate()

//该Session是否为新的
boolean isNew()

//session自杀了,安全退出时,清除cookie,把session自杀掉
invalidate() 

getSession()
//会根据jsessionid值查找是否存在sesison对象,如果没找到,则创建新的session
				
getSession(boolean flag):
true:默认值,会根据jsessionid值查找是否存在sesison对象,如果没找到,则创建新的session
false:会根据jsessionid值查找是否存在sesison对象,如果没找到,则返回null
  • Session有着request和ServletContext类似的方法。其实Session也是一个 域对象。Session作为一种记录浏览器状态的机制,只要Session对象没有被销毁,Servlet之间就可以通过Session对象实现通讯;

  • 设置

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute("name", "test");
    }
  • 获取
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession httpSession = request.getSession();
        String value = (String) httpSession.getAttribute("name");
        System.out.println(value);
    }

代码示例:

--------给session域中存放数据
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(name = "Servlet1", value = "/home1")
public class Servlet1 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int num=100;
        HttpSession session = request.getSession();
        String sessionId = session.getId();
        System.out.println(sessionId);
        session.setAttribute("num",num);

        //重定向
        response.sendRedirect(request.getContextPath()+"/home2");
    }
}
------从session域中取出数据
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet(name = "Servlet2", value = "/home2")
public class Servlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //session也是一个域对象,它的范围是一次会话的范围
        System.out.println("home2收到请求了");
        HttpSession session = request.getSession();
        System.out.println(session.getAttribute("num"));
    }
}
-----输出结果:
D56454072425ED7FD9EFD9E4AF3EAB98
home2收到请求了
100
  • 生命周期和有效期

1、用户第一次访问服务器Servlet,jsp等动态资源就会自动创建Session,Session对象保存在内存里,这也就为什么上面的例子可以直接使用request对象获取得Session对象
2、如果访问HTML、Image等静态资源Session不会被创建;
3、Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,无论是否对Session进行读写,服务器都会认为Session活跃了一次。
4、由于会有越来越多的用户访问服务器,因此Session也会越来越多。为了防止内存溢出,服务器会把长时间没有活跃的Session从内存中删除,这个时间也就是Session的超时时间;
5、Session的超时时间默认是30分钟,有三种方式可以对Session的超时时间进行修改:

第一种方式:在tomcat/conf/web.xml文件中设置,时间值为20分钟,所有的WEB应用都有效————<session-timeout>20<session-timeout>
第二种方式:在单个的web.xml文件中设置,对单个web应用有效,如果有冲突,以自己的web应用为准
第三种方式:通过setMaxInactiveInterval()方法设置
httpSession.setMaxInactiveInterval(60);
  • Session 和 Cookie的小区别

1、Session周期指的是不活动的时间,如果我们设置Session是10s,在10s内,没有访问session,session中属性失效,如果在9s的时候,你访问了session,则会重新计时。如果重启了tomcat,或者reload web应用,或者关机了,Session也会失效,我们也可以通过函数让Session失效,invalidate()该方法是让Session中的所有属性失效,常常用于安全退出。如果你希望某个Session属性失效,可以使用方法removeAttribute。

2、Cookie的生命周期就是按累积的时间来算的,不管用户有没有访问过Session;

  • Session

问题:我在Aservlet中设置了Session属性,在Bservlet中获取A的属性;在浏览器中新建一个页面再次访问Bservlet 报空指针异常。现在问题来了:服务器是如何实现一个session为一个用户浏览器服务的?换个说法:为什么服务器能够为不同的用户浏览器提供不同session?

1、HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一个用户。于是乎:服务器向用户浏览器发送了一个名为JESSIONID的Cookie,它的值是Session的id值。其实Session依据Cookie来识别是否是同一个用户。简单来说:Session 之所以可以识别不同的用户,依靠的就是Cookie;

2、该Cookie是服务器自动颁发给浏览器的,不用我们手工创建的。该Cookie的maxAge值默认是-1,也就是说仅当前浏览器使用,不将该Cookie存在硬盘中;

【流程概述:】

访问Aservlet时,服务器就会创建一个Session对象,执行我们的程序代码,执行我们的程序代码,并自动颁发一个Cookie给用户浏览器;
当我用同一个浏览器访问BServlet的时候,浏览器会把Cookie的值通过Http协议带过去给服务器,服务器就知道用哪一个Session;
而当我们使用新会话的浏览器访问BServlet的时候,该新浏览器并没有Cookie,服务器无法辨认使用哪一个Session,所以获取不到值。

  • 浏览器禁用Cookie后Session的使用

遇到两种情况:
1、用户浏览器禁用了Cookie
2、绝大多数手机浏览器都不支持Cookie;

Java Web提供了解决方法:URL地址重写

HttpServletResponse类提供了两个URL地址重写的方法:

encodeURL(String url)
encodeRedirectURL(String url)

需要值得注意的是:这两个方法会自动判断该浏览器是否支持Cookie,如果支持Cookie,重写后的URL地址就不会带有jsessionid了【当然了,即使浏览器支持Cookie,第一次输出URL地址的时候还是会出现jsessionid(因为没有任何Cookie可带)】

String url = "/web-01/Servlet5";
response.sendRedirect(response.encodeURL(url));

URL地址重写的原理:

将Session的id信息重写到URL地址汇总,服务器解析重写后URL获取Session的id,这样一来即使浏览器禁用掉了Cookie,但是Session的id通过服务端传递,还是可以使用Session来记录用户的状态。

代码示例:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int num = 100;
        HttpSession session = request.getSession();
        String sessionId = session.getId();
        System.out.println(sessionId);
        session.setAttribute("num", num);

        //重定向
        String url = request.getContextPath() + "/home2";
        //URL重写:将超链接的url进行重写
        url = response.encodeURL(url);
        response.sendRedirect(url);
    }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //session也是一个域对象,它的范围是一次会话的范围
        System.out.println("home2收到请求了");
        HttpSession session = request.getSession();
        System.out.println(session.getAttribute("num"));
    }

在这里插入图片描述
在这里插入图片描述

理论上来说,我们可以在第一次请求完home1的时候,拿到这个jsessionid的值,然后手动拼接在访问home2的地址栏后面,但是很显然,一般的用户肯定不会这样做,所以就有了URL重写的出现;

  • Session和Cookie的区别

【从存储方式上比较】

Cookie只能存储字符串,如果要存储非ASCII字符串还要对其编码。

Session可以存储任何类型的数据,可以把Session看成是一个容器

【从隐私安全上比较】

Cookie存储在浏览器中,对客户端是可见的。信息容易泄露出去。如果使用Cookie,最好将Cookie加密;

Session存储在服务器上,对客户端是透明的。不存在敏感信息泄露问题。

【从有效期上比较】

Cookie保存在硬盘中,只需要设置maxAge属性为比较大的正整数,即使关闭浏览器,Cookie还是存在的;

Session保存在服务器中,设置maxInactiveInterval属性值来确定Session的有效期。并且Session依赖于名为JSESSIONID的Cookie,该Cookie默认的maxAge属性为-1。如果关闭了浏览器,该Session虽然没有从服务器中消亡,但也就失效了。

服务器的钝化与活化机制:

  • 由于session的存在依赖于cookie,所以,一旦关闭浏览器,session也就随之失效;
  • 但是关闭服务器呢?session中的数据还能取出来吗?答案是可以的,因为这涉及到服务器的钝化与活化机制:

session的钝化:内存的数据写入到硬盘上的过程;
session的活化:与钝化相反,就是将硬盘的数据恢复到内存中(在服务器中项目路径下的work目录中可以看到该文件的出现与消失,关闭服务器,他就出现了,一旦开启服务器,服务器读取了该文件,他就消失了)

  • session的钝化与活化不是针对session的而是针对放入session中的对象的;

代码示例:

@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		int num = 100;
		HttpSession session = request.getSession();
		String sessionId = session.getId();
		System.out.println("这是demo1里面的jsessionid" + sessionId);
		session.setAttribute("num", num);
	}
@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		System.out.println("demo2收到请求了");
		HttpSession session = request.getSession();
		System.out.println(session.getAttribute("num"));
		String sessionId = session.getId();
		System.out.println("这是demo2里面的jsessionid" + sessionId);
	}

在这里插入图片描述

【从对服务器的负担比较】

Session是保存在服务器的,每个用户都会产生一个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存。

Cookie是保存在客户端的。不占用服务器的资源。像baidu、Sina这样的大型网站,一般都是使用Cookie来进行会话跟踪。

【从浏览器的支持上比较】

如果浏览器禁用了Cookie,那么Cookie是无用的了!

如果浏览器禁用了Cookie,Session可以通过URL地址重写来进行会话跟踪。

【从跨域名上比较】

Cookie可以设置domain属性来实现跨域名

Session只在当前的域名内有效,不可跨域名

6、网页小图标

在这里插入图片描述

  • 我们经常看到别人的网站,Header部分有小图标,我们自己其实也可以做;设置项目的favicon图标,方式有两种:全局方式和局部方式

1、全局方式:

进入tomcat服务器\webapps\ROOT,然后用自己项目的favicon.ico替换tomcat自带的favicon.ico图片;

2、局部方式:

只对设置的页面起作用,将自己项目需要的favicon.ico图片作为资源文件(一个图片)添加到项目下,然后在页面如index.jsp的<head>标签中引入该图片

<link rel="icon" href="/favicon.ico" mce_href="/favicon.ico" type="image/x-icon">  
<link rel="shortcut icon" href="/favicon.ico" mce_href="/favicon.ico" type="image/x-icon"> 

代码:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>首页</title>
    <link rel="icon" href="/LoginAndRegister_war_exploded/aa.ico" mce_href="/LoginAndRegister_war_exploded/aa.ico" type="image/x-icon">
    <link rel="shortcut icon" href="/LoginAndRegister_war_exploded/aa.ico" mce_href="/LoginAndRegister_war_exploded/aa.ico" type="image/x-icon">
</head>
<script>
</script>
<body>
</body>
</html>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值