JavaWeb(二) Servlet

本文最后更新于:2023年2月28日 下午

Java Servlet (Server Applet) 是运行在 Web 服务器或应用服务器上的程序,实现动态资源文件。

其作为 Web 浏览器或其他 HTTP 客户端和 HTTP 服务器上的数据库或应用程序之间的中间层,扩展服务器功能并响应传入请求。

Servlet接口实现类的开发步骤:

  1. 创建一个继承 HttpServlet 父类的 Java类,实现Servlet 接口
  2. 重写 HttpServlet 父类方法 doGet() / doPost()
  3. web.xml中注册类信息,完成路由分发

1. Servlet容器

Servlet容器,提供Servlet功能的服务器。实现Servlet接口规范,为部署在容器内的应用提供支持,而无需关心接口被应用如何使用。

Servlet应用,使用Servlet接口规范,完成具体业务逻辑,而无需关心规范被容器如何实现

Tomcat

轻量级Web服务器。

下载

下载文件夹压缩包,免去安装。

目录结构

1
2
3
4
5
6
7
/bin 		包含Tomcat的启动文件startup.bat和关闭文件shutdown.bat
/conf 配置文件
/lib Tomcat的jar包和一些规范(Servlet/jsp)jar包
/log 运行日志
/temp 运行时临时文件
/webapp web应用(供外界访问的web资源)
/work Tomcat的工作目录(如: 包含编译后的jsp文件)

使用Maven Tomcat插件

注意!!! 插件是Tomcat7,不支持Servlet3.0,互搓的概率非常大,还是下载Tomcat后配入IDEA吧

pom.xml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 设置编码格式 -->
<uriEncoding>UTF-8</uriEncoding>
<!-- 控制 tomcat 端口号 -->
<port>8080</port>
<!-- 项目发布到 tomcat 后的名称 -->
<path></path>
</configuration>
</plugin>
</plugins>
</build>

2. Servlet路由配置

由于Servlet的java文件路经过深,我们将Servlet类文件映射至一个别名,实现路由分发。

具体有两种实现方式,Servlet3.0及以上版本均支持注解实现。

web.xml

web.xml主要用来配置Servlet三大组件Filter、Listener、Servlet,Servlet3.0后非web工程必须文件。本节学习记录Servleti相关标签。

根元素

根元素 <web-app>,其属性配置模式文件、指定路由配置是否全部依赖于web.xmlmetadata-complete="true")等

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
metadata-complete="true">
</web-app>

servlet元素

<servlet></servlet> 用来声明一个servlet的数据,主要有以下子元素:

  • <servlet-name></servlet-name> 指定servlet的名称
  • <servlet-class></servlet-class> 指定servlet的类名称
  • <jsp-file></jsp-file> 指定web站台中的某个JSP网页的完整路径
  • <init-param></init-param> 用来定义参数,可有多个init-param。在servlet类中通过getInitParamenter(String name)方法访问初始化参数
  • <load-on-startup></load-on-startup>指定当Web应用启动时,装载Servlet的次序。当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet。当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它。

<servlet-mapping></servlet-mapping>用来定义servlet所对应的URL 路由,包含两个子元素:

  • <servlet-name></servlet-name> 指定servlet的名称
  • <url-pattern></url-pattern> 指定servlet所对应的URL
1
2
3
4
5
6
7
8
<servlet>
<serservlet-name>LoginServlet</serservlet-name>
<servlet-class>com.controller.LoginServlet</servlet-class>
</servlet>
<servlt-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlt-mapping>

默认资源文件

用于配置启动项目时(输入项目URL)直接跳转的页面。

1
2
3
4
5
6
7
<!-- “/” 为项目根路径,优先级从上至下 -->
<!-- 如设置的页面均不存在则返回404页面 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

注解

@WebServlet

为每个Servlet类配置一个@WebServlet注解,有以下参数:

属性 类型 意义
name String 指定 Servlet 的 name 属性,等价于 <servlet-name>。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。
value/urlPatterns String[] 指定一组 Servlet 的 URL 匹配模式。等价于<url-pattern>标签。
loadOnStartup int 指定 Servlet 的加载顺序,等价于 <load-on-startup>标签。
initParams WebInitParam[] 指定一组 Servlet 初始化参数,等价于<init-param>标签。
asyncSupported boolean 声明 Servlet 是否支持异步操作模式,等价于<async-supported> 标签。
description String 该 Servlet 的描述信息,等价于 <description>标签。
displayName String 该 Servlet 的显示名,通常配合工具使用,等价于 <display-name>标签。

示例

1
2
3
4
5
6
7
8
@WebServlet(name = "myUserServlet", 
urlPatterns = "/user/test",
loadOnStartup = 1,
initParams = {
@WebInitParam(name="name", value="小明"),
@WebInitParam(name="pwd", value="123456")
}
)

3. Servlet结构

Servlet生命周期

Servlet 由其容器实例化,每个Servlet实例化时都会经历生命周期。

  • init() 初始化
  • service() 处理客户端的请求
  • destroy() 销毁

结构详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* Servlet接口 */
public interface Servlet {
/* 3个生命周期方法 */
public void init(ServletConfig config) throws ServletException;
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public void destroy();
/* 2个普通方法 */
public ServletConfig getServletConfig();
public String getServletInfo();
}

/* GenericServlet抽象类 */
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
/* 重写除service()的生命周期方法 */
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void destroy() {}
}

/* HttpServlet抽象类 */
public abstract class HttpServlet extends GenericServlet {
/* 重写service()方法,用于判断Http请求方法 */
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
... ...
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if ... ...
}
/* 提供doGet()、doPost()等Http协议请求方法,如下 */
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
}

因此,Servlet实现业务逻辑的模式为(Java设计模式中的模板模式):

graph LR;
	Tomcat新建LoginServlet实例 --执行Servlet生命周期--> 调用service方法 --判断Get/Post请求类型--> 调用LoginServlet中重写的doGet/doPost方法

4. Servle处理Request

浏览器访问Servlet的过程

在客户端发出每个Http请求时,服务器都会创建一个对应的HttpServletRequest对象,并把请求数据封装到该对象中。根据上文对Servlet结构的拆解,我们发现HttpServletRequest对象将作为service()方法和对应请求类型的服务方法doGet()/doPost() 的参数传入。

HttpServletResponse对象的实现结构如下:

graph BT;
	RequestFacade类 --implements--> HttpServletRequest接口 --extends--> ServletRequest接口

HttpServletRequest对象封装了请求信息(请求行、请求头、请求体),在实现业务逻辑时通过其封装方法解读请求数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 现有HttpServletRequest实例化对象req */
/* 3个返回枚举对象的概览方法 */
// getHeaderNames() 返回包含在该请求中包含的所有的头名
Enumeration headerNames = req.getHeaderNames();
// getParameterNames() 返回包含在该请求中所有参数的名称(不论GET/POST)
Enumeration parameterName = req.getParameterNames();

/* 获取请求中具体字段的信息 */
// getRequestURL() 返回请求URL的StringBuffe
String url = req.getRequestURL().toString();
// getMethod() 返回请求方法
String method = req.getMethod();
// getCharacterEncoding() 返回请求主体中使用的字符编码的名称
String characaterEncoding = req.getCharacterEncoding();
// getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null
String contentType = req.getContentType()
// getContextPath() 返回部署路径
String contextPath = req.getContextPath()
// getParameter(String name)返回指定请求参数的值,或者如果参数不存在则返回 null
String <parameter> = getParameter(String <parameter_name>)

5. Servlet处理Response

在客户端发出每个Http请求时,服务器创建一个对应的HttpServletRequest对象的同时,也会创建一个HttpServletResponse对象。HttpServletResponse对象同样将将作为service()方法和对应请求类型的服务方法doGet()/doPost() 的参数传入。

HttpServletResponse对象的实现结构如下:

graph BT;
	ResponseFacade类 --implements--> HttpServletResponse接口 --extends--> ServletResponse接口

HttpServletResponse对象封装了响应信息(状态行、响应头、响应体),在实现业务逻辑时通过其封装方法获得文本输出流,或返回响应数据:

1
2
3
4
5
6
7
8
9
/* 现有HttpServletResponse实例化对象resp */
/* 更改响应消息 */
// void setContentType(String type) 设置 Servlet 输出内容的 MIME类型,
res.setContentType(String <type>);

/* 获得文本输出流 */
// getWriter() 获得文本输出流,返回类型为PrintWriter类,实现了Writer抽象类(字符输出流的根类),提供print()方法
PrintWriter out = reap.getWriter();
out.print(<文本格式变量(如字符串/HTML)>);

6. Redirect与RequestDispatcher

Redirect RequestDispatcher
功能 重定向 转发
对象 响应对象的执行方法 resp.sendRedirect(<path>) 请求对象的执行方法 req.getRequestDispatcher(<path>).forward(req, resp)
实现逻辑 服务器返回客户端响应消息中状态为 302重定向;location 写入重定向地址
客户端发起新的Get请求
地址栏变为重定向地址
产生新request/response对象
服务器内部处理转发,与客户端无关
地址栏不变
转发原request/response对象
传参方式 因发起新Get请求
仅支持URL地址传递参数
因服务器内部转发
可直接获取使用request/response对象
跳转范围 支持重定向到服务器外部资源
不支持重定向至项目安全目录
不支持转发至外部资源
仅支持转发至服务器内部资源
相对位置 / 服务器根路径
通常需要 getContextPath() + <path> 拼接跳转路径
/ 项目根路径
运用场景 用于处理Post请求的刷新重复提交。因此,无论Post请求逻辑处理成功/失败/异常,均需执行重定向 用于处理Get请求转发相应资源(JSP/HTML/图片/文档等),仅对外暴露请求地址,真实资源由服务器端转发响应。因此,客户端将不再直接访问文档等资源,而是仅能访问资源映射的地址

以某登录界面 /login 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// GET请求时,RequestDispatcher实现静态资源访问的转发
req.getRequestDispatcher("/WEB-INF/jsp/login.html").forward(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// POST请求时,收集表单数据。根据表单数据的逻辑处理重定向至相应路径
String userName = req.getParameter("username");
String password = req.getParameter("pwd");
String url;
if (... ...) {
... ...
url = "/welcome";
} else {
url = "/login";
}
resp.sendRedirect(req.getContextPath() + url);
}
}

7. 多个Servlet数据共享

会话是从浏览器访问服务器开始,到访问服务器结束,浏览器关闭为止的这段时间内容产生的多次请求和响应的总和。

Cookie是客户端会话技术:

  1. 用户通过浏览器第一次向某网站发送请求,并由 <OneServlet> 处理业务逻辑时。<OneServlet> 在运行期间创建一个 cookie 存储与当前用户相关数据。
  2. <OneServlet> 工作完毕后,将cookie写入响应头交返回浏览器。浏览器收到响应包之后,将 cookie 存储在浏览器的缓存中。
  3. 一段时间之后,用户通过同一个浏览器再次向某网站发送请求,并由申请<TwoServlet> 处理业务逻辑时。浏览器需要无条件的将该网站已有的 cookie 写入请求头发送给服务器。此时<TwoServlet>通过读取请求头中cookie中信息,得到<OneServlet> 提供的共享数据。

Cookie的写入

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/write")
public class WriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 创建Cookie对象
// 一个cookie对象存放一个键值对,在实例化时以String类型传参给构造器,键不支持中文
Cookie cookie1 = new Cookie("key1", "abc");
Cookie cookie2 = new Cookie("key2", "def");
// 2. 将cookie写入请求头
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}

Cookie的读出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet("/read")
public class ReadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 从请求头中读出cookie
// getCookies() 返回所有cookie的数组
Cookie[] cookieArray = req.getCookies();
for (Cookie c:cookieArray) {
if (c.geName().equals("key1")){
// cookie键
String key = c.getName();
// cookie值
String value = c.getValue();
... ...
}
}
}
}

Cookie的有效时间

会话Cookie & 持久性Cookie

如果 cookie 不包含到期时间,则可视为会话 cookie。存储在内存中,不会写入磁盘。当浏览器关闭时,cookie 将永久丢失。

如果 cookie 包含到期时间,则可视为持久性 cookie。在指定的到期时间,cookie 将从磁盘中删除。

设置到期时间

cookie.setMaxAge(<正数/负数/0>);

  • 如果设置为正数,则cookie存储到客户端电脑,不论关闭浏览器或关闭电脑,直到时间(此cookie从创建到过期所能存在的时间,以秒为单位)到才会过期;
  • 如果设置为负数,则cookie在内存中保存,关闭浏览器即失效;
  • 如果设置为0,则立即删除该cookie。

Servlet域对象

对于web应用程序,在不同的Servlet实现类之间传递数据是至关重要的。Servlt有三个不同的作用域: request scope, session scope , application scope 。

域对象的作用:保存数据、获取数据、共享数据。

域对象的名称 域对象的类型
application ServletContext (javax.servlet.http.HttpServletRequest)
request HttpServletRequest (javax.servlet.http.HttpServletRequest)
session HttpSession (javax.servlet.http.HttpSession)

全局作用域对象(ServletContext)

服务器启动时,为每个Web应用创建一个ServletContext对象,保存在服务器内存里。所有Servlet实现类均可直接由request对象获得。

还是不明白ServletContext域对象的实现结构。既然作为全局作用域,所有Servlet实现类共享的变量仓库,就应该存在于服务器端,为何是从HttpServletRequest对象中拿到(req.getServletContext()),并且发现GenericServlet抽象类中重写了ServletConfig接口的方法 getServletContext(),他是怎么跑到HttpServletRequest对象里去的呢?除非HttpServletRequest对象封装请求数据的同时还封装了Web容器的初始数据。那么它是怎么做到的呢?由于HttpServletRequest是接口,无法实例化,他应该是借由其实现类Request类/RequestFacade类等通过向上转型实现实例化的。难道玄机在这些类中?

时间精力有限,希望之后能把Tomat的源码掰开读读,就能解惑了。

ServletContext域对象写入

1
2
3
4
5
6
7
8
9
10
11
@WebServlet("/write")
public class WriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 通过请求对象从Tomcat获取全局作用域对象
ServletContext application = req.getServletContext();
// 2. 将数据添加至全局作用域对象
// setAttribut() 第一参数:键为String,第二参数:值为任意类型
application.setAttribute("number", 100);
}
}

Attribute以键值对的形式存储数据

ServletContext域对象读出

1
2
3
4
5
6
7
8
9
10
11
@WebServlet("/read")
public class ReadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 通过请求对象从Tomcat获取全局作用域对象
ServletContext application = req.getServletContext();
// 2. 将数据添加至全局作用域对象
// getAttribut() 返回Object类型,需强制转换
Integar number = (Integar)application.getAttribute("number");
}
}

请求作用域对象(HttpServletRequest)

在同一Web项目下,两个Servlet实现类间通过请求转发的同时共享请求对象共享数据。

HttpServletRequest域对象写入

1
2
3
4
5
6
7
8
9
10
@WebServlet("/write")
public class WriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 将数据添加至请求作用域对象中
req.setAttribute("number", 100);
// 2. 请求转发
req.getRequestDispatcher(<path>).forward(req, resp);
}
}

HttpServletRequest域对象读出

1
2
3
4
5
6
7
8
@WebServlet("/read")
public class ReadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从请求作用域对象中读取数据
Integar number = (Integar)req.getAttribute("number");
}
}

会话作用域对象(Session)

Session是服务器端会话技术:

  1. 每个浏览器访问服务器Servlet,jsp等动态资源文件时,就会在服务器中自动创建相应Session对象,并分配SessionID作为标识。SessionID将以JSESIONID存储为cookie。
  2. 浏览器每次发起请求,请求头中都携带cookie:JSESIONID,凭此在服务器中获取其Session对象中存储的数据。
  3. 服务器将相应数据写入响应体中返回浏览器客户端。

Session域对象写入

1
2
3
4
5
6
7
8
9
10
11
@WebServlet("/write")
public class WriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 通过请求对象从Tomcat获取Session对象
// getSession(false) 是当发起请求的浏览器无对应的Session对象时,不为其新建Session对象
HttpSession session = req.getSession();
// 2. 将数据添加至会话作用域对象
sesion.setAttribute("number", 100)
}
}

Session域对象读出

1
2
3
4
5
6
7
8
9
10
11
@WebServlet("/read")
puvbblic class ReadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 通过请求对象从Tomcat获取全局作用域对象
HttpSession session = req.getSeesion();
// 2. 读取数据
// getAttribut() 返回Object类型,需强制转换
Integar number = (Integar)session.getAttribute("number");
}
}

Session的有效时间

session是必须存在到期时间的,过期后服务器中存储的Session对象销毁。但session中的有效时间并不是从Session对象创建时开始计时,而是从其停止活动时计时(因此,若sessiob一直被使用,则一直重新计时而session不会过期)。

无法保证session数据的存储方式。它可能存储在文件中,在服务器重新启动时不可用,或者它可能存储在数据库中,因此在服务器重新启动时可用。

但sesion不一定在销毁时失效。由于Session对象由SeesionID唯一标识,通常存储于 cookie中。当cookie销毁时(如会话cookie在关闭浏览器时销毁),SessionID同时被销毁无法找回Session对象,Session失效。

设置到期时间

  1. web.xml 文件中配置

    单位分,默认为30分钟

    1
    2
    3
    <session-config>
    <session-timeout>30</session-timeout>
    </session-config>
  2. session.setMaxInactiveInterval(); 设置Session对象的到期时间

Session对象的销毁 session.invalidate();

作用范围与生命周期

cookie session request application
创建 在Servlet实现类中实例化Cookie对象 浏览器访问服务器Servlet,jsp等动态资源文件时就会自动创建Session对象,保存在服务器内存里可直接由request对象获得 客户端向服务器发送一次请求,服务器就会创建Request对象 服务器启动时,为每个Web应用创建一个ServletContext对象,保存在服务器内存里可直接由request对象获得
销毁 1. 会话cookie:关闭浏览器
2. 持久性cookie:过期
3. 手动销毁
1. session过期
2. 非正常关闭服务器(正常关闭session会序列化,再次启动服务器session会被反序列化)
3. 手动销毁
服务器对这次请求作出响应后就会销毁Request对象 服务器关闭或者项目从服务器中移除
有效 会话cookie在一次会话期间有效 (打开浏览器会话开始,直到关闭浏览器会话结束)
持久性cookie在有效时间内有效
一次会话期间 (打开浏览器会话开始,直到关闭浏览器会话结束) 当前请求中 服务器运行中

关于parameter & attribute & cokkie & session 的一些总结:

  • 每个作用域接口均包含setAttribute()/ getAttribute()方法,作用域对象均从request对象中获取
  • parameter & cookie只能是字符串类型,attribute & session可以是任意类型

8 Servlet监听器

监听器可以用来检测网站的在线人数,统计网站的访问量等,由于目前未涉及到这样规模的项目,暂不深究。

9. Servlet过滤器

Servlet规范中提供Filter接口,实现该接口可以对访问指定资源文件(静态/动态)的请求和响应进行拦截。Filter对用户请求进行预处理,接着将请求交给Servlet进行业务逻辑处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter主要有:

  • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
  • 日志Filter:详细记录某些特殊的用户请求。
  • 负责解码的Filter:包括对非标准编码的请求解码。
  • 能改变XML内容的XSLT Filter 等。

路由配置

web.xml

web.xml中,与Servlet类似

1
2
3
4
5
6
7
8
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.filter.loginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

注解

1
@WebFilter("<path>")

路径格式:

  • 精确匹配,仅过滤指定路径地址:/user/LoginServlet

  • 通配符过滤指定路径下的所有下级路径:/admin/*

  • 通配符过滤指定文件类型:*.action

  • 不支持既指定路径,又指定扩展名的规则:/admin/*.action

生命周期

  • init(),首次创建过滤器后,回调

  • void doFilter(ServletRequest request, ServletResponse response, FilterChain chain),过滤功能的具体实现方法,每当请求匹配过滤器的过滤规则时,均回调

    传入参数包括拦截的请求与响应 requestresponse;当前 Filter链上的对象chain ,其方法chain.doFilter(req, res);代表过滤器放行,请求与响应将进入下一个Filter或Servlet

  • destroy(),过滤器对象销毁前,回调

例子:判断用户是否登陆的过滤器

为防止用户未登录即访问服务器资源吗,过滤器通过Session信息识别用户是否已登录。已登录用户在服务器中有对应的session对象,以此为是否登陆的表示进行过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebFilter("/*")
public class loginFilter extends HttpFilter {
private List<String> excludes = List.of("/login");

@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
// 放行登陆页面
for (String e : excludes) {
if (e.equals(req.getServletPath())) {
chain.doFilter(req, res);
return;
}
}
// 除登录页面外必须登陆后才可访问
User user = (User) req.getSession().getAttribute("user");
if (user != null) {
chain.doFilter(req, res);
} else {
res.sendRedirect(req.getContextPath() + "/login");
}
}
}

参考资料


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!