❤️java之Cookie和Session

小明的学习圈子2023-12-09后端

一、会话

1、会话:一次会话中包含多次请求和响应。

  • 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止

2、功能:

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

3、方式:

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

4、http协议无状态

状态的含义:

客户端域服务器在某次会话中产生的数据,这些数据存在于缓存区中,缓存区中存储、记忆、共享一些临时数据,从而无状态就意味着,这些数据不会被保留。

无状态的官方解释:

  1. 协议对于事务处理没有记忆能力
  2. 对同一个url请求没有上下文关系
  3. 每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
  4. 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。

但是,通过增加cookie和session机制,现在的网络请求其实是有状态的。 在没有状态的http协议下,服务器也一定会保留你每次网络请求对数据的修改,但这跟保留每次访问的数据是不一样的,保留的只是会话产生的结果,而没有保留会话。

举例思考:

假如没有cookie没有session,http无状态的时候,当一个用户访问网站的时候,会有下面的问题: 你每访问一次需要权限的内容都需要在客户端输入用户名和密码。 你的每一次操作都要与系统底层的数据库进行交互(多次少量的访问存在非常大的性能浪费。非常容易就能想到肯定是一次大量的操作更有效率,于是就想到了缓存区) 你的非重要琐碎数据也被写进数据库中,跟你的主要数据放在一起(一次次添加和删除购物车只是跟你这次浏览,或者叫这次会话有关,是临时的数据,跟用户的主要信息无关,它们没什么价值,纯粹的冗余数据,用什么存放这些临时的数据,我们也很容易想到缓存区。) 上面就是无状态时候,会出现的问题,即使有连接,也无法解决【每一次操作都要与系统底层的数据交互】的问题,要解决【每一次操作都要与系统底层的数据库进行交互】就必须在服务端开辟一块缓存区。

二、cookie

1、概念:

客户端会话技术,服务端给客户端的数据,存储于客户端(浏览器)。由于是保存在客户端上的,所以存在安全问题,并且cookie是由个数和大小限制的(4KB),所以一般cookie用来存储一些比较小且安全性要求不高的数据,而且一般数据都会进行加密。

我们平时在登录某些网站时,关闭浏览器后再次打开登录,用户名密码等数据会自动填充在表单。 或者我们浏览淘宝的某个商品后,下次再打开发现出现的商品很多都是我们之前浏览的同类商品等。 这些都是cookie的应用场景。

2、快速入门

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

3、实现原理

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

4、cookie的细节

1.一次可不可以发送多个cookie?

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

2.cookie在浏览器中保存多长时间?

默认情况下在浏览器关闭后就会失效。即一次会话后就失效。因为cookie是放在浏览器缓存的,浏览器关闭会清除缓存所以cookie会失效。

要想使这个cookie在浏览器关闭后仍然有效就需要设置有效时间将其写到磁盘下。

持久化存储:

setMaxAge(int seconds)     正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效     负数:默认值     零:删除cookie信息 3.cookie能不能存中文?

  1. 在tomcat 8 之前 cookie中不能直接存储中文数据。

需要将中文数据转码---一般采用URL编码(%E3)

  1. 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析

4.cookie共享问题?

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

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

4、2不同的tomcat服务器间cookie共享问题?

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

5、Cookie的特点和作用

cookie存储数据在客户端浏览器 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)

作用:

cookie一般用于存出少量的不太敏感的数据 在不登录的情况下,完成服务器对客户端的身份识别

6、Cookie的生命周期

Cookie也是一个类,我们需要关注一下它什么时候生成什么时候消亡。这样我们才能更好的确定何时获取Cookie Cookie的出生

当执行完这句代码的时候就代表这个Cookie诞生 Cookie cookie = new Cookie(String name,String value);

Cookie的消亡

默认情况下,在你关闭客户端后Cookie就会消失。此时你去获取cookie会返回null 如果设置了有效时间后则需要在有效时间到期后才会消亡。

7、代码示例

package com.stuwork.crowdfunding.controller;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.stuwork.crowdfunding.bean.Member;
import com.stuwork.crowdfunding.bean.Permission;
import com.stuwork.crowdfunding.bean.User;
import com.stuwork.crowdfunding.manager.service.UserService;
import com.stuwork.crowdfunding.potal.service.MemberService;
import com.stuwork.crowdfunding.util.AjaxResult;
import com.stuwork.crowdfunding.util.ConstUtil;
import com.stuwork.crowdfunding.util.MD5Util;

@Controller
public class DispatcherController {

    @Autowired
    private UserService userService;
    
    @Autowired
    private MemberService memberService;
    //跳转到web-inf的index页面。
    @RequestMapping("/index")
    public String index (){
        return "index";
    }
    //跳转到登录页面
    @RequestMapping("/login")
    public String login(HttpServletRequest request,HttpSession session){
        //判断是否需要自动登录
        //如果之前登录过,cookie中存放了用户信息,需要获取cookie中的信息,并进行数据库的验证
        
        boolean needLogin = true;
        String logintype = null ;
        Cookie[] cookies = request.getCookies();
        if(cookies != null){ //如果客户端禁用了Cookie,那么无法获取Cookie信息
            
            for (Cookie cookie : cookies) {
                if("logincode".equals(cookie.getName())){
                    String logincode = cookie.getValue();
                    System.out.println("获取到Cookie中的键值对"+cookie.getName()+"===== " + logincode);
                    //loginacct=admin&userpwd=21232f297a57a5a743894a0e4a801fc3&logintype=1
                    String[] split = logincode.split("&");
                    if(split.length == 3){
                        String loginacct = split[0].split("=")[1];
                        String userpwd = split[1].split("=")[1];
                        logintype = split[2].split("=")[1];
                        Map<String, String> mapParam = new HashMap<String, String>();
                        mapParam.put("loginacct", loginacct);
                        mapParam.put("userpswd", userpwd);
                        mapParam.put("type", logintype);
                        if("user".equals(logintype)){
                            
                            User dbLogin = userService.getUserInfo(mapParam);
                            
                            if(dbLogin!=null){
                                session.setAttribute(ConstUtil.LOGIN_USER, dbLogin);
                                needLogin = false ;
                            }
                            //----------------------------------
                            List<Permission> permissionList = userService.getUserRolePermissionByUserId(dbLogin.getId());
                            Permission permissionRoot = null;
                            Set<String> uris = new HashSet<String>();
                            Map<Integer,Permission> map = new HashMap<Integer,Permission>();
                            for(Permission bean :permissionList){
                                map.put(bean.getId(), bean);
                                uris.add("/"+bean.getUrl());
                            }
                            for(Permission bean :permissionList){
                                Permission children = bean;
                                if(bean.getPid() == 0){
                                    permissionRoot = bean;
                                }else{
                                    Permission parent = map.get(children.getPid());
                                    parent.getChildren().add(children);     
                                }
                            }
                            session.setAttribute(ConstUtil.PERMISSION_ROOT, permissionRoot);
                            session.setAttribute(ConstUtil.URIS, uris);
                            //----------------------------------
                        }else if("member".equals(logintype)){
                            Member dbLogin = memberService.getMemberInfo(mapParam);
                            
                            if(dbLogin!=null){
                                session.setAttribute(ConstUtil.LOGIN_MEMBER, dbLogin);
                                needLogin = false ;
                            }
                        }
                        
                    }
                }
            }
        }
        
        if(needLogin){
            return "login";
        }else{
            if("user".equals(logintype)){
                return "redirect:/main.htm";
            }else if("member".equals(logintype)){
                return "redirect:/member.htm";
            }
       }
       return "login";
    }
    //@RequestParam里的value必须是前端页面name熟悉的值,login可以接收到值(和name属性值一样也可以),否则接收不到
    //同步请求
    /*@RequestMapping("/doLogin")
    public String doLogin(@RequestParam(value="loginacct") String login,
           String userpswd,String type,HttpSession session){
        Map<String, String> map = new HashMap<String, String>();
        map.put("loginacct", login);
        map.put("userpswd", userpswd);
        map.put("type", type);
        
        User user = userService.getUserInfo(map);
        session.setAttribute(ConstUtil.LOGIN_USER, user);
        
        //重定向到主页面,不直接返回main,是防止刷新页面重复提交表单
        return "redirect:/main.htm";
        
    }*/
    //异步请求
    @ResponseBody
    @RequestMapping("/doLogin")
    public Object doLogin(@RequestParam(value="loginacct") String login,
                          String userpswd,
                          String type,
                          boolean rememberMe,
                          HttpSession session,
                          HttpServletResponse response){
        AjaxResult result = new AjaxResult();
        Map<String, String> mapParam = new HashMap<String, String>();
        mapParam.put("loginacct", login);
        mapParam.put("userpswd", MD5Util.digest(userpswd));
        mapParam.put("type", type);
        try {

            if("member".equals(type)){
                Member member = memberService.getMemberInfo(mapParam);
                session.setAttribute(ConstUtil.LOGIN_MEMBER, member);
                if(rememberMe){
                    String logincode = "\"loginacct="+member.getLoginacct()+"&userpwd="+member.getUserpswd()+"&logintype=member\"";
                    //loginacct=admin&userpwd=21232f297a57a5a743894a0e4a801fc3&logintype=1
                    System.out.println("用户-存放到Cookie中的键值对:logincode::::::::::::"+logincode);
                    Cookie c = new Cookie("logincode",logincode);
                    
                    c.setMaxAge(60*60*24*14); //2周时间Cookie过期     单位秒
                    c.setPath("/"); //表示任何请求路径都可以访问Cookie
                    
                    response.addCookie(c);
                }
            }else{
                User user = userService.getUserInfo(mapParam);
                session.setAttribute(ConstUtil.LOGIN_USER, user);
                if(rememberMe){
                    //服务器向客户端写Cookie时含有特殊符号,Tomcat7不需要增加双引号;Tomcat6需要增加双引号;
                    //否则将来服务器端可能读取不到Cookie值(logincode串)
                    String logincode = "\"loginacct="+user.getLoginacct()+"&userpwd="+
                                       user.getUserpswd()+"&logintype=user\"";
                    //loginacct=admin&userpwd=21232f297a57a5a743894a0e4a801fc3&logintype=1
                    System.out.println("用户-存放到Cookie中的键值对:logincode::::::::::::"+logincode);
                    Cookie c = new Cookie("logincode",logincode);
                    
                    c.setMaxAge(60*60*24*14); //2周时间Cookie过期     单位秒
                    c.setPath("/"); //表示任何请求路径都可以访问Cookie
                    
                    response.addCookie(c);
                }
                //----------------------------------
                List<Permission> permissionList = userService.getUserRolePermissionByUserId(user.getId());
                Permission permissionRoot = null;
                Set<String> uris = new HashSet<String>();
                Map<Integer,Permission> map = new HashMap<Integer,Permission>();
                for(Permission bean :permissionList){
                    map.put(bean.getId(), bean);
                    uris.add("/"+bean.getUrl());
                }
                for(Permission bean :permissionList){
                    Permission children = bean;
                    if(bean.getPid() == 0){
                        permissionRoot = bean;
                    }else{
                        Permission parent = map.get(children.getPid());
                        parent.getChildren().add(children);     
                    }
                }
                session.setAttribute(ConstUtil.PERMISSION_ROOT, permissionRoot);
                session.setAttribute(ConstUtil.URIS, uris);
                //----------------------------------
            }
            result.setData(type);
            result.setSuccess(true);
        } catch (Exception e) {
            
            e.printStackTrace();
            result.setMessage("登录失败!");
            result.setSuccess(false);
        }
                 
        return result;
        
    }
    //跳转到主页面
    @RequestMapping("/main")
    public String main(HttpSession session){

        return "main";
    }
    //跳转到主页面
    @RequestMapping("/member")
    public String member(HttpSession session){
        return "member/index";
    }
    //跳转到首页
    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();//销毁session对象
        return "redirect:/index.htm";
    }
}

三、session

1、概念

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

2、快速入门

1. 获取HttpSession对象

HttpSession session = request.getSession();

2. 使用HttpSession对象:

Object getAttribute(String name) void setAttribute(String name, Object value) void removeAttribute(String name)

3、 原理

  • Session的实现是依赖于Cookie的。
  • cookie中有JSESSIONID这个字段,实际上首次请求网页时在请求头里是没有这个字段的,因为我们并没有创建session,当我们调用request.getSession()时,此时会创建一个session,并且将sessionId保存到cookie中,然后回写给response,所以我们发现首次创建session时的响应头中有JSESSIONID这个字段,后面的request默认都会带上JSESSIONID这个字段,而response中则不会再有该字段了。而服务器就能够根据JSESSIONID这个字段值查找对应的session。
  • 如果浏览器禁用了cookie,那么,每次请求都会重新创建session,因为服务器没有获取到JSESSIONID这个值,也无法根据JSESSIONID的值查找相应的session,也就是说,如果客户端禁用了cookie,那么,每次请求得到的sessionId是不一样的。

4、细节

1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
        * 默认情况下。不是。
        * 如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
             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() 。
        3. session默认失效时间 30分钟
            选择性配置修改    
            <session-config>
                <session-timeout>30</session-timeout>
            </session-config>

5、session的特点

  1. session用于存储一次会话的多次请求的数据,存在服务器端
  2. session可以存储任意类型,任意大小的数据

6、session的创建和关闭

  • Session创建:在你打开一个浏览器开始访问的时候,就创建了。
  • Session关闭:他在你关闭浏览器的时候或者默认时间(Tomcat是30分钟)后会销毁。

7、session与Cookie的区别:

  1. session存储数据在服务器端,Cookie在客户端
  2. session没有数据大小限制,Cookie有
  3. session数据安全,Cookie相对于不安全

img

Last Updated 2024/4/6 11:55:17