会话跟踪
会话跟踪是Web程序中用于跟踪用户整个会话的技术,常用的会话跟踪技术是Cookie和Session。Cookie通过在客户端记录确定用户身份信息,Session通过在服务器端记录确定用户身份信息
Cookie
简介
由于HTTP协议是一个无状态的协议,服务器无法知道是哪个客户端(浏览器)访问了它,因此需要一个表示用于让服务器区分不同的客户端。Cookie就是管理服务器和客户端之间状态的标识,用于弥补HTTP协议无状态的不足
- Cookie功能需要浏览器支持,如果浏览器不支持Cookie或者禁用了Cookie,则Cookie功能失效
- 不同的浏览器采用不同的方式保存Cookie
- Cookie有时也用其复数形式Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息
原理
Cookie原理即客户端第一次先服务器发送请求时,服务器在response头部设置Set-Cookie字段,客户端受到响应手就会设置Cookie并存储,在下一次该客户端向服务器发送请求时,就会在request头部自动带上Cookie字段,服务器收到Cookie用于区分不同客户端。为了Cookie与某个用户有对应关系,应该在第一次访问时就存在与服务器,此时需要Session
案例
创建Cookie
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
* 创建Cookie
* 1. name:Cookie的名称。Cookie一旦创建,名称便不可更改
* 2. value:Cookie的值。如果值为Unicode字符(中文),需要为字符编码。如果值为二进制数据,则需要使用BASE64编码
* Cookie cookie = new Cookie("wen", URLEncoder.encode("文", StandardCharsets.UTF_8.name()));
*/
Cookie cookie = new Cookie("account", req.getParameter("account"));
/*
* Cookie失效的时间,单位秒,默认为–1
* 如果为正数,则该Cookie在maxAge秒之后失效
* 如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie
* 如果为0,表示删除该Cookie
*/
cookie.setMaxAge(60 * 60);
//Cookie是否仅被使用安全协议传输。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false
cookie.setSecure(true);
//Cookie是不可跨域名的,正常情况下,同一个一级域名下的两个二级域名也不能交互使用Cookie,如 map.baidu.com 和 www.baidu.com
//如果想所有baidu.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数
cookie.setDomain(".baidu.com");
//决定允许访问Cookie的路径(ContextPath),如只允许/session/下的程序使用Cookie,但/session/test/a.jsp获取不到/session/source/b.jsp的Cookie
//设置为"/"时允许所有路径使用Cookie。path属性需要使用符号"/"结尾。name相同但domain相同的两个Cookie也是两个不同的Cookie
cookie.setPath("/session/");
//输出到客户端
resp.addCookie(cookie);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
请一次请求无对应的Cookie,请求完后返回Set-Cookie
第二次请求带上Cookie
获取Cookie
@WebServlet("/login2")
public class LoginServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request对象获取Cookies数组
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
System.out.println("name: "+cookie.getName()+",value: "+ URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name()));
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
输出
缺陷
- 数量和大小受限:客户端能创建Cookie数量最多300个,并且每个大小不能超过4kb;每个Web站点能设置Cookie总数不超过20个
- 安全问题:存在跨站点脚本攻击的可能,在受到跨站点脚本攻击时,脚本指令将会读取当前站点的所有Cookie 内容(已不存在 Cookie 作用域限制),然后通过某种方式将 Cookie 内容提交到指定的服务器(如:AJAX)。一旦 Cookie 落入攻击者手中,它将会重现其价值
- 客户端禁用Cookie:禁用Cookie后,则Cookie功能失效
应用场景
主要的应用场景是用作客户端和服务器之间的会话状态保持
Session
简介
Session(会话)是浏览器第一次访问服务端,服务端就会创建一次会话,在会话中存储特定用户会话所需的属性及配置信息。它与Cookie的区别就是Session是缓存在服务端的,Cookie则是缓存在客户端,它们都由服务端生成,为了弥补Http协议无状态的缺陷
- 如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了
原理
Session原理是,当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。服务器会向客户浏览器发送一个每个用户特有的会话编号sessionID,放到Cookie里。服务器同时也把sessionID和对应的用户信息、用户操作记录在服务器上,这些记录就是Session。客户端再次访问时会带入到Cookie发送给服务器,其中就包含sessionID。服务器从Cookie里找到sessionID,再根据sessionID找到以前记录的用户信息就可以知道客户端之前操控、访问过哪里
案例
创建session
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private static final Map<String,String> DATA = new HashMap<String,String>(){
{
put("yi","yi");
put("wen","wen");
}
};
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-type", "text/html;charset=UTF-8");
String account = req.getParameter("account");
if (DATA.containsKey(account)) {
//获取Session对象
HttpSession session = req.getSession();
//设置Session中的属性
session.setAttribute("account",account);
resp.getWriter().write("用户:"+ account +"登录成功");
} else {
resp.getWriter().write("登录失败");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
第一次访问服务器,服务器创建一个session的JSESSIONID返回
第二次访问服务器,客户端携带JSESSIONID访问
获取session
@WebServlet("/login2")
public class LoginServlet2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
System.out.println("name: "+cookie.getName()+",value: "+ URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name()));
}
//获取Session对象
HttpSession session = req.getSession();
//获取Session中的属性
System.out.println(session.getAttribute("account"));
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
输出
缺陷
Session存放到服务器内存中,如果大量请求,那么服务器存放Session内存增大,影响服务器性能
应用场景
主要的应用场景是用作客户端和服务器之间的会话状态保持
Session-Cookie认证
实现思路
-
服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(可以将seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后在响应头中放下这个唯一标识字符串
-
签名。这一步通过秘钥对sid进行签名处理,避免客户端修改sid(非必需步骤)
-
浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次HTTP请求的请求头中会带上该域名下的cookie信息
-
服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法
优缺点
Session-Cookie 认证机制在基本上所有的网页浏览器上都能够支持,而且实现方式也很简单
- 当存在多台服务器时会出现session同步问题
- 很容易遭受到Cookie欺骗和CRFS攻击
- 服务端存储压力,当很多的session存储到服务端时,会对服务器的存储造成压力
- 跨域问题,Cookie是属于同源策略限制的条件之一
登录Servlet
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
private static final Map<String,String> DATA = new HashMap<String,String>(){
{
put("yi","yi");
put("wen","wen");
}
};
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-type", "text/html;charset=UTF-8");
String account = req.getParameter("account");
String password = req.getParameter("password");
if (DATA.containsKey(account) && DATA.get(account).equals(password)) {
HttpSession session = req.getSession();
session.setAttribute("sid",account);
Cookie cookie = new Cookie("sid", account);
resp.addCookie(cookie);
//resp.getWriter().write("登录成功");
resp.sendRedirect("/index.jsp");
} else {
resp.getWriter().write("登录失败");
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
退出登录Servlet
@WebServlet("/loginOutServlet")
public class LoginOutServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Content-type", "text/html;charset=UTF-8");
//将session失效
req.getSession().invalidate();
Cookie cookie = new Cookie("account", "");
//将客户端cookie清除
cookie.setMaxAge(0);
resp.addCookie(cookie);
resp.getWriter().write("退出成功");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
全局拦截器,校验是否登录
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
//资源过滤
Set<String> urls = new HashSet<String>() {
{
add("/login.jsp");
add("/imgs/");
add("/css/");
add("/register.jsp");
add("/loginServlet");
add("/registerServlet");
add("/loginOutServlet");
}
};
String url = req.getRequestURL().toString();
for (String s : urls) {
if (url.contains(s)) {
chain.doFilter(request,response);
return;
}
}
HttpSession session = req.getSession();
String sid = (String) session.getAttribute("sid");
if (Objects.nonNull(sid)) {
chain.doFilter(request,response);
} else {
/*
req.setAttribute("login-tip","未登录");
req.getRequestDispatcher("/login.jsp").forward(req,response);
*/
resp.setHeader("Content-type", "text/html;charset=UTF-8");
resp.getWriter().write("未登录");
}
}
@Override
public void destroy() {
}
}
标签:resp,req,Session,cookie,Cookie,客户端 From: https://www.cnblogs.com/52-IT-y/p/17180783.html