LiiuZeYu_branch
lzy 10 months ago
parent 317605f376
commit af1283fc07

@ -0,0 +1,163 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.filter;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.handler.HttpHandler;
import com.yami.shop.common.response.ResponseEnum;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.security.common.adapter.AuthConfigAdapter;
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
import com.yami.shop.security.common.manager.TokenStore;
import com.yami.shop.security.common.util.AuthUserContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
*
* AuthConfigAdapter访
* 访线
*
* @author
* @date 2022/3/25 17:33
*/
@Component
public class AuthFilter implements Filter {
// 用于记录日志,方便在运行过程中输出相关的调试、错误等信息,便于排查问题
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
// 注入AuthConfigAdapter接口的实现类通过它可以获取到配置的需要授权和不需要授权的路径信息
@Autowired
private AuthConfigAdapter authConfigAdapter;
// 注入HttpHandler用于处理向Web端输出响应相关的操作比如将服务器响应信息正确地返回给前端页面
@Autowired
private HttpHandler httpHandler;
// 注入TokenStore主要用于管理和操作与令牌Token相关的存储及查询等功能例如从存储中获取用户信息等
@Autowired
private TokenStore tokenStore;
// 通过配置文件注入令牌名称,用于后续从请求头中获取对应的令牌信息
@Value("${sa-token.token-name}")
private String tokenName;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
/**
*
*
* @param request ServletHttpServletRequestHTTP
* @param response ServletHttpServletResponse
* @param chain ServletJSP
* @throws IOException
* @throws ServletException Servlet
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将ServletRequest转换为HttpServletRequest方便获取HTTP请求相关的属性和信息比如请求路径、请求头信息等
HttpServletRequest req = (HttpServletRequest) request;
// 将ServletResponse转换为HttpServletResponse便于后续进行HTTP响应相关的操作比如设置响应状态码、响应头、输出响应内容等
HttpServletResponse resp = (HttpServletResponse) response;
// 获取当前请求的URI统一资源标识符用于后续与配置的授权路径进行匹配判断
String requestUri = req.getRequestURI();
// 通过AuthConfigAdapter获取不需要授权的路径列表用于判断当前请求是否命中这些无需授权的路径
List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
// 创建AntPathMatcher对象用于进行路径的匹配操作它支持通配符等方式来灵活匹配路径
AntPathMatcher pathMatcher = new AntPathMatcher();
// 如果不需要授权的路径列表不为空就遍历这些路径检查当前请求的URI是否匹配其中某个无需授权的路径模式
if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
for (String excludePathPattern : excludePathPatterns) {
// 使用AntPathMatcher进行路径匹配如果匹配成功说明当前请求是不需要授权的直接将请求传递给下一个过滤器或目标资源
if (pathMatcher.match(excludePathPattern, requestUri)) {
chain.doFilter(req, resp);
return;
}
}
}
// 从请求头中获取名为tokenName的令牌信息这个令牌通常用于验证用户的登录状态和权限等
String accessToken = req.getHeader(tokenName);
// 判断当前请求的URI是否匹配可能需要登录但不登录也能用的路径模式通过AuthConfigAdapter中定义的常量路径进行匹配
boolean mayAuth = pathMatcher.match(AuthConfigAdapter.MAYBE_AUTH_URI, requestUri);
// 用于存储从令牌中解析出来的用户信息,如果后续成功获取到用户信息则会赋值
UserInfoInTokenBO userInfoInToken = null;
try {
// 如果获取到的令牌信息不为空(即存在令牌),说明用户可能已经登录,需要进一步验证并获取用户信息
if (StrUtil.isNotBlank(accessToken)) {
// 使用StpUtil工具类来校验用户是否已经登录若登录验证失败会抛出异常在这里捕获异常并进行相应处理
try {
StpUtil.checkLogin();
} catch (Exception e) {
// 如果登录校验失败通过HttpHandler将表示未授权的服务器响应信息输出到Web端通常是返回给前端页面显示相应的错误提示
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
// 如果登录校验通过从TokenStore中根据令牌获取对应的用户信息从缓存等存储中查询并解析用户信息
userInfoInToken = tokenStore.getUserInfoByAccessToken(accessToken, true);
} else if (!mayAuth) {
// 如果没有令牌且当前请求也不属于可能不需要登录就能访问的路径,那么说明该请求需要授权但未提供有效令牌,返回表示未授权的响应给前端
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
// 将获取到的用户信息保存到AuthUserContext中方便在后续的请求处理过程中比如在其他地方需要获取当前登录用户信息时可以直接获取
AuthUserContext.set(userInfoInToken);
// 如果前面的授权校验等操作都通过了将请求传递给下一个过滤器或者最终的目标资源如业务处理的Servlet等继续处理
chain.doFilter(req, resp);
} catch (Exception e) {
// 手动捕获非controller层抛出的异常进行统一处理
if (e instanceof YamiShopBindException) {
// 如果是YamiShopBindException类型的异常通过HttpHandler将该异常对应的响应信息输出到Web端
httpHandler.printServerResponseToWeb((YamiShopBindException) e);
} else {
// 如果是其他类型的异常,直接抛出,让上层的异常处理机制(比如容器的异常处理)去进一步处理
throw e;
}
} finally {
// 无论请求处理过程是否出现异常最终都要清理AuthUserContext中的用户信息避免数据残留影响下一次请求处理
AuthUserContext.clean();
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
Loading…
Cancel
Save