init: 导入RuoYi‑Vue‑Plus 6.X完整代码
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-security 安全模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- satoken -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-satoken</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package org.dromara.common.security.config;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.filter.SaTokenContextFilterForJakartaServlet;
|
||||
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.HttpStatus;
|
||||
import org.dromara.common.core.utils.NetUtils;
|
||||
import org.dromara.common.core.utils.ServletUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.common.security.config.properties.SecurityProperties;
|
||||
import org.dromara.common.security.handler.AllUrlHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限安全配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(SecurityProperties.class)
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityConfig implements WebMvcConfigurer {
|
||||
|
||||
private static final String CLIENT_RULE_SEPARATOR_REGEX = "[,;\\r\\n]+";
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
/**
|
||||
* 重新注册 Sa-Token 上下文过滤器,使其覆盖 Servlet 异步分发。
|
||||
* <p>
|
||||
* SSE、WebSocket 握手等场景可能触发 ASYNC/ERROR dispatcher,如果上下文过滤器只处理普通 REQUEST,
|
||||
* 后续统一鉴权或业务代码读取 SaHolder/StpUtil 时会出现 SaTokenContext 未初始化。
|
||||
*
|
||||
* @param filter Sa-Token 官方上下文过滤器
|
||||
* @return 过滤器注册配置
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<SaTokenContextFilterForJakartaServlet> saTokenContextFilterRegistration(
|
||||
SaTokenContextFilterForJakartaServlet filter) {
|
||||
FilterRegistrationBean<SaTokenContextFilterForJakartaServlet> registration = new FilterRegistrationBean<>();
|
||||
registration.setFilter(filter);
|
||||
registration.setName("saTokenContextFilterForServlet");
|
||||
registration.addUrlPatterns("/*");
|
||||
registration.setDispatcherTypes(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR));
|
||||
registration.setAsyncSupported(true);
|
||||
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 Sa-Token 路由拦截器并配置鉴权规则。
|
||||
*
|
||||
* @param registry 拦截器注册器
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册路由拦截器,自定义验证规则
|
||||
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||
AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
|
||||
// 登录验证 -- 排除多个路径
|
||||
SaRouter
|
||||
// 获取所有的
|
||||
.match(allUrlHandler.getUrls())
|
||||
// 对未排除的路径进行检查
|
||||
.check(() -> {
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
HttpServletResponse response = ServletUtils.getResponse();
|
||||
response.setContentType(SaTokenConsts.CONTENT_TYPE_APPLICATION_JSON);
|
||||
// 检查是否登录 是否有token
|
||||
StpUtil.checkLogin();
|
||||
|
||||
// 检查 header 与 param 里的 clientid 与 token 里的是否一致
|
||||
String headerCid = request.getHeader(LoginHelper.CLIENT_KEY);
|
||||
String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY);
|
||||
String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString();
|
||||
if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) {
|
||||
// token 无效
|
||||
throw NotLoginException.newInstance(StpUtil.getLoginType(),
|
||||
"-100", "客户端ID与Token不匹配",
|
||||
StpUtil.getTokenValue());
|
||||
}
|
||||
validateClientAccessRules(request);
|
||||
|
||||
// 有效率影响 用于临时测试
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.info("剩余有效时间: {}", StpUtil.getTokenTimeout());
|
||||
// log.info("临时有效时间: {}", StpUtil.getTokenActivityTimeout());
|
||||
// }
|
||||
|
||||
});
|
||||
})).addPathPatterns("/**")
|
||||
// 排除不需要拦截的路径
|
||||
.excludePathPatterns(securityProperties.getExcludes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 actuator 健康检查接口配置 Basic Auth 鉴权过滤器。
|
||||
*
|
||||
* @return Sa-Token Servlet 过滤器
|
||||
*/
|
||||
@Bean
|
||||
public SaServletFilter getSaServletFilter() {
|
||||
String username = SpringUtils.getProperty("spring.boot.admin.client.username");
|
||||
String password = SpringUtils.getProperty("spring.boot.admin.client.password");
|
||||
return new SaServletFilter()
|
||||
.addInclude("/actuator", "/actuator/**")
|
||||
.setAuth(obj -> {
|
||||
SaHttpBasicUtil.check(username + StringUtils.COLON + password);
|
||||
})
|
||||
.setError(e -> {
|
||||
HttpServletResponse response = ServletUtils.getResponse();
|
||||
response.setContentType(SaTokenConsts.CONTENT_TYPE_APPLICATION_JSON);
|
||||
return SaResult.error(e.getMessage()).setCode(HttpStatus.UNAUTHORIZED);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按客户端配置校验接口访问路径与来源 IP。
|
||||
*
|
||||
* @param request 当前请求
|
||||
*/
|
||||
private void validateClientAccessRules(HttpServletRequest request) {
|
||||
String requestPath = StringUtils.blankToDefault(request.getServletPath(), request.getRequestURI());
|
||||
String accessPath = getTokenExtra(LoginHelper.CLIENT_ACCESS_PATH_KEY);
|
||||
if (StringUtils.isNotBlank(accessPath)) {
|
||||
List<String> accessPathList = StringUtils.str2List(accessPath, CLIENT_RULE_SEPARATOR_REGEX, true, true);
|
||||
if (!StringUtils.matches(requestPath, accessPathList)) {
|
||||
throw new NotPermissionException("当前客户端未授权访问该接口路径");
|
||||
}
|
||||
}
|
||||
|
||||
String ipWhitelist = getTokenExtra(LoginHelper.CLIENT_IP_WHITELIST_KEY);
|
||||
if (StringUtils.isNotBlank(ipWhitelist)) {
|
||||
String clientIp = ServletUtils.getClientIP(request);
|
||||
List<String> ipWhitelistList = StringUtils.str2List(ipWhitelist, CLIENT_RULE_SEPARATOR_REGEX, true, true);
|
||||
boolean matched = ipWhitelistList.stream().anyMatch(rule -> NetUtils.isMatchIpRule(rule, clientIp));
|
||||
if (!matched) {
|
||||
throw new NotPermissionException("当前客户端IP不在白名单内");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 token 扩展信息,兼容空值场景。
|
||||
*
|
||||
* @param key 扩展字段
|
||||
* @return 扩展值
|
||||
*/
|
||||
private String getTokenExtra(String key) {
|
||||
Object extra = StpUtil.getExtra(key);
|
||||
return extra == null ? null : extra.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.dromara.common.security.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Security 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "security")
|
||||
public class SecurityProperties {
|
||||
|
||||
/**
|
||||
* 排除路径
|
||||
*/
|
||||
private String[] excludes;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package org.dromara.common.security.handler;
|
||||
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 获取所有Url配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
public class AllUrlHandler implements InitializingBean {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
|
||||
|
||||
private List<String> urls = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 初始化并收集系统中的全部请求路径。
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Set<String> set = new HashSet<>();
|
||||
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
map.keySet().forEach(info -> {
|
||||
// 获取注解上边的 path 替代 path variable 为 *
|
||||
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
|
||||
.forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
|
||||
});
|
||||
urls.addAll(set);
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
org.dromara.common.security.handler.AllUrlHandler
|
||||
org.dromara.common.security.config.SecurityConfig
|
||||
Reference in New Issue
Block a user