Cookie 和 Session

状态管理

现有问题

  • Http 协议是无状态的,不能保存每次提交的信息。
  • 如果用户发来一个新的请求,服务器知道它是否与上次的请求有联系。
  • 对于那些需要多次提交的数据才能完成的Web 操作,比如说登录来说,就成问题了。

状态管理概念:

​     将浏览器与web 服务器之间多次交互当做一个整体来处理,并且将多次交互所涉及的数据(即状态)保存下来。

    简单来说:就是 记录客户端和服务器之间多次交互的状态。

状态管理分类:

  • 客户端状态管理:将状态保存在客户端,代表性的是 Cookie 技术。
  • 服务器状态管理技术:将状态保存在服务器端,代表性的是 Session 技术(服务器传递 sessionID 时需要使用Cookie 的方式 和 application)

Cookie

  • Cookie 是在浏览器访问 web 服务器的某个资源时,由 web 服务器在 HTTP 响应头中附带传送给 浏览器的一小段数据。
  • 一旦 浏览器 保存了某个Cookie ,那么它 以后每次访问该 web 服务器时,都应该在 Http 请求头中将这个 Cookie 回传给服务器。
  • 一个 Cookie 主要有 该信息的名称 (name)和 值(value)组成。

    创建 Cookie ,然后响应的时候携带,让客户端保存,再次请求的时候,携带 Cookie 信息。让请求的 Servlet 获取。

// 创建 Cookie
Cookie cookie = new Cookie("code",code);
// 设置 Cookie 的路径(哪些可以访问)
cookie.setMaxAge(-1); // 取值有三种:>0 单位:秒; =0 浏览器关闭;  <0 内存存储,默认 -1 ;
// 将 Cookie 响应给客户端,可以是一个也可以是多个
response.addCookie(cookie);  // 添加到 response 对象中,响应时发送给客户端
// 通过 request 对象获取所有的 cookie
        Cookie[] cookies = req.getCookies();

        // 通过 循环遍历数组Cookie
/*        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+":"+cookie.getValue());

        }*/


        // 但是有可能传入 的cookie 是一个空,那么如何遍历呢,或者说如何避免空指针异常呢?
        // 简单:加个判断就好

        if (cookies != null){
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName() + ":" + cookie.getValue());
            }
        }

只需要保证 Cookie 的名称和路径是一致的。这时会认为修改操作。

// 创建 Cookie
Cookie cookie = new Cookie("code","code");
cookie.setPath("/webs");  // 必须是一致的,不一样就新建了个 cookie
cookie.setMaxAge(-1);
response.addCookie(cookie);

注意】:如果改变 name 和有效路径会新建 cookie;而改变 cookie 的 value 、有效期,会覆盖原有的 cookie。

很简单。。。

4、Cookie 的编码与解码#

​     Cookie 默认不支持中文,只能包含 ASCII 字符,所以 cookie 需要对 Unicode 字符进行编码,否则会出现乱码。

  • 编码使用 java.net.URLEncoder 类中的,encode(String str,String encoding) 方法。(对指定的字符串,用指定的编码格式编码)
  • 解码使用 java.net.URLDecoder 类的decode(String str,String encoding)方法。

创建带中文的 cookie(编码)#

Cookie cookie = new Cookie("姓名", "张三");  // cookie 不支持
cookie.setPath("/webproject/get");
cookie.setMaxAge(60 * 60);
resp.addCookie(cookie);

需要设置 编码格式

Cookie cookie = new Cookie(
        URLEncoder.encode("姓名","utf-8"),
        URLEncoder.encode("张三","utf-8"));
cookie.setPath("/webproject/get");
cookie.setMaxAge(60 * 60);
resp.addCookie(cookie);

    同时也需要设置,解码方式:(不然出现的是%E5%A7%93%E5%90%8D:%E5%BC%A0%E4%B8%89)

if (cookies != null) {
    for (Cookie cookie : cookies) {
        System.out.println(
                URLDecoder.decode(cookie.getName(), "utf-8")
                        + ":" + URLDecoder.decode(cookie.getValue(), "utf-8"));
    }
}

总结#

优点:

  1. 可配置到期规则
  2. 简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
  3. 数据持久性:cookie 默认在过期之前是可以一直存在客户端浏览器上。

缺点:

  1. 大小收到限制:大多数浏览器对cookie 的大小有 4k,到8k 字节的限制。
  2. 有些用户配置禁用,就会限制 cookie 功能
  3. 潜在安全风险,cookie 可能会被篡改,会对安全性造成风险或者导致依赖于cookie 的应用程序失败。

Session

概述#

  • Session 用于记录用户的状态,Session 指的是在一段时间内,单个客户端与 web 服务器的一连串相关的交互过程。
  • 在一个 Session 中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器资源。

Session 原理#

  • 服务器会为每一次会话分配一个 Session 对象。
  • 同一个浏览器发起的多次请求,同属于一次会话。
  • 首次使用到 Session 时,服务器会自动创建 Session,并创建 Cookie 存储 SessionID ,并且回传给客户端。

注意:Session 是服务端创建的。

首次请求:在响应中设置 cookie

未关闭浏览器,再次请求:请求中携带 Session

Session 的使用#

session 是服务器的一个 map 集合。

  • Session 作用域:拥有存储数据的空间,作用范围是一次会话有效。
    • 一次会话:是使用统一浏览器发送的多次请求,一旦浏览器关闭,则会话结束。
    • 可以将数据存入 Session 中,在一次会话的任意位置进行获取。
    • 可传递任何数据(基本数据类型,对象,集合,数组)

浏览器在没有关闭的情况下,访问同一个资源,拿到的是同一个 SessionID

1、获取 Session 对象#

// 获取 Session 对象
HttpSession session = request.getSession();
// 唯一标识
System.out.println("session :"+session.getId());

2、Session 保存数据#

setAttribute(属性名,Object)保存数据到 session 中

session.setAttribute("key",value); // 以键值对形式存储在 session 作用域中

3、Session 获取数据#

getAttribute(属性名); 获取 session 中的数据

session.setAttribute("key");  // 通过String 类型的key 访问Object 类型的 value

4、Session 移除数据#

removeAttribute(属性名); 从session 中移除数据

removeAttribute("key");  // 通过键移除session 作用域中的值

Session 和 Request 应用区别#

区别#

  • request 是一次请求有效,请求改变,则 request 改变。

  • session 是一次会话有效,只要浏览器不关闭,那么 Session 是不会改变的。

实例

@WebServlet(value = "/ss")
public class SessionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 获取 Session 对象
        HttpSession session = req.getSession();
        System.out.println("session :"+session.getId());
        session.setAttribute("name", "Asia");

        // 用 request 域传递数据
        req.setAttribute("password", "123");
        resp.sendRedirect("/webproject/getvalue");


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }



}

获取值的 Servlet

@WebServlet(value = "/getvalue")
public class GetValue extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 获取 Session 对象
        HttpSession session = req.getSession();

        // 获取 Session 数据
        String name = (String) session.getAttribute("name");

        System.out.println("name:" + name);

        // 获取 request 域中的 数据,这里因为采取的是定向的方式,所以是获取不到的
        String password = (String) req.getAttribute("password");
        System.out.println(password);


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

结果:(先访问 /ss,然后重定向到 /getvalue )以证明request 和 session 的区别。

session :F41D767BD803DDEBDE5E9AB2F6C9B819
name:Asia
null

Session 的生命周期#

  • 开始:第一次使用到Session 的请求产生,则创建 Session
  • 结束:
    • 浏览器关闭,失效
    • Session 超时,失效
      • session.setMaxInactiveInterval(60 * 60);
    • 手动销毁,失效
      • session.invalidate(); // 登录退出、注销
@WebServlet(value = "/gets")
public class getSessionLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 获取 Session 对象
        HttpSession session = req.getSession();

        // 设置 session 有效时间
      //  session.setMaxInactiveInterval(10);  // 以秒为单位


        System.out.println("第一次获取 sessionID:" + session.getId());

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}
@WebServlet(value = "/secget")
public class SecondGetLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 获取 Session 对象
        HttpSession session = req.getSession();


        System.out.println("第二次获取 sessionID :" + session.getId());  // 因为我是在一次会话请求的这个 Servlet,同时我还设置了 10 秒超时,所以 10 秒后,超时了,第二次获取到的 session  和第一次是不一样的。


        // 手动销毁失效    手动销毁后,在此获取到 session 是 新session
        session.invalidate();

        /*
        *   第一次获取 sessionID:969168F6CD8A47168209EB8D13E438E0
            第二次获取 sessionID :969168F6CD8A47168209EB8D13E438E0
            再次访问第一次获取的 servlet: sessionID:99C5CAFCE6BD0287F7CBC8B6621A92AB
        * */
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}

浏览器禁用 cookie 的后果

​     现在大家都很喜欢将 cookie 全部禁用了,防止在看学习资料的时候,被别人发现。

​     服务器默认情况下,会使用 cookie 的方式将 sessionID 发送给浏览器,我们禁止了,sessionID 就不会保存,那我们多次获取到的 sessionID 就是不同了,这就属于一次会话,多个 sessionID ,这当然是不行的了。

解决方法:URL 重写

​     浏览器在访问服务器上的某个地址时,不再使用原来的那个地址,而是使用经过改写的地址(即在原来的地址后面加上了 sessionID

实现:

response.encodeRedirectURL(String url) 生成重写的 URL

@WebServlet(value = "/gets")
public class getSessionLife extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        // 获取 Session 对象
        HttpSession session = req.getSession();

        // 设置 session 有效时间
      //  session.setMaxInactiveInterval(10);  // 以秒为单位


        // 生成 重写 URL
        String newURI = resp.encodeRedirectURL("/webproject/secget");


        System.out.println("第一次获取 sessionID:" + session.getId());

        System.out.println(newURI);
        resp.sendRedirect(newURI);


    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


}
/*

第一次获取 sessionID:62DFA4B0640D24471A0B00FAB4D3C948
/webproject/secget;jsessionid=62DFA4B0640D24471A0B00FAB4D3C948
第二次获取 sessionID :62DFA4B0640D24471A0B00FAB4D3C948


*/

​ 做到了 即使客户端禁用了 cookie ,我仍旧可以传 serssionID 来保证,两次请求都属于同一个 session 会话。

Session 应用场景#

1、保存用户登录信息#

写一个管理员的实体类,mapper,service,controller,挨个写,在 controller 中做判断。

if (mgr != null){
    // 登录成功

    // 将管理员信息存在 Session 里面 (这样我可以在任何位置,访问管理员的信息)
    // 有效期是一次会话
    HttpSession session = req.getSession();
    session.setAttribute("mgr", mgr);
    // 跳转,目标,方式(通过重定向的方式,跳转到 showallController)
    resp.sendRedirect("/webproject/showAllController");
} else {
    // 登录失败
    resp.sendRedirect("/webproject/loginMgr.html");
}

​     在 showAllController 中,也要做判断,如果登录过的话,可以直接访问,没有登录,重定向到登录页面,这样做是为了防止,用户直接访问 showAllController.

// 通过 HttpSession 完成权限的控制
HttpSession session = req.getSession();
Manager mgr = (Manager) session.getAttribute("mgr");

// 如果登录了,执行业务,没有登录,则登录失败,跳转到登录页面。 
if (mgr != null){

    System.out.println("进入 /showAllController  doGet");


    AdminService adminService = new AdminServiceImpl();
    List<User> userList = adminService.showAllUser();

    // request 作用域存数据
    req.setAttribute("user", userList);
    // 通过转发,跳转到显示结果的 servlet
    req.getRequestDispatcher("/showAlljsp").forward(req, resp);
} else{
    // 登录失败,转到登录页面登录
    resp.sendRedirect("/webproject/loginMgr.html");
}

​     同时因为是一次会话,所以我们可以在任意位置,获取,session 的信息,session 存的是管理员信息,所以可以做到,显示 welcome ,管理员! 这样的效果。

2、保存验证码#

  • 导入 validateCode.jar
  • 创建生成验证码 的 servlet

思路:

​     创建生成验证码的 servlet ,创建验证码图片和响应给客户端,这时候,在 logMgr.html 页面已经有验证码了,接下来,我们就需要在 loginMrgController 里面首先收参,然后判断验证码,是否正确,接着再进行后续业务的比较。

​ 【注意】:创建 验证码 生成的 servlet 的时候,将 验证码获取,然后存在 session 里面,然后在 loginMgrController 里面,获取session 中存的 验证码,然后进行比较。

login.html

<form action="/webproject/loginMgr" method="post">
    用户名:<input type="text" name="username"><br>
    密 码:<input type="password" name="password"><br>
    验证码:<input type="text" name="inputVcode"/><img src="/webproject/createCode"><br> <!--这里已经对createCode 做了一次请求,响应。再次点击登录,那就是两次请求,所以,不能用request 存验证码数据-->
    <input type="submit" value="登录">


</form>

验证码生成 servlet

@WebServlet(value = "/createCode")
public class CreateCodeController extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 创建验证码图片

        ValidateCode vc = new ValidateCode(150, 30, 5, 10);

        // 2. 验证码图片响应给客户端
        vc.write(resp.getOutputStream());

        // 3. 创建session  保存 验证码
        // 获取验证码生成 的5个字符  在 ValidateCode中 有个 getcode方法
        String codes = vc.getCode();

        HttpSession session = req.getSession();
        session.setAttribute("code",codes);

    }

loginMgrController servlet

@WebServlet(value = "/loginMgr")
public class LoginMgrController extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 1. 处理乱码

        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        // 2. 收参
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String inputVcode = req.getParameter("inputVcode");


        // 首先判断,验证码,是否正确,然后,接着判断业务逻辑

        // 从session 中 获取随机生成的验证码,然后我们在调用业务方法,进行后续的比较
        String code = (String) req.getSession().getAttribute("code");
        if (inputVcode != null && inputVcode.equalsIgnoreCase(code)) {

            // 3. 调用业务方法
            ManagerServiceImpl managerService = new ManagerServiceImpl();
            Manager mgr = managerService.login(username, password);


            // 4. 处理结果,流程跳转

            if (mgr != null) {
                // 登录成功

                // 将管理员信息存在 Session 里面 (这样我可以在任何位置,访问管理员的信息)
                // 有效期是一次会话
                HttpSession session = req.getSession();
                session.setAttribute("mgr", mgr);
                // 跳转,目标,方式(通过重定向的方式,跳转到 showallController)
                resp.sendRedirect("/webproject/showAllController");
            } else {
                // 登录失败
                resp.sendRedirect("/webproject/loginMgr.html");
            }
        }else {
            resp.sendRedirect("/webproject/loginMgr.html");
        }

    }
posted @   走马!  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示
主题色彩