客户端会话技术:Cookie
服务器端会话技术:Session
状态的含义:
客户端域服务器在某次会话中产生的数据,这些数据存在于缓存区中,缓存区中存储、记忆、共享一些临时数据,从而无状态就意味着,这些数据不会被保留。
无状态的官方解释:
- 协议对于事务处理没有记忆能力
- 对同一个url请求没有上下文关系
- 每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
- 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。
但是,通过增加cookie和session机制,现在的网络请求其实是有状态的。
在没有状态的http协议下,服务器也一定会保留你每次网络请求对数据的修改,但这跟保留每次访问的数据是不一样的,保留的只是会话产生的结果,而没有保留会话。
举例思考:
假如没有cookie没有session,http无状态的时候,当一个用户访问网站的时候,会有下面的问题:
你每访问一次需要权限的内容都需要在客户端输入用户名和密码。
你的每一次操作都要与系统底层的数据库进行交互(多次少量的访问存在非常大的性能浪费。非常容易就能想到肯定是一次大量的操作更有效率,于是就想到了缓存区)
你的非重要琐碎数据也被写进数据库中,跟你的主要数据放在一起(一次次添加和删除购物车只是跟你这次浏览,或者叫这次会话有关,是临时的数据,跟用户的主要信息无关,它们没什么价值,纯粹的冗余数据,用什么存放这些临时的数据,我们也很容易想到缓存区。)
上面就是无状态时候,会出现的问题,即使有连接,也无法解决【每一次操作都要与系统底层的数据交互】的问题,要解决【每一次操作都要与系统底层的数据库进行交互】就必须在服务端开辟一块缓存区。
客户端会话技术,服务端给客户端的数据,存储于客户端(浏览器)。由于是保存在客户端上的,所以存在安全问题,并且cookie是由个数和大小限制的(4KB),所以一般cookie用来存储一些比较小且安全性要求不高的数据,而且一般数据都会进行加密。
我们平时在登录某些网站时,关闭浏览器后再次打开登录,用户名密码等数据会自动填充在表单。
或者我们浏览淘宝的某个商品后,下次再打开发现出现的商品很多都是我们之前浏览的同类商品等。
这些都是cookie的应用场景。
使用步骤:
创建Cookie对象,绑定数据
new Cookie(String name, String value)
发送Cookie对象
response.addCookie(Cookie cookie)
获取Cookie,拿到数据
Cookie[] request.getCookies()
基于响应头set-cookie和请求头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可以共享
cookie存储数据在客户端浏览器
浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
作用:
cookie一般用于存出少量的不太敏感的数据
在不登录的情况下,完成服务器对客户端的身份识别
Cookie也是一个类,我们需要关注一下它什么时候生成什么时候消亡。这样我们才能更好的确定何时获取Cookie
Cookie的出生
当执行完这句代码的时候就代表这个Cookie诞生
Cookie cookie = new Cookie(String name,String value);
Cookie的消亡
默认情况下,在你关闭客户端后Cookie就会消失。此时你去获取cookie会返回null
如果设置了有效时间后则需要在有效时间到期后才会消亡。
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"; } }
服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。HttpSession
HttpSession session = request.getSession();
Object getAttribute(String name)
void setAttribute(String name, Object value)
void removeAttribute(String name)
Session的实现是依赖于Cookie的。
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>
- session用于存储一次会话的多次请求的数据,存在服务器端
- session可以存储任意类型,任意大小的数据
- session存储数据在服务器端,Cookie在客户端
- session没有数据大小限制,Cookie有
- session数据安全,Cookie相对于不安全