❤️java之Cookie和Session
一、会话
1、会话:一次会话中包含多次请求和响应。
- 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
2、功能:
- 在一次会话的范围内的多次请求间,共享数据
3、方式:
客户端会话技术:Cookie 服务器端会话技术:Session
4、http协议无状态
状态的含义:
客户端域服务器在某次会话中产生的数据,这些数据存在于缓存区中,缓存区中存储、记忆、共享一些临时数据,从而无状态就意味着,这些数据不会被保留。
无状态的官方解释:
- 协议对于事务处理没有记忆能力
- 对同一个url请求没有上下文关系
- 每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
- 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。
但是,通过增加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能不能存中文?
- 在tomcat 8 之前 cookie中不能直接存储中文数据。
需要将中文数据转码---一般采用URL编码(%E3)
- 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
4.cookie共享问题?
4、1假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
- 默认情况下cookie不能共享
- setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录
- 如果要共享,则可以将path设置为"/"
4、2不同的tomcat服务器间cookie共享问题?
- setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享
- 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的特点
- session用于存储一次会话的多次请求的数据,存在服务器端
- session可以存储任意类型,任意大小的数据
6、session的创建和关闭
- Session创建:在你打开一个浏览器开始访问的时候,就创建了。
- Session关闭:他在你关闭浏览器的时候或者默认时间(Tomcat是30分钟)后会销毁。
7、session与Cookie的区别:
- session存储数据在服务器端,Cookie在客户端
- session没有数据大小限制,Cookie有
- session数据安全,Cookie相对于不安全

