tech.mhuang.pacebox.springboot.auth.interceptor.OpAuthInterceptor Maven / Gradle / Ivy
The newest version!
package tech.mhuang.pacebox.springboot.auth.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import tech.mhuang.pacebox.core.exception.BusinessException;
import tech.mhuang.pacebox.core.util.CollectionUtil;
import tech.mhuang.pacebox.core.util.StringUtil;
import tech.mhuang.pacebox.springboot.auth.constant.AuthConstant;
import tech.mhuang.pacebox.springboot.core.constans.Global;
import tech.mhuang.pacebox.springboot.core.jackson.JsonUtil;
import tech.mhuang.pacebox.springboot.core.spring.start.SpringContextHolder;
import tech.mhuang.pacebox.springboot.protocol.GlobalHeader;
import tech.mhuang.pacebox.springboot.protocol.Result;
import tech.mhuang.pacebox.springboot.protocol.auth.AuthExcludeUrl;
import tech.mhuang.pacebox.springboot.protocol.auth.AuthUrl;
import tech.mhuang.pacebox.springboot.redis.commands.IRedisExtCommands;
import java.util.List;
import java.util.Objects;
/**
* 拦截器
*
* @author mhuang
* @since 1.0.0
*/
@Slf4j
public class OpAuthInterceptor implements HandlerInterceptor {
@Setter
private int redisDataBase;
@Setter
private boolean checkUrl;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof ResourceHttpRequestHandler)) {
try {
if (checkUrl) {
log.debug("拦截的URL:{},拦截类型:{}", request.getRequestURL().toString(), request.getMethod());
boolean flag = this.checkUrlIsNotLogin(getUri(request));
if (!flag) {
String headJson = request.getHeader(Global.GLOBAL_HEADER);
if (StringUtil.isNotBlank(headJson)) {
GlobalHeader globalHeader = JsonUtil.toData(headJson, GlobalHeader.class);
if (StringUtil.isNotBlank(globalHeader.getToken())) {
flag = this.checkUrlPower(globalHeader.getUserId(), request);
if (!flag) {
throw new BusinessException(Result.SYS_FAILD, "您没有权限访问!");
}
} else {
//检查是否在排除的路径中
flag = this.checkUrlIsNotLogin(getUri(request));
if (!flag) {
throw new BusinessException(Result.SYS_FAILD, "您没有权限访问!");
}
}
} else {
throw new BusinessException(Result.SYS_FAILD, "您没有权限访问!");
}
}
}
} catch (BusinessException e) {
log.error("检查权限异常", e);
this.writeJson(e.getCode(), e.getMessage(), response);
return false;
} catch (Exception e) {
this.writeJson(Result.SYS_FAILD, "授权失败!", response);
log.error("检查权限异常", e);
return false;
}
}
return true;
}
/**
* 检查地址是否不需要登录
*
* @param uri url
* @return boolean
*/
private boolean checkUrlIsNotLogin(String uri) {
//检查路径权限
IRedisExtCommands repository = SpringContextHolder.getBean(IRedisExtCommands.class);
log.debug("开始检查请求的URI:{}是否不需要登录", uri);
return isFilterFlag(uri, repository, false, AuthConstant.NOT_LOGIN_VIST_URLS_CACHEKEY);
}
private boolean isFilterFlag(String uri, IRedisExtCommands repository, boolean filterFlag, String cacheExcludeKey) {
List vos = repository.hgetList(redisDataBase, AuthConstant.AUTH_DICT_KEY, cacheExcludeKey, AuthExcludeUrl.class);
if (CollectionUtil.isNotEmpty(vos)) {
filterFlag = vos.parallelStream().anyMatch(vo -> (StringUtil.isNotBlank(vo.getUrl()) && (uri.startsWith(vo.getUrl()) || "*".equals(vo.getUrl()))));
}
return filterFlag;
}
/**
* 写json数据
*
* @param code 状态码
* @param message 信息
* @param response 返回
*/
private void writeJson(int code, String message, HttpServletResponse response) {
try {
Result result = Result.status(code,message);
response.setContentType("application/json;charset=utf-8");
response.setStatus(403);
response.getWriter().write(JsonUtil.toString(result));
response.getWriter().flush();
} catch (Exception ignored) {
}
}
/**
* 检查路径权限
*
* @param userId 用户id
* @param httpRequest http请求
*/
@SuppressWarnings("unchecked")
private boolean checkUrlPower(String userId, HttpServletRequest httpRequest) {
//检查权限排除路径地址
String url = getUri(httpRequest);
log.debug("开始检查请求的URI:{}是否具有访问权限", url);
//检查路径权限
IRedisExtCommands repository = SpringContextHolder.getBean(IRedisExtCommands.class);
String cacheExcludeKey = AuthConstant.EXCLUDE_VIST_URLS_CACHEKEY;
boolean filterFlag = isFilterFlag(url, repository, false, cacheExcludeKey);
if (!filterFlag) {
String cacheKey = AuthConstant.USER_VIST_URL_CACHEKEY;
AuthUrl authUrl = repository.hget(redisDataBase, cacheKey, userId.concat("-").concat(url), AuthUrl.class);
if (authUrl != null) {
filterFlag = true;
}
}
return filterFlag;
}
/**
* Retrieves the current request servlet path.
* Deals with differences between servlet specs (2.2 vs 2.3+)
*
* @param request the request
* @return the servlet path
*/
public static String getServletPath(HttpServletRequest request) {
String servletPath = request.getServletPath();
String requestUri = request.getRequestURI();
// Detecting other characters that the servlet container cut off (like anything after ';')
if (requestUri != null && servletPath != null && !requestUri.endsWith(servletPath)) {
int pos = requestUri.indexOf(servletPath);
if (pos > -1) {
servletPath = requestUri.substring(requestUri.indexOf(servletPath));
}
}
if (null != servletPath && !servletPath.isEmpty()) {
return servletPath;
}
int startIndex = StringUtil.equals(request.getContextPath(), "") ? 0 : request.getContextPath().length();
int endIndex = request.getPathInfo() == null ? Objects.requireNonNull(requestUri).length() : Objects.requireNonNull(requestUri).lastIndexOf(request.getPathInfo());
if (startIndex > endIndex) {
// this should not happen
endIndex = startIndex;
}
return requestUri.substring(startIndex, endIndex);
}
/**
* Gets the uri from the request
*
* @param request The request
* @return The uri
*/
public static String getUri(HttpServletRequest request) {
// handle http dispatcher includes.
String uri = (String) request.getAttribute("jakarta.servlet.include.servlet_path");
if (uri != null) {
return uri;
}
uri = getServletPath(request);
if (uri != null && !uri.isEmpty()) {
return uri;
}
uri = request.getRequestURI();
return uri.substring(request.getContextPath().length());
}
}