专栏首页Java小白成长之路第47次文章:cookie&session

第47次文章:cookie&session

本周我们来聊聊网络传输中的点心和主菜


会话技术

一、会话

一次会话中包含多次请求和响应。 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。

二、功能

在一次会话的范围内的多次请求间,共享数据

三、方式

  • 客户端会话技术:Cookie
  • 服务器端会话技术:Session

一、概念

客户端会话技术,将数据保存到客户端

二、快速入门

使用步骤:

  1. 创建Cookie对象,绑定数据:new Cookie(String name,String value)
  2. 发送Cookie对象:response.addCookie(Cookie cookie)
  3. 获取Cookie,拿到数据:Cookie[] request.getCookies()

三、cookie实现原理

基于响应头set-cookie和请求头cookie实现

四、cookie的细节

1、一次可不可以发送多个cookie

可以一次创建多个cookie对象,然后使用response调用多次addCookie方法发送cookie即可。

2、cookie在浏览器中保存时间

(1)默认情况下

当浏览器关闭后,Cookie数据被销毁。

(2)持久化存储方法

设置存活时间方法:setMasAge(int seconds),seconds的值有以下几种形式,代表的含义也不相同。

  • 正数:将Cookie数据写到硬盘的文件中。持久化存储,seconds的值就代表了cookie存活时间
  • 负数:默认值
  • 0:删除cookie信息
3、cookie存中文
  • 在Tomcat 8 之前 cookie中不能直接存储中文数据。需要将中文数据转码---一般采用URL编码
  • 在Tomcat 8 之后 cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
4、 cookie共享问题?

(1)假设在一个Tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?

  • 默认情况下cookie不能共享。
  • setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录。如果要共享,则可以将path设置为"/"

(2)不同的Tomcat服务器间cookie共享问题?

setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享。例如:setDomain(".baidu,com"),那么tiba.baidu.com和news.baidu.com中cookie可以共享。

五、cookie的特点和作用

1、特点
  • cookie存储数据在客户端浏览器
  • 浏览器对于单个cookie的大小有限制(4KB),对同一个域名下的总cookie数量也有限制(20个)
2、作用
  • cookie一般用于存储少量的不太敏感的数据。
  • 在不登录的情况下,完成服务器对客户端的身份识别。

六、案例:记住上一次访问时间

1、需求
  • 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
  • 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
2、分析

(1) 因为这个案例所存储的数据不属于敏感数据,并且根据要求,应该需要将访问时间存放在客户端更为合适,所以我们可以采用cookie来完成。

(2) 在服务器中的servlet判断是否有一个名为lastTime的cookie

1. 有:不是第一次访问
    1. 响应数据:欢迎回来,您上次访问时间为:lastTime的值
    2. 写回cookie:lastTime=当前时间
2. 没有:是第一次访问
    1. 响应数据:您好,欢迎您首次访问
    2. 写回cookie:lastTime=当前时间
3、代码实现
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置输出格式以及编码
        response.setContentType("text/html;charset=utf-8");
        //1.首先获取所有的cookie
        Cookie[] cookies = request.getCookies();
        //2.遍历所有的cookie名称
        boolean flag = false;//标记是否存在一个名为lastTime的cookie
        if (cookies != null||cookies.length > 0){
            for (Cookie cookie : cookies) {
                //获取cookie的名称
                String name = cookie.getName();

                //如果有一个名称为lastTime的cookie,代表不是第一次访问
                if ("lastTime".equals(name)){
                    flag  = true;

                    //1.响应数据
                    String value = cookie.getValue();//获取cookie的值,上一次的访问时间
                    System.out.println("解码前:"+value);
                    //URL解码
                    value = URLDecoder.decode(value,"utf-8");
                    System.out.println("解码后:"+value);
                    response.getWriter().write("<h1>欢迎回来,您上次访问时间为:"+value+"</h1>");

                    //2.写回cookie:lastTime=当前时间
                    Date date = new Date();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                    String str_date = sdf.format(date);
                    System.out.println("编码前:"+str_date);
                    //URL编码
                    str_date = URLEncoder.encode(str_date, "utf-8");
                    System.out.println("编码后:"+str_date);
                    cookie.setValue(str_date);
                    //设置cookie的存活时间
                    cookie.setMaxAge(60 * 60 * 24 * 30);//将cookie进行永久保存一个月
                    response.addCookie(cookie);
                    break;
                }
            }
        }

        //没有lastTime的cookie,则我们创建新的cookie
        if (cookies == null || cookies.length == 0 || flag == false){
            //1.响应数据
            response.getWriter().write("<h1>您好,欢迎您首次访问!</h1>");

            //2.写回cookie:lastTime=当前时间
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
            String str_date = sdf.format(date);
            //URL编码
            str_date = URLEncoder.encode(str_date,"utf-8");
            Cookie cookie = new Cookie("lastTime", str_date);
            cookie.setMaxAge(60 * 60 * 24 * 30);//将cookie进行持久化保存一个月
            response.addCookie(cookie);
        }
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
4、最后效果

首次访问:

再次访问:

JSP:入门学习

一、概念

JSP的全称为:Java server pages ,java服务器端页面。

可以理解为:一个特殊的页面,其中既可以定义HTML标签,又可以定义java代码。主要的功能是用于简化书写代码。

二、原理

jsp本质上就是一个servlet

三、JSP的脚本

JSP定义java代码的方式,主要有以下几种方式:

  • <% 代码 %>:定义的java代码,在service方法中。service方法中可以定义的内容,该脚本中都可以定义。
  • <%! 代码 %>:定义的java代码,在jsp转换后的java类的成员位置。
  • <%= 代码 %>:定义的java代码,会输出到页面上。输出语句中可以定义的内容,该脚本中都可以定义。

四、JSP的内置对象

(1)内置对象的概念

在jsp页面中不需要获取和创建,可以直接使用的对象。

(2)jsp一共有9个内置对象,简单介绍3个。

  • request:
  • response:
  • out:字符输出流对象。可以将数据输出到页面上。和response.getWriter()类似

(3)response.getWriter()和out.write()的区别

  • 在Tomcat服务器真正给客户端做出响应之前,会先找response缓冲区数据,再找out缓冲区数据。
  • response.getWriter()数据输出永远在out.write()之前。

五、案例:改造cookie案例

学习完JSP文件之后,我们将上面利用cookie对象来记录登录时间的案例进行改进。上面我们使用的java代码来进行编写。此次我们使用JSP文件格式来进行编写。具体代码内容如下:

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

    //设置输出格式以及编码
    response.setContentType("text/html;charset=utf-8");
    //1.首先获取所有的cookie
    Cookie[] cookies = request.getCookies();
    //2.遍历所有的cookie名称
    boolean flag = false;//标记是否存在一个名为lastTime的cookie
    if (cookies != null||cookies.length > 0){
        for (Cookie cookie : cookies) {
            //获取cookie的名称
            String name = cookie.getName();

            //如果有一个名称为lastTime的cookie,代表不是第一次访问
            if ("lastTime".equals(name)){
                flag  = true;

                //1.响应数据
                String value = cookie.getValue();//获取cookie的值,上一次的访问时间
                System.out.println("解码前:"+value);
                //URL解码
                value = URLDecoder.decode(value,"utf-8");
                System.out.println("解码后:"+value);
                %>

               <h1>欢迎回来,您上次访问时间为:<%=value%></h1>
<input>
<%
                //2.写回cookie:lastTime=当前时间
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                String str_date = sdf.format(date);
                System.out.println("编码前:"+str_date);
                //URL编码
                str_date = URLEncoder.encode(str_date, "utf-8");
                System.out.println("编码后:"+str_date);
                cookie.setValue(str_date);
                //设置cookie的存活时间
                cookie.setMaxAge(60 * 60 * 24 * 30);//将cookie进行永久保存一个月
                response.addCookie(cookie);

                break;
            }
        }
    }

    //没有lastTime的cookie,则我们创建新的cookie
    if (cookies == null || cookies.length == 0 || flag == false){
        //1.响应数据
        %>

            <h1>您好,欢迎您首次访问!</h1>
<%
        //2.写回cookie:lastTime=当前时间
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String str_date = sdf.format(date);
        //URL编码
        str_date = URLEncoder.encode(str_date,"utf-8");
        Cookie cookie = new Cookie("lastTime", str_date);
        cookie.setMaxAge(60 * 60 * 24 * 30);//将cookie进行持久化保存一个月
        response.addCookie(cookie);
    }

%>
</body>
</html>

tips:在上面的这段代码中,我们对于HTML中的元素可以直接利用HTML的语法写在文件中。而不必像前面的response.getWriter().write()的方法来写。减轻了我们开发中的编写程序的压力。对于java代码,我们仅仅需要利用前面的几种包裹标签即可。 在这段代码中,其实也并没有特别好的显示出JSP的简洁,主要是由于在这个案例中,我们整个代码中使用到的HTML元素较少,大部分其实是java代码。然而当整个页面中需要有大量的HTML元素的时候,其简化作用将会十分明显。比如我们可以在JSP文件中随便加一写input标签等等,都是十分方便的。

我们开看一下最后的页面效果:

Session

一、概念

服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession

二、快速入门

1、获取HttpSession对象

HttpSession session = request.getSession();

2、使用HttpSession对象
  • Object getAttribute(String name)
  • void setAttribute(String name,Object value)
  • void removeAttribute(String name)

三、原理

session的实现,是依赖于cookie的。当我们第一次访问服务器的时候,没有存储session信息的cookie对象,服务器会自行创建一个cookie对象,name为JSESSIONID,value为此session的ID值。一次会话之内,cookie不会被销毁,所以一次会话之内的多次请求之间就可以访问同一个session。

四、细节

1、当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
  • 默认情况下,不是同一个。因为默认情况下,cookie会在客户端浏览器关闭的时候被销毁,所以携带sessionID的cookie也会被销毁。那么当我们再次打开客户端浏览器的时候,就无法找到原来的session了。
  • 如果需要相同,则可以创建cookie,键为JSESSIONID,值为session的ID,设置cookie的存活时间,让cookie持久化保存。 //1.获取session HttpSession session = request.getSession(); System.out.println(session); //期望客户端关闭后,session也能相同 Cookie c = new Cookie("JSESSIONID",session.getId()); c.setMaxAge(60*60); response.addCookie(c);
2、客户端不关闭,服务器关闭后,两次获取的session是同一个么?

不是同一个,但是要确保数据不丢失。Tomcat自动完成以下工作:

  • session的钝化:在服务器正常关闭之前,将session对象序列化到硬盘上
  • session的活化:在服务器启动后,将session文件转化为内存中的session对象即可
3、session什么时候被销毁?

(1)服务器关闭的时候

(2)session对象调用invalidate(),该方法是session自销毁方法。

(3)session默认失效时间为30分钟,如果需要修改的话,可以进入配置文件中找到下面的代码进行修改。

选择性配置修改
<session-config>
    <session-timeout>30</session-timeout>
<session-config>

五、session的特点

(1)session用于存储一次会话的多次请求的数据,存在服务器端

(2)session可以存储任意类型,任意大小的数据

(3)session与cookie的区别:

  • session存储数据在服务器端,cookie在客户端
  • session没有数据大小限制,cookie有
  • session数据安全,cookie相对不安全

案例:用户登录+验证码

一、案例需求

  1. 访问带有验证码的登录页面login.jsp
  2. 用户输入用户名,密码以及验证码
    • 如果用户名和密码输入有误,跳转登录页面,提示:用户名或密码错误
    • 如果验证码输入有误,跳转登录页面,提示:验证码错误
    • 如果全部输入正确,则跳转到主页success.jsp,显示:用户名,欢迎您

二、分析

关于此次案例,我们可以借鉴之前的讲解的两个案例。

案例1:讲解servlet的时候,做过的一个案例,当时仅仅是从登录页面获取用户名和密码,然后与数据库进行比对,查看是否能够登录成功。

案例2:讲解response的时候,我们讲解过一个验证码的案例。

三、代码实现

1、首先我们实现登录页面的展示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>login</title>
    <script>
        window.onload = function () {
            document.getElementById("img").onclick = function () {
                this.src = "/day16_test/checkCodeServlet?=time"+new Date().getTime();
            }
        }
</script>

    <style>
        div{
            color: red;
        }
</style>

</head>
<body>

<form action="/day16_test/loginServlet" method="post">

    <table>
        <tr>
            <td>用户名</td>
            <td><input name="username" type="text"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input name="password" type="password"></td>
        </tr>
        <tr>
            <td>验证码</td>
            <td><input name="checkCode" type="text"></td>
        </tr>
        <tr>
            <td colspan="2"><img id="img" src="/day16/checkCodeServlet"></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>

<div><%=request.getAttribute("cc_error") == null ? "" : request.getAttribute("cc_error")%></div>
<div><%=request.getAttribute("login_error") == null ? "" : request.getAttribute("login_error")%></div>
</body>
</html>

tips:在登录页面上,除了分析图上基本的元素信息之外,我们还需要指定在每次错误输入验证码或者用户名密码错误的时候出现的提示信息。此时就体现了jsp在简化书写方面的优势,可以在编写HTML元素的同时,方便的获取错误信息。

2、然后编写登录界面跳转的servlet
package cn.itcast.servlet;

import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import org.apache.commons.beanutils.BeanUtils;

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 java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置request的编码
        request.setCharacterEncoding("utf-8");

        //2.从表单中获取验证码信息
        String checkCode = (String)request.getParameter("checkCode");

        //2.1从session获取服务器中生成的验证码
        String checkCode_session = (String) request.getSession().getAttribute("checkCode_session");

        //先判断验证码是否正确,忽略大小写比较
        if (checkCode_session != null && checkCode_session.equalsIgnoreCase(checkCode)){
            //验证码正确
            //判断用户名和密码是否一致

            //3.获取所有的参数map
            Map<String, String[]> map = request.getParameterMap();//此处的ParameterMap会被锁定,无法更改其中的map值

            //4创建对象
            User loginUser = new User();

            //4.1使用BeanUtils封装,将所有获取到的map参数都存放在loginUser中
            try {
                BeanUtils.populate(loginUser,map);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

            //5.从数据库中获取与登录信息相同的user对象
            User user = new UserDao().login(loginUser);

            if (user != null){
                //登陆成功
                //存储信息,用户信息
                request.getSession().setAttribute("user",user.getUsername());
                //重定向
                response.sendRedirect(request.getContextPath()+"/success.jsp");
            }else{
                //登录失败
                //存储提示信息到request
                request.setAttribute("login_error","用户名或密码错误");
                //转发到登录页面
                request.getRequestDispatcher("/login.jsp").forward(request,response);
            }
        }else{
            //验证码不一致
            //存储提示信息到request
            request.setAttribute("cc_error","验证码错误");
            //转发到登录页面
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }
        //删除session中存储的验证码
        request.getSession().removeAttribute("checkCode_session");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

tips:按照我们之前的分析图,我们在做判断的时候,更应该先去判断验证码的正确性,这样可以避免不必要的数据库访问,节约资源。关于loginServlet中的其他使用到的类,比如User,UserDao等等,与我们在前面的文章(第45次文章:HTTP&Request)末尾使用的是相同的,需要使用的同学,可以点击查看呀!其中跳转使用到的验证码信息代码,我们在上周的文章(第46次文章:Response)中有信息的讲解,需要的小伙伴可以自取哟!

3、然后构建我们的登陆成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1><%=request.getSession().getAttribute("user")%>,欢迎回来</h1>
</body>
</html>

tips:我们需要根据不同的用户登录情况来改变页面的展示情况,所以需要获取服务器传输过来的用户信息。

四、查看效果

1、当我们访问登录页面时:展示效果如下:
2、当我们输入的验证码错误之后,展示的效果如下:
3、当我们输入的用户名密码错误之后,展示的效果如下:
4、当我们输入的信息都正确之后,展示的效果如下:

本文分享自微信公众号 - Java小白成长之路(Java_xiaobai),作者:鹏程万里

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Cookie与Session的今世前缘

    之前在学习JavaWeb的时候,学习过Cookie与Session的内容,当时也进行了相关的记录,写了一篇学习笔记,具体的学习笔记小伙伴儿们可以自行点击查看(第...

    鹏-程-万-里
  • 独一无二的“你”

    【题目描述】一个数组中其他元素都出现了两次,然而只有一个元素出现了一次,我们需要找到这个特殊的数字。

    鹏-程-万-里
  • 字符串排序---字典序

    本周我们分享一个获取全排列的算法。这道题当时也是花了蛮久的时间才跟着题解写出来!小白经历了这道题目的“煎熬”之后,就为大家保驾护航,一起轻松拿下此题吧!

    鹏-程-万-里
  • 各浏览器Cookie大小、个数限制

    一、浏览器允许每个域名所包含的cookie数:   Microsoft指出InternetExplorer8增加cookie限制为每个域名50个,但IE7似乎也...

    小柒2012
  • session与cookie的区别

    (4)设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。

    葆宁
  • 【小家java】Session和Cookie的区别和联系、分布式session的几种实现方式

    session和cookie两个概念,在web开发是经常被提及到的两个概念。它们之间有联系也有区别,那么本文主要解惑一些咱们平时挺关心的一些区别:

    YourBatman
  • 爬虫cookies详解

    Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。定义于 R...

    星星在线
  • 怎样用 JavaScript 操作 Cookie[每日前端夜话0xC4]

    Web 服务器和 HTTP 服务器是无状态的,因此当 Web 服务器将网页发送到浏览器时,连接会被断开,服务器会忘记与用户相关的所有内容。

    疯狂的技术宅
  • Python爬虫番外篇之关于登录

    常见的登录方式有以下两种: 查看登录页面,csrf,cookie;授权;cookie 直接发送post请求,获取cookie 上面只是简单的描述,下面是详细的针...

    coders
  • 企业面试题: cookies,sessionStorage 和 localStorage 的区别?

    舒克

扫码关注云+社区

领取腾讯云代金券