【JavaEE】深入理解Web会话机制:Cookie、Session与Header解析


Cookie/Session
回顾Cookie

HTTP 协议⾃⾝是属于 “⽆状态” 协议

“⽆状态” 的含义指的是:

默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信, 和下次通信之间没有直接的联系.

HTTP是没有记忆功能

现在请求和过一会儿请求,同样的请求参数,得到的结果是一样的(处理逻辑一样,而不是指数据)

但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的

例如登陆⽹站成功后, 第⼆次访问的时候服务器就能知道该请求是否是已经登陆过了.

Cookie是客户端机制,Session是服务器机制

在这里插入图片描述

上述图中的 “令牌” 通常就存储在 Cookie 字段中.

⽐如去医院挂号

  1. 看病之前先挂号. 挂号时候需要提供⾝份证号, 同时得到了⼀张 “就诊卡”, 这个就诊卡就相当于患者的 “令牌”.

  2. 后续去各个科室进⾏检查, 诊断, 开药等操作, 都不必再出⽰⾝份证了, 只要凭就诊卡即可识别出当前患者的⾝份.

  3. 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的⾝份和就诊卡的关联就销毁了. (类似于⽹站的注销操作)

  4. ⼜来看病, 可以办⼀张新的就诊卡, 此时就得到了⼀个新的 “令牌”

此时在服务器这边就需要记录"令牌"信息, 以及令牌对应的⽤⼾信息, 这个就是 Session 机制所做的⼯作.

理解Session

我们先来了解⼀下什么是会话.

会话: 对话的意思

在这里插入图片描述

在计算机领域, 会话是⼀个客⼾与服务器之间的不中断的请求响应. 对客⼾的每个请求,服务器能够识别出请求来⾃于同⼀个客⼾. 当⼀个未知的客⼾向Web应⽤程序发送第⼀个请求时就开始了⼀个会话.当客⼾明确结束会话或服务器在⼀个时限内没有接受到客⼾的任何请求时,会话就结束了.

⽐如我们打客服电话

每次打客服电话, 是⼀个会话. 挂断电话, 会话就结束了

下次再打客服电话, ⼜是⼀个新的会话

如果我们⻓时间不说话, 没有新的请求, 会话也会结束

服务器同⼀时刻收到的请求是很多的. 服务器需要清楚的区分每个请求是从属于哪个⽤⼾, 也就是属于哪个会话, 就需要在服务器这边记录每个会话以及与⽤⼾的信息的对应关系.

Session是服务器为了保存⽤⼾信息⽽创建的⼀个特殊的对象.

在这里插入图片描述

Session的本质就是⼀个 “哈希表”, 存储了⼀些键值对结构. Key 就是SessionID, Value 就是⽤⼾信息(⽤⼾信息可以根据需求灵活设计).

在这里插入图片描述

SessionId 是由服务器⽣成的⼀个 “唯⼀性字符串”, 从 Session 机制的⻆度来看, 这个唯⼀性字符串称为 “SessionId”. 但是站在整个登录流程中看待, 也可以把这个唯⼀性字符串称为 “token”.

上述例⼦中的令牌ID, 就可以看做是SessionId, 只不过令牌除了ID之外, 还会带⼀些其他信息, ⽐如时间, 签名等.

在这里插入图片描述

  1. 当⽤⼾登陆的时候, 服务器在 Session 中新增⼀个新记录, 并把 sessionId 返回给客⼾端. (通过 HTTP 响应中的 Set-Cookie 字段返回).

  2. 客⼾端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的 Cookie 字段带上).

  3. 服务器收到请求之后, 根据请求中的 sessionId 在 Session 信息中获取到对应的⽤⼾信息, 再进⾏后续操作.找不到则重新创建 Session, 并把SessionID 返回

在这里插入图片描述

Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.

在这里插入图片描述

这是Session存在的问题,解决方式是通过令牌的方式

Cookie 和 Session 的区别

两个均是会话机制

会话:对话

  • Cookie 是客⼾端保存⽤⼾信息的⼀种机制. Session 是服务器端保存⽤⼾信息的⼀种机制.

Cookie是客户端机制,Session是服务器机制

  • Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session 之间的桥梁

  • Cookie 和 Session 经常会在⼀起配合使⽤. 但不是必须配合.

    • 完全可以⽤ Cookie 来保存⼀些数据在客⼾端. 这些数据不⼀定是⽤⼾⾝份信息, 也不⼀定是 SessionId

    • Session 中的 sessionId 也不需要⾮得通过 Cookie/Set-Cookie 传递, ⽐如通过URL传递

获取Cookie

Spring MVC是基于 Servlet API 构建的原始 Web 框架, 也是在Servlet的基础上实现的HttpServletRequest , HttpServletResponse 是Servlet提供的两个类, 是Spring MVC⽅法的内置对象. 需要时直接在⽅法中添加声明即可.

HttpServletRequest 对象代表客⼾端的请求, 当客⼾端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的⽅法,可以获得客⼾端请求的所有信息.

HttpServletResponse 对象代表服务器的响应. HTTP响应的信息都在这个对象中, ⽐如向客⼾端发送的数据, 响应头, 状态码等. 通过这个对象提供的⽅法, 可以获得服务器响应的所有内容

Spring MVC在这两个对象的基础上进⾏了封装, 给我们提供更加简单的使⽤⽅法.

@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request, HttpServletResponse response){
    //这两个参数是Spring的内置对象,需要使用时,直接在方法声明即可
    Cookie[] cookies = request.getCookies();
    //for (Cookie cookie : cookies) {
    //    System.out.println(cookie.getName()+":"+cookie.getValue());
    //}
    Arrays.stream(cookies).forEach(cookie->{
        System.out.println(cookie.getName()+":"+cookie.getValue());
    });
    return "获取cookie成功";
}

此时在浏览器输入http://127.0.0.1:8080/param/getCookie后,在idea控制台就会显示

java.lang.NullPointerException: null
	at java.util.Arrays.stream(Arrays.java:5004) ~[na:1.8.0_271]
	at com.example.demo.controller.ParamController.getCookie(ParamController.java:85) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_271]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_271]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_271]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_271]

修正后

@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request, HttpServletResponse response){
    //这两个参数是Spring的内置对象,需要使用时,直接在方法声明即可
    Cookie[] cookies = request.getCookies();
    //for (Cookie cookie : cookies) {
    //    System.out.println(cookie.getName()+":"+cookie.getValue());
    //}
    if(cookies!=null) {
        Arrays.stream(cookies).forEach(cookie -> {
            System.out.println(cookie.getName() + ":" + cookie.getValue());
        });
    }
    return "获取cookie成功";
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

以上是传统获取cookie的方式

简洁获取cookie的方式

@CookieValue

@RequestMapping("/getCookie2")
public String getCookie(@CookieValue String bite){
    return "cookie中存储的值bite:"+bite;
}

在这里插入图片描述

因为Cookie是客户端机制,与浏览器有关,因此重启服务器后Cookie仍然存在,但以上通过注解只能拿到一个cookie

获取session

Session 存储和获取

Session是服务器端的机制, 我们需要先存储, 才能再获取

Session 也是基于 HttpServletRequest 来存储和获取的

Session存储

因为Session是服务器的,无法伪造

@RequestMapping("/setSession")
public String setSession(HttpServletRequest request){
    HttpSession session=request.getSession();
    session.setAttribute("username","zhangsan");
    return "success";
}

这个代码中看不到 SessionId 这样的概念的. getSession 操作内部提取到请求中的 Cookie ⾥的 SessionId, 然后根据 SessionId 获取到对应的 Session 对象, Session 对象⽤ HttpSession来描述

在这里插入图片描述

HttpSession getSession(boolean create) : 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null

HttpSession getSession(): 和getSession(true) 含义⼀样, 默认值为true.

void setAttribute(String name, Object value): 使⽤指定的名称绑定⼀个对象到该 session 会话

Session读取

读取 Session 可以使⽤ HttpServletRequest

@RequestMapping("/getSession")
public String getSession(HttpServletRequest request){
    HttpSession session=request.getSession(false);//true的话不存在session就创建一个,false不存在就不创建
                                                    //默认值为true
    //根据sessionId获取session对象,类似根据学号找到整个学生
    if(session!=null){
        String username= (String) session.getAttribute("username");
        return "登录用户:"+username;
    }
    return "session 为空";
}

Object getAttribute(String name): 返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null

在这里插入图片描述

在这里插入图片描述

setSession通过fiddler观察

在这里插入图片描述

setSession通过浏览器观察
在这里插入图片描述

getSession通过fiddler观察

在这里插入图片描述

可以看到, Http请求时, 把SessionId通过Cookie传递到了服务器.

getSession通过浏览器观察

在这里插入图片描述

简洁获取 Session (1)

@SessionAttribute

@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute String username){
    return "username:"+username;
}

重启服务器后要先启动setSession因为这是服务器机制的,重启服务器后就清空了,默认是存在内存中的,重启后就清空了

使用这种方式的话,Session不存在,浏览器就会报错,因为@SessionAttribute底层要求这个session是必传的,不传就报错了

@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute(required = false) String username){
 	return "username:"+username;
}

或者改成这样就不会报错,只会在浏览器中显示null

在这里插入图片描述

在使用setSession后同样可以通过getSession2获取到session

在这里插入图片描述

简洁获取 Session (2)

通过Spring MVC内置对象 HttpSession 来获取

@RequestMapping("/getSession3")
public String getSession3(HttpSession session){
    String username= (String) session.getAttribute("username");
    return "登录用户:"+username;
}

HttpSession session = request.getSession();

Session 不存在的话, 会⾃动进⾏创建

HttpSession session这个参数等同于HttpSession session=request.getSession(true)

在使用setSession后同样可以通过getSession3获取到session

在这里插入图片描述

获取Header

传统获取 header

获取Header也是从 HttpServletRequest 中获取

@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request){
    String userAgent = request.getHeader("User-Agent");
    return "userAgent:"+userAgent;
}

使⽤ HttpServletRequest 提供的 getHeader ⽅法来获取, 参数对应HTTP请求报头的"Key"

通过fiddler观察

在这里插入图片描述

通过浏览器观察

在这里插入图片描述

简洁获取 Header

@RequestHeader

@RequestMapping("/getHeader2")
public String getHeader2(@RequestHeader("User-Agent")String userAgent){
    return "userAgent:"+userAgent;
}

@RequestHeader注解的参数值为HTTP请求报头中的"Key"

@RequestHeader("User-Agent")这部分的内容要求和上面fiddler观察力的User-Agent保持一样才对,后面参数改名为别的都可以

在这里插入图片描述

  • 27
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值