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版本有关)。



目录
相关文章
|
8月前
|
设计模式 人工智能 安全
AQS:Java 中悲观锁的底层实现机制
AQS(AbstractQueuedSynchronizer)是Java并发包中实现同步组件的基础工具,支持锁(如ReentrantLock、ReadWriteLock)和线程同步工具类(如CountDownLatch、Semaphore)等。Doug Lea设计AQS旨在抽象基础同步操作,简化同步组件构建。 使用AQS需实现`tryAcquire(int arg)`和`tryRelease(int arg)`方法以获取和释放资源,共享模式还需实现`tryAcquireShared(int arg)`和`tryReleaseShared(int arg)`。
456 32
AQS:Java 中悲观锁的底层实现机制
|
7月前
|
自然语言处理 前端开发 Java
JBoltAI 框架完整实操案例 在 Java 生态中快速构建大模型应用全流程实战指南
本案例基于JBoltAI框架,展示如何快速构建Java生态中的大模型应用——智能客服系统。系统面向电商平台,具备自动回答常见问题、意图识别、多轮对话理解及复杂问题转接人工等功能。采用Spring Boot+JBoltAI架构,集成向量数据库与大模型(如文心一言或通义千问)。内容涵盖需求分析、环境搭建、代码实现(知识库管理、核心服务、REST API)、前端界面开发及部署测试全流程,助你高效掌握大模型应用开发。
762 5
|
7月前
|
前端开发 JavaScript Java
Java 学习路线规划及项目案例中的技术栈应用解析
内容包括:**Java 17核心特性**(如sealed class、record)与模块化开发;Spring Boot 3 + Spring Cloud微服务架构,涉及响应式编程(WebFlux)、多数据库持久化(JPA、R2DBC、MongoDB);云原生技术**如Docker、Kubernetes及CI/CD流程;性能优化(GraalVM Native Image、JVM调优);以及前后端分离开发(Vue 3、Spring Boot集成)。通过全栈电商平台项目实战,掌握从后端服务(用户、商品、订单)到前端应用(Vue 3、React Native)的全流程开发。
333 9
|
8月前
|
人工智能 Java 关系型数据库
Java——SPI机制详解
SPI(Service Provider Interface)是JDK内置的服务提供发现机制,主要用于框架扩展和组件替换。通过在`META-INF/services/`目录下定义接口实现类文件,Java程序可利用`ServiceLoader`动态加载服务实现。SPI核心思想是解耦,允许不同厂商为同一接口提供多种实现,如`java.sql.Driver`的MySQL与PostgreSQL实现。然而,SPI存在缺陷:需遍历所有实现并实例化,可能造成资源浪费;获取实现类方式不够灵活;多线程使用时存在安全问题。尽管如此,SPI仍是Java生态系统中实现插件化和模块化设计的重要工具。
359 0
|
6月前
|
人工智能 前端开发 安全
Java开发不可不知的秘密:类加载器实现机制
类加载器是Java中负责动态加载类到JVM的组件,理解其工作原理对开发复杂应用至关重要。本文详解类加载过程、双亲委派模型及常见类加载器,并介绍自定义类加载器的实现与应用场景。
297 4
|
7月前
|
人工智能 Java 开发者
【Java实例-简易计算机】使用Java实现简单的计算机案例
一个简单的Java案例——“简易计算器”,帮助编程新手快速上手。通过实现用户输入、基本逻辑运算和结果输出,学习者可以掌握变量声明、Scanner对象使用、控制流语句等关键知识点。文章分为设计思路、关键知识点、完整代码和测试运行四个部分。
237 9
【Java实例-简易计算机】使用Java实现简单的计算机案例
|
6月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
311 0
|
6月前
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
562 0
|
7月前
|
缓存 算法 NoSQL
校招 Java 面试高频常见知识点深度解析与实战案例详细分享
《2025校招Java面试核心指南》总结了Java技术栈的最新考点,涵盖基础语法、并发编程和云原生技术三大维度: 现代Java特性:重点解析Java 17密封类、Record类型及响应式Stream API,通过电商案例演示函数式数据处理 并发革命:对比传统线程池与Java 21虚拟线程,详解Reactor模式在秒杀系统中的应用及背压机制 云原生实践:提供Spring Boot容器化部署方案,分析Spring WebFlux响应式编程和Redis Cluster缓存策略。
211 0
|
7月前
|
人工智能 Java API
Java 生态大模型应用开发全流程实战案例与技术路径终极对决
在Java生态中开发大模型应用,Spring AI、LangChain4j和JBoltAI是三大主流框架。本文从架构设计、核心功能、开发体验、性能扩展性、生态社区等维度对比三者特点,并结合实例分析选型建议。Spring AI适合已有Spring技术栈团队,LangChain4j灵活性强适用于学术研究,JBoltAI提供开箱即用的企业级解决方案,助力传统系统快速AI化改造。开发者可根据业务场景和技术背景选择最适合的框架。
1576 2