cookie机制 + java 案例

简介: cookie机制 + java 案例


为什么会有cookie??

       cookie是浏览器在本地存储数据的一种机制首先我们的数据很多都是存放在服务器上的,但是服务端也想这能不能在客户端也存放一些数据。但是又要保证本地的安全,不能让浏览器直接控制本地的存储设备,所以就做了一个中立的做法,也就是说让浏览器开一个口子,使用某些特殊的键值对,来限定数据的存储保证安全,同时又可以让浏览器往本地存储数据。

cookie从哪里来的??

       cookie是从服务器来的。服务器在响应中会带有Set-Cookie字段,通过这个字段就可以把要保存在浏览器本地的数据给返回回去。

cookie到哪里去??

       后续浏览器访问服务器的时候,就会把当前本地的所有cookie都通过http带给服务端

cookie有啥用??

       最典型的场景就是使用cookie保存当前用户的登录状态。就例如我们经常访问b站的时候,只登录了一次,后面关闭页面再次打开,任然保持这上次的登录状态。

       在cookie保存用户身份标识,这样的应用场景之中,此时身份标识该如何分配,以及身份信息该如何存储,都是需要服务器的支持的。这个就要利用一个session会话机制。

session

       session给当前用户分配一个sessionId。同时记录下当前用户的身份信息(可以自定义的),这个id就会被返回到浏览器的cookie中,后续浏览器访问服务器都会带着这个。从而能让服务器识别到当前用户的身份。

下面我们结合代码,来了解一下cookie的机制。

HttpServletRequest类中的相关方法

方法 描述
HttpSession  getSession() 在服务器中获取会话,如果参数为true,则当前不存在会话时会创建新会话,如果参数为false,则当不存在的时候会返回null
Cookie[ ] getCookies() 返回一个数组,包含客户端发送该请求的所有cookie对象,会自动把cookie中的格式解析成为键值对

       下面我们来解析一下这个getSession的用法,首先getSession的参数如果为false,那么就会触发以下流程:

  1. 读取cookie中的sessionid
  2. 然后再服务器这边根据服务器存储的数据和sessionid一起,查询Session对象
  3. 如果查到了就会直接返回这个session对象
  4. 否则返回null

       如果为true的话,与false唯一不同就只有第四步不一样,如下:

  1. 读取cookie中的sessionid
  2. 然后再服务器这边根据服务器存储的数据和sessionid一起,查询Session对象
  3. 如果查到了就会直接返回这个session对象
  4. 否则返回创建一个新的session对象,同时生成一个sessionid,同时以sessionid为key ,以session对象为value构成一个键值对,然后把这个键值对存储到服务器的hash表里面
  5. 同时把sessionid以setcookie 的方式返回给浏览器

       这个对我们实现登录功能非常实用。

简单的实现cookie登录功能

        接下来我们基于httpServlet来实现一个简单的登录功能。

       首先需要提供两个页面:

  1. 登录页(输入用户id,输入密码,然后还有一个登录按钮,点击登录就会发起一个http请求,服务器就会处理这个http请求,服务器处理这个请求的时候就会验证用户名和密码,如果用户名和密码都ok,就会跳转主页)
  2. 主页(不存在任何业务逻辑,仅仅只是展示一个欢迎页面)

       其中的登陆页面就只是单纯的html页面,还需要写一个servlet,实现登录的时候用户名密码校验,还要写一个servlet来生成主页

实现登录页面

       首先先来回忆一下我们传输键值对的方式,一说到传输键值对,那么肯定就要提到html里面的form表单

    <form action="login" method="post">
        <input type="text" name="username"> <!--用户输入的用户名-->
        <br>
        <input type="password" name="password">  <!--用户输入的密码-->
        <br>
        <input type="submit" value="登录">
    </form>

       form会组织这里的数据,以键值对的形式提交给服务器,其中key就是input的name属性,value就是input中用户输入的内容,最终会构造post请求,在body中以键值对的形式,进行组织。服务器通过getParameter方法来获取指定key的value,具体getParameter的方法可以参看我前面的servelet的内容。

       完整的登录页面的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name="username"> <!--用户输入的用户名-->
        <br>
        <input type="password" name="password">  <!--用户输入的密码-->
        <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

实现servlet逻辑

        新建一个类,为loginServlet类来处理上述的登录请求。

       登录的请求形式如下:

  • POST/login
  • Content-Type:application/x-www-form-urlencoded
  • username=zhangshan&password=1233

        一般像登录这样的请求都是post

构造好的登录的处理逻辑如下:

package Login;
 
import javax.jws.WebService;
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;
 
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        resp.setContentType("text/html;charset=utf8");
        // 首先通过getParameter方法来获取对应key的value值
        String username = req.getParameter("name");
        String password = req.getParameter(("password"));
        // 然后来验证这个用户和密码是否正确
        if (username == null || password == null || username.equals("") || password.equals("")){
            resp.getWriter().write("当前用户名和密码不能为空");
            return;
        }
        // 假设此处的密码和用户名只能是zhangsan和1234
        // 逻辑上此处应该是在服务器的数据库里面去查找验证用户名和密码,此处简化步骤
        if (username.equals("zhangsan") || username.equals("lisi")) {
            if (password.equals("1234")) {
                // 此时用户输入密码正确
            } else {
                // 用户密码有误
                resp.getWriter().write();
            }
        } else {
            // 用户名错误
        }
    }
}

这是简单的登录逻辑。

       接下来,如果登录的用户名和密码验证正确的话,就会触发session会话,如下:

        // 此时用户属于未登录的状态,所以请求的cookie中没有sessionid,所以无法从服务器的hash表中获取到对应的session对象
        // 所以此处吧getSession中的参数设置为true,(在查询不到的时候创建新的sesion会话和session对象并存储到hash表中)
        // 同时会返回这个session对象,并且接下来会将新的sessionid通过响应http返回给客户端浏览器。
        HttpSession session = req.getSession(true);
 
        // 然后让刚刚创建好的session对象存储我们自定义的数据,就可以在这个对象中存储用户的身份信息
        session.setAttribute("username",username);

       session对象本身就可以看作一个hash表,通过setAttribute方法来存储键值对(setAttribute方法里面有两个参数,一个是String(key),一个是Object(value)),后续就可以通过getAttribute根据key来获取value。

       最后登录成功之后跳转到主页:

// 登录成功之后 需要自动跳转到 主页欢迎页
        resp.sendRedirect("index");

       下面是loginServlet的代码:

package Login;
 
import javax.jws.WebService;
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("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf8");
        resp.setContentType("text/html;charset=utf8");
        // 首先通过getParameter方法来获取对应key的value值
        String username = req.getParameter("name");
        String password = req.getParameter(("password"));
        // 然后来验证这个用户和密码是否正确
        if (username == null || password == null || username.equals("") || password.equals("")){
            resp.getWriter().write("当前用户名和密码不能为空");
            return;
        }
        // 假设此处的密码和用户名只能是zhangsan和1234
        // 逻辑上此处应该是在服务器的数据库里面去查找验证用户名和密码,此处简化步骤
        if (username.equals("zhangsan") || username.equals("lisi")) {
            if (password.equals("1234")) {
                // 此时用户输入密码正确
            } else {
                // 用户密码有误
                resp.getWriter().write();
            }
        } else {
            // 用户名错误
        }
        // 此时用户属于未登录的状态,所以请求的cookie中没有sessionid,所以无法从服务器的hash表中获取到对应的session对象
        // 所以此处吧getSession中的参数设置为true,(在查询不到的时候创建新的sesion会话和session对象并存储到hash表中)
        // 同时会返回这个session对象,并且接下来会将新的sessionid通过响应http返回给客户端浏览器。
        HttpSession session = req.getSession(true);
 
        // 然后让刚刚创建好的session对象存储我们自定义的数据,就可以在这个对象中存储用户的身份信息
        session.setAttribute("username",username);
 
        // 登录成功之后 需要自动跳转到 主页欢迎页
        resp.sendRedirect("index");
    }
}

实现生成主页

        首先需要验证身份信息:

HttpSession session = req.getSession(false);
        if (session == null ) {
            // 当前是未登录状态
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }

       此处的查询到的sessiom对象应该是和刚才登录的session对象是同一个对象,因为是同一个sessionid。刚才登录成功,sessionid就会通过Set-Cookie返回给浏览器,浏览器下次访问IndexServlet的时候,就会带上这个同一个sessionid。

package Login;
 
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("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 此处禁止创建新的会话,如果没有找到,那么就认为此用户为未登录状态
        // 如果找到了才认为是登录状态
        HttpSession session = req.getSession(false);
        if (session == null ) {
            // 当前是未登录状态
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        String username = (String)session.getAttribute("username");
        // 在服务器这边维护了一个全局的hash表,key就是sessionid,value就是session对象
        if (username == null) {
            resp.setContentType("text/html;charset=utf8");
            resp.getWriter().write("当前用户未登录");
            return;
        }
        // 如果都ok就生成动态主页
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write("欢迎你!!"+ username);
    }
}

部署tomcat服务器

        选择edit configuration:

       添加smart tomcat选项:

        设置端口8080(默认),重新命名,然后启动:

访问登录页面

       访问: http//127.0.0.1:8080/login/login.html

       输入“zhangsan”,1234

        就会收到浏览器请求保存cookie的请求。点击明白即可,我们点击编辑可以查看到我们的用户名和密码都已经保存到本地了。

       下面使用费德勒来抓包看看我们信息传递的过程,如下:

       输入用户名和密码之后就会构造一个http请求访问login的页面,随后访问index页面

        抓包的结果可以看出来,第一次访问的时候是没有cookie的,后面setCookie之后就有了。

       第二次请求index的请求如下:

       可以看到最后的存在一个Cookie字段

         

       下次无论你怎么刷新,都是显示“欢迎你!!zhangsan” 。

问题

       上面的sessionid也不一定会一直存在,比如说服务器重新启动,session的hash表就会清除,此时如果再访问就可能出现sessionid无法查询到,此时还是未登录状态,需要重新登录。

       服务器默认保存会话是再内存中的,一但重启服务器会话服务就会销毁,但是smart tomcat为了方便程序员方便调试程序,会在停止服务器的时候,把会话持久化保存,下次启动的时候自动恢复到内存中,这个时候会话还是不会丢失的(这个时候真的有效,跟tomcat版本有关)。



目录
相关文章
|
29天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
67 2
|
1月前
|
Java 编译器
探索Java中的异常处理机制
【10月更文挑战第35天】在Java的世界中,异常是程序运行过程中不可避免的一部分。本文将通过通俗易懂的语言和生动的比喻,带你了解Java中的异常处理机制,包括异常的类型、如何捕获和处理异常,以及如何在代码中有效地利用异常处理来提升程序的健壮性。让我们一起走进Java的异常世界,学习如何优雅地面对和解决问题吧!
|
12天前
|
Java 程序员
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
|
13天前
|
Java 开发者 UED
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
37 2
|
13天前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
38 2
|
16天前
|
Java 程序员 UED
深入理解Java中的异常处理机制
本文旨在揭示Java异常处理的奥秘,从基础概念到高级应用,逐步引导读者掌握如何优雅地管理程序中的错误。我们将探讨异常类型、捕获流程,以及如何在代码中有效利用try-catch语句。通过实例分析,我们将展示异常处理在提升代码质量方面的关键作用。
27 3
|
16天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
17天前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
60 4
|
16天前
|
开发框架 安全 Java
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
34 2
|
21天前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
24 4