会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话。
在客户端与服务器端交互的过程中,会产生一些数据。为了保存会话过程中产生的数据,在Servlet
技术中,提供了两个用于保存会话数据的对象,分别是Cookie和Session
。
一个网站怎么证明你来过?
-
服务端给客户端一个信件(cookie),客户端下次访问服务端带上信件就可以了
-
服务器登记你来过了,下次你来的时候我来匹配你(session)
- cookie,客户端技术
- session,服务器技术,我们可以把信息或者数据放在session中
cookie
浏览器端的会话技术,它通过在浏览器中记录一些服务器传递过来的数据,解决会话从什么时候开始,到什么时候结束。
就是在浏览器上记录会话过程中产生的临时数据。
HTTP协议是无状态协议,只定义规则,不保存数据,就有了cookie
的产生。
cookie的应用场景
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 上一次访问时间
- 购物车
cookie api
package com.lx.servlet.cookiesession;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/cookieSessionDemo")
public class CookieSessionDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("demoUserName","jayChou");
// cookie路径
// 服务器是根据路径获取cookie的,cookie默认路径是 请求路径的上一级
// 如果默认路径为多级的话,就拿不到本网站所有的cookie
// 所以一般我们设置项目路径为cookie的路径
cookie.setPath(req.getContextPath());
// cookie默认是会话级别,存储在内存中
// 也可以设置为持久化级别,存储在硬盘
// 单位为秒,正整数为持久化的时间,
// 负整数,代表会话级别
// 0 代表删除
// 删除cookie,同名同路径,setMaxAge 为 0
// 就是说如果不设置持久化的话,cookie就是会话级别,
// 一旦持久化,关闭浏览器再开启cookie依然存在,但是不安全
cookie.setMaxAge(60*60*24*7);
// 解决cookie跨域,一般设置为顶级域名,这里实际上设置的是前端的域名
cookie.setDomain("itlx.com");
// 向浏览器添加cookie
resp.addCookie(cookie);
Cookie[] cookies = req.getCookies();
// 这个获取所有cookie只是获取本网站的cookie,而不是浏览器的所有cookie
if (cookies!=null && cookies.length > 0){
for (Cookie cookieObj : cookies) {
System.out.println(cookieObj.getName() + ":" +cookieObj.getValue());
}
}
}
cookie注意事项
- 不支持中文,不能跨浏览器
- 有大小限制,4kb
- 对于一个网站,最多20个cookie,浏览器的上限是200个cookie
session
- 服务器会给每一个用户(浏览器)创建一个session对象
package com.lx.servlet.cookiesession;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/cookieSessionDemo")
public class CookieSessionDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 如果浏览器第一次访问服务器,就会创建一个session对象
// 服务器会自动创建一个cookie 名字是 JSESSIONID
HttpSession session = req.getSession();
session.setAttribute("sessionUserName","jay");
// 服务器创建session后会有一个随机的id
String id = session.getId();
Cookie cookie = new Cookie("JSESSIONID", id);
// 将session设置失效时间为7天
cookie.setMaxAge(60*60*24*7);
cookie.setPath(req.getContextPath());
resp.addCookie(cookie);
resp.setContentType("text/html;charset=utf-8");
String sessionUserName = (String) session.getAttribute("sessionUserName");
resp.getWriter().write("名字是:" + sessionUserName);
// session.isNew() 判断session是不是新创建的
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
session应用场景
Session 代表着服务器和客户端一次会话的过程。
Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。
cookie与session的面试问题
cookie和session的区别
- 存储位置不同
- cookie的数据信息存放在客户端浏览器上
- session的数据信息存放在服务器上
- 存储容量不同
- 单个cookie保存的数据<=4KB,一个站点最多保存20个Cookie
- 对于session来说并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西
- 隐私策略不同
- cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的
- session存储在服务器上,对客户端是透明对,不存在敏感信息泄漏的风险
- 性能使用不同
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
cookie与session生命周期
cookie默认的生命周期为会话级别,可以手动设置为持久化。
Tomcat中默认是30分钟, 在tomcat的conf/web.xml中有如下配置,也可以直接在java项目的web.xml中配置
<session-config>
<session-timeout>30</session-timeout>
</session-config>
这个30分钟是指距最后一次访问30分钟
服务端是根据 Cookie 中的信息判断用户是否登录,那么如果浏览器中禁止了 Cookie,如何保障整个机制的正常运转
默认情况下禁用 Cookie 后,Session 是无法正常使用的。
这是因为大多数 Web 服务器都是依赖于 Cookie 来传递 Session 的会话 ID 的。客户端浏览器禁用 Cookie 时,服务器将无法把会话 ID 发送给客户端,客户端也无法在后续请求中携带会话 ID 返回给服务器,从而导致服务器无法识别用户会话。
解决方案
-
URL 中携带 SessionID(URL重写):可以通过 URL 重写的方式将 Session ID 添加到所有的 URL 中。服务器生成 Session ID 后,将其作为 URL 的一部分传递给客户端,客户端在后续的请求中将 Session ID 带在 URL 中。服务器端需要相应地解析 URL 来获取 Session ID,并维护用户的会话状态。
第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接xxx?SessionID=123456…。 -
第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。
Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和Session的使用机制比较类似。
当用户第一次登录后,服务器根据提交的用户信息生成一个 Token,响应时将 Token 返回给客户端,以后客户端只需带上这个 Token前来请求数据即可,无需再次登录验证 -
隐藏表单字段传递 SessionID:将 Session ID 添加到 HTML 表单的隐藏字段中。在每个表单中添加一个隐藏的字段,保存 Session ID,客户端提交表单时会将 Session ID 随表单数据一起发送到服务器,服务器通过解析表单数据中的 Session ID 来获取用户的会话状态。
web三大组件
- servlet 处理请求并给与响应
- filter 过滤器
- listener 监听器
filter
filter的目的是,在请求和响应之间拦截检查
请求和响应的内容,主要是针对请求和响应做相应的处理。
javax.servlet.Filter
接口,定义过滤器必须具备筛选功能,具体的筛选逻辑由实现类去实现。
filter主要用于对用户请求进行预处理
Filter 可以认为是 Servlet 的一种”加强版”。它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理,是个典型的处理链;
Filter 可以对用户请求生成响应,和 Servlet 相同;
处理流程:用户请求 -> Filter 预处理 -> Servlet 处理请求生成响应 -> Filter 对响应进行后处理。
用处
- 在 HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest;
根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 头和数据;
- 在 HttpServletResponse 到达客户端之前,拦截 HttpServletResponse;
根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse头和数据。
应用场景
所谓过滤器顾名思义是用来过滤的。在 Java Web中,传入的 request、response 提前过滤掉一些信息,或者提前设置一些参数;然后再传入 Servlet 或者 Struts 的 action 进行业务逻辑。比如,过滤掉非法 URL(不是 login.do 的地址请求,如果用户没有登陆都过滤掉),或者在传入 Servlet 或者 Struts 的 action 前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话)。
- 登录、权限检查
- 过滤敏感词汇
- 全站编码
生命周期
- init 初始化
- doFilter 执行过滤操作
- destory 销毁
-
创建
服务器启动的时候创建,在servlet之前。 -
过滤
每当被过滤的请求被请求时,就会执行一次doFilter方法。如果不放行,到达不了servlet的service方法中。
-
销毁
关闭服务器的时候,在servlet之后。
过滤器执行顺序
一个请求路径上可以有多个过滤器进行拦截,并且他们的执行是有顺序的,
-
如果采用的是注解方式的话,跟过滤器的
名字
有关系,自然顺序(abcde)。 -
如果采用的是web.xml方式的话,顺序是配置
filter
的顺序。
注解配置方式
<%-- index.jsp
Created by IntelliJ IDEA.
User: Administrator
Date: 2022/5/4
Time: 20:33
To change this template use File | Settings | File Templates.
--%>
<%-- 这是一个指定 代表当前页面的解析方式--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>使用filter处理全站乱码问题</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/encoding" method="post">
<input type="text" name="name">
<input type="submit" value="提交">
</form>
</body>
</html>
package com.lx.servlet.filter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/encoding")
public class FilterDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name" );
resp.getWriter().write(name);
}
}
package com.lx.servlet.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 配置过滤路径,注解配置 和 web.xml 配置
*
* url-pattern
* 精确匹配 /servlet完整请求路径
* 目录匹配 / 中间自定义 * 例如:/abc/*
* 后缀名匹配 *.jsp *.action
*
*/
@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
// 过滤筛选方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// servletRequest 没有对get post进行区分,需要向下转型到HttpServletRequest
//
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
// 全站编码
if (httpServletRequest.getMethod().equalsIgnoreCase("post")){
httpServletRequest.setCharacterEncoding("utf-8");
}
// 设置响应对象 缓冲区的 编码
servletResponse.setContentType("text/html;charset=utf-8");
System.out.println("进入:doFilter");
// 放行,就是可以访问原本访问的servlet
// 如果不写,程序到这里就被拦截停止
filterChain.doFilter(servletRequest,servletResponse);
// 执行完servlet还能进入到过滤器
System.out.println("执行完servlet还能进入到过滤器");
}
}
xml配置方式
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--
配置拦截器name 和 路径
-->
<filter>
<filter-name>filterDemo2</filter-name>
<filter-class>com.lx.servlet.filter.FilterDemo2</filter-class>
</filter>
<!-- 配置要拦截的路径-->
<filter-mapping>
<filter-name>filterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filterDemo</filter-name>
<filter-class>com.lx.servlet.filter.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
拦截方式的介绍
package com.lx.servlet.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 默认情况下,对请求转发没有进行拦截操作
*
* <dispatcher></dispatcher>
* 作用:指定哪一种的访问方式会被拦截。
*
* REQUEST 默认的请求类型,就是直接从浏览器发送的请求
* FORWARD 请求转发的时候会进行拦截
* ERROR 拦截跳转到错误页面
* INCLUDE 拦截一个页面中包含另外一个页面
*/
@WebFilter(urlPatterns = "/*",dispatcherTypes = {DispatcherType.FORWARD,DispatcherType.REQUEST})
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filterDemo");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
listener
监听器实际应用中不多。
监听web服务器的运行,当发生特定事件的时候,采取预先设定的处理方案。
主要监听域对象
- Request 一次请求
- Session 一次会话
- ServletContext 整个web项目
主要监听这三个对象的状态变化,创建与销毁。
监听器常见的有8个,这里只说3个。
- 监听三个域对象的生命周期
- 监听三个域对象中属性的变化
- 监听javabean在session的变化
生命周期
-
ServletContext
创建:服务器启动的时候创建,一个web项目只有一个ServletContext对象
销毁:服务器关闭的时候,或者重新发布项目的时候 -
HttpSesion
创建:一次会话开始的时候,也就是第一次访问
销毁:session对象,在未操作的状态30分钟,要么调用手动销毁方法。
应用场景
- 统计在线人数或者统计网站的访问量
- 加载一些web初始化信息
ServletContextListener
监听域对象的创建和销毁
package com.lx.servlet.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* ServletContextListener 监听web项目启动 和 销毁
*/
@WebListener
public class ServletContextListenerDemo implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("web项目启动");
// 获取项目对象
Object source = servletContextEvent.getSource();
System.out.println(source);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("web项目销毁");
}
}
也可以配置xml
<listener>
<listener-class>com.lx.servlet.listener.ServletContextListenerDemo</listener-class>
</listener>
HttpSessionListener
- 访问HTML是否创建session:不会
- 访问JSP是否创建session:会
- 访问servlet是否创建session:不会(默认没有调用getSession方法)
package com.lx.servlet.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* ServletContextListener 监听web项目启动 和 销毁
* HttpSessionListener 监听一次会话的创建 和 销毁
*/
@WebListener
public class ServletContextListenerDemo implements ServletContextListener,HttpSessionListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("web项目启动");
// 获取项目对象
Object source = servletContextEvent.getSource();
System.out.println(source);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("web项目销毁");
}
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("一个session对象创建了");
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + ":上线了");
}
/**
* 何时销毁
*
* 非正常关闭服务器
* session过期(tomcat默认过期时间30分钟)
* 手动调用session.invalidate()
* @param httpSessionEvent
*/
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("一个session对象销毁了");
HttpSession session = httpSessionEvent.getSession();
System.out.println(session.getId() + ":下线了");
}
}
拦截器与过滤器的区别
过滤器(Filter)
过滤器,是在java web中将你传入的request、response提前过滤掉一些信息,或者提前设置一些参数。然后再传入Servlet或Struts2的 action进行业务逻辑处理。比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入Servlet或Struts2的action前统一设置字符集,或者去除掉一些非法字符。
拦截器(Interceptor)
拦截器,是面向切面编程(AOP,Aspect Oriented Program)的。就是在你的Service或者一个方法前调用一个方法,或者在方法后调用一个方法。比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器与过滤器的区别
Spring 的 Interceptor(拦截器)与 Servlet 的 Filter 有相似之处,比如二者都是 AOP 编程思想的体现,都能实现权限检查、日志记录等。
不同之处总结如下:
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2、拦截器是属于springmvc的,过滤器是属于servlet的。
3、filter只在servlet前后起作用,拦截器能够深入到方法前后、异常抛出前后等,具有更大的弹性。
在spring中,优先使用拦截器,它几乎可以实现过滤器的所有功能。
使用场景
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
拦截器:
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
过滤器应用场景:
1)过滤敏感词汇(防止sql注入)
2)这是字符编码
3)URL级别的权限访问控制
4)压缩响应信息
部分知识转载自:
https://www.bilibili.com/video/BV12J411M7Sj?p=17&spm_id_from=pageDriver&vd_source=64c73c596c59837e620fed47fa27ada7
https://www.iteye.com/blog/18810098265-2002428
https://www.cnblogs.com/jirglt/archive/2012/10/20/2732255.html
https://blog.csdn.net/wz2292667460/article/details/79366493
https://blog.51cto.com/u_15989526/6288855
https://blog.csdn.net/weixin_37264997/article/details/90693525
https://blog.csdn.net/u013938578/article/details/136189698
第一,科学家的类别可以不一样,有些是架构型的科学家,有些是阐述型科学家,有些是突破型的科学家,有些是工匠型的科学家……。年龄大的科学家,他们人生阅历很丰富,对系统架构的认识很清晰;他们向青年人阐述架构模型,年轻人有奇思怪想就容易突破,这个架构里的缺陷就容易被修复了;有些人深入实践,在工程实现方面积累了丰富的Know-How经验,善于解决复杂的工程问题,我们也称之为工匠科学家。最有创造力的是年轻人,但最有架构能力的还是有经验的老专家、老教授,他们对整个架构有很清晰的认识。所以,要将老、中、青结合起来,国家才能够攻克大的难关。
https://baijiahao.baidu.com/s?id=1760664270073856317&wfr=spider&for=pc
擦亮花火、共创未来——任正非在“难题揭榜”花火奖座谈会上的讲话
任正非