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



目录
相关文章
|
9天前
|
设计模式 人工智能 安全
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)`。
73 32
AQS:Java 中悲观锁的底层实现机制
|
17天前
|
Java 区块链 网络架构
酷阿鲸森林农场:Java 区块链系统中的 P2P 区块同步与节点自动加入机制
本文介绍了基于 Java 的去中心化区块链电商系统设计与实现,重点探讨了 P2P 网络在酷阿鲸森林农场项目中的应用。通过节点自动发现、区块广播同步及链校验功能,系统实现了无需中心服务器的点对点网络架构。文章详细解析了核心代码逻辑,包括 P2P 服务端监听、客户端广播新区块及节点列表自动获取等环节,并提出了消息签名验证、WebSocket 替代 Socket 等优化方向。该系统不仅适用于农业电商,还可扩展至教育、物流等领域,构建可信数据链条。
|
2月前
|
缓存 Dubbo Java
理解的Java中SPI机制
本文深入解析了JDK提供的Java SPI(Service Provider Interface)机制,这是一种基于接口编程、策略模式与配置文件组合实现的动态加载机制,核心在于解耦。文章通过具体示例介绍了SPI的使用方法,包括定义接口、创建配置文件及加载实现类的过程,并分析了其原理与优缺点。SPI适用于框架扩展或替换场景,如JDBC驱动加载、SLF4J日志实现等,但存在加载效率低和线程安全问题。
理解的Java中SPI机制
|
1月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
75 1
|
2月前
|
缓存 运维 Java
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
96 4
|
4月前
|
存储 监控 Java
JAVA线程池有哪些队列? 以及它们的适用场景案例
不同的线程池队列有着各自的特点和适用场景,在实际使用线程池时,需要根据具体的业务需求、系统资源状况以及对任务执行顺序、响应时间等方面的要求,合理选择相应的队列来构建线程池,以实现高效的任务处理。
199 12
|
5月前
|
存储 前端开发 Java
【SpringMVC】——Cookie和Session机制
获取URL中参数@PathVarible,上传文件@RequestPart,HttpServerlet(getCookies()方法,getAttribute方法,setAttribute方法,)HttpSession(getAttribute方法),@SessionAttribute
127 11
|
5月前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
130 20
|
6月前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
447 6
|
5月前
|
开发框架 安全 Java
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
121 5

热门文章

最新文章