自定义Filter
:继承Filter
并且重写其方法(主要重写doFilter()
)
配置Filter
:配置@WebFilter(urlPatterns = ("/*"))
以及拦截路径,并且在启动类添加注解@ServletComponentScan
开启Servlet
组件支持
@WebFilter(urlPatterns = ("/*"))
public class DemoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init 初始化拦截器...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("拦截到了请求...放行前");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("拦截到了请求...放行后");
}
@Override
public void destroy() {
System.out.println("destroy方法执行...");
}
}
init()
:初始化方法,Web 服务器启动,创建Filter
时调用,只调用一次
doFilter()
:拦截到了请求,并执行该方法,可多次调用,注意关键的实现filterChain.doFilter()
以及拦截前后的逻辑实现
destroy()
:销毁方法,服务器关闭执行,只调用一次
拦截路径 | urlPatterns值 | 含义 |
---|---|---|
拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
目录拦截 | /emps/* | 访问/emps下的所有资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
一个web应用中,可以配置多个过滤器,这多个过滤器就形成了一个过滤器链。
执行顺序: 注解配置的Filter
,优先级是按照过滤器类名(字符串A, B, C, ....
)的自然排序。
doFilter()
)通过校验请求认证头(authHeader
)所携带的token
去校验,我在Postman
中使用的是Bearer Token
身份校验,Bearer Token
在请求头中以 Bearer
关键字加上令牌本身的形式发送,格式通常为Authorization: Bearer <token>
。详细可看Apifox--什么是Bearer Token
代码展示 --- doFilter()
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 设置响应头
setupResponseHeaders(httpResponse);
// 处理预检请求
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
// 获取请求路径
String requestPath = httpRequest.getRequestURI();
String contextPath = httpRequest.getContextPath();
String path = requestPath.substring(contextPath.length());
// 检查是否为排除路径
if (isExcludePath(path)) {
chain.doFilter(request, response);
return;
}
// 获取认证头部
String authHeader = httpRequest.getHeader(AUTH_HEADER);
// 检查认证头是否存在且格式正确
if (authHeader == null || !authHeader.startsWith(BEARER_PREFIX)) {
sendUnauthorizedResponse(httpResponse, "请先登录");
return;
}
// 提取 JWT 令牌
String jwtToken = authHeader.substring(BEARER_PREFIX.length()).trim();
try {
// 验证 JWT 令牌
if (!JwtUtils.validateToken(jwtToken)) {
sendUnauthorizedResponse(httpResponse, "令牌无效或已过期");
return;
}
// 从 JWT 中解析用户信息
Long userId = JwtUtils.getUserIdFromToken(jwtToken);
String userAccount = JwtUtils.getUserAccountFromToken(jwtToken);
String userName = JwtUtils.getUserNameFromToken(jwtToken);
// 将用户信息放入请求属性,供后续使用
httpRequest.setAttribute("id", userId);
httpRequest.setAttribute("userAccount", userAccount);
httpRequest.setAttribute("userName", userName);
System.out.println("JWT认证成功 - 用户ID: " + userId + ", 账号: " + userAccount);
// 继续过滤器链
chain.doFilter(request, response);
} catch (Exception e) {
sendUnauthorizedResponse(httpResponse, "令牌解析失败: " + e.getMessage());
}
}
在执行过滤器中,除了校验令牌,我们还要做其他的工作
检查是否为排除路径(不需要认证的路径):比如登录、注册等等
// 排除认证的路径
private static final String[] EXCLUDE_PATHS = {
"/user/login",
"/user/register",
"/doc.html",
"/webjars/",
"/swagger-resources",
"/v2/api-docs"
};
/**
* 检查是否为排除路径(不需要认证的路径)
*/
private boolean isExcludePath(String path) {
for (String excludePath : EXCLUDE_PATHS) {
if (path.startsWith(excludePath)) {
return true;
}
}
return false;
主要处理CORS(跨域资源共享)配置和字符编码
/**
* 设置响应头
*/
private void setupResponseHeaders(HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With");
response.setHeader("Access-Control-Max-Age", "3600");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
}
作用:
Access-Control-Allow-Origin
: "*
"
http://your-frontend.com
"Access-Control-Allow-Methods
: "GET, POST, PUT, DELETE, OPTIONS
"
Access-Control-Allow-Headers
: "Authorization, Content-Type, X-Requested-With
"
JWT Token
Access-Control-Max-Age
: "3600"
OPTIONS
请求OPTIONS 是 HTTP 方法之一,用于获取目标资源支持的通信选项。在 CORS 中,浏览器会自动发送 OPTIONS请求来检查是否允许跨域访问。
触发条件:
跨域请求(域名、端口、协议不同)
非简单请求(满足以下任一条件):
简单请求(不会触发 OPTIONS):
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://frontend.com
非简单请求(会触发 OPTIONS):
自定义请求头
POST /api/user HTTP/1.1
Host: localhost:8080
Origin: http://localhost:3000
Authorization: Bearer token123 # 自定义头
X-Custom-Header: value # 自定义头
非简单 Content-Type
POST /api/user HTTP/1.1
Content-Type: application/json # 非简单 Content-Type
非简单 HTTP 方法
PUT /api/user/1 HTTP/1.1
PATCH /api/user/1 HTTP/1.1
DELETE /api/user/1 HTTP/1.1
/**
* 发送未认证错误响应
*/
private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
String jsonResponse = String.format(
"{\"code\": 401, \"message\": \"%s\", \"data\": null, \"description\": \"未授权访问\"}",
message
);
response.getWriter().write(jsonResponse);
}
end
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。