mirror of
https://gitee.com/dromara/RuoYi-Cloud-Plus.git
synced 2026-04-23 02:48:34 +08:00
update 优化 客户端管理 增加白名单路径和白名单IP功能 可限制客户端能访问的具体路径与可访问的具体IP地址
This commit is contained in:
@@ -52,6 +52,16 @@ public class RemoteClientVo implements Serializable {
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
private String accessPath;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
private String ipWhitelist;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package org.dromara.auth.service;
|
||||
|
||||
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.dromara.auth.domain.vo.LoginVo;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import org.dromara.system.api.domain.vo.RemoteClientVo;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 授权策略
|
||||
*
|
||||
@@ -32,6 +37,30 @@ public interface IAuthStrategy {
|
||||
return instance.login(body, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按客户端配置构建统一登录参数。
|
||||
*/
|
||||
static SaLoginParameter buildLoginParameter(RemoteClientVo client) {
|
||||
return buildLoginParameter(client, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按客户端配置构建统一登录参数,并预留自定义扩展入口。
|
||||
*/
|
||||
static SaLoginParameter buildLoginParameter(RemoteClientVo client, Consumer<SaLoginParameter> customizer) {
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
model.setExtra(LoginHelper.CLIENT_ACCESS_PATH_KEY, client.getAccessPath());
|
||||
model.setExtra(LoginHelper.CLIENT_IP_WHITELIST_KEY, client.getIpWhitelist());
|
||||
if (ObjectUtil.isNotNull(customizer)) {
|
||||
customizer.accept(model);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
|
||||
@@ -49,13 +49,7 @@ public class EmailAuthStrategy implements IAuthStrategy {
|
||||
loginService.checkLogin(LoginType.EMAIL, loginUser.getUsername(), () -> !validateEmailCode(email, emailCode));
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
SaLoginParameter model = IAuthStrategy.buildLoginParameter(client);
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
|
||||
@@ -61,13 +61,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
||||
loginService.checkLogin(LoginType.PASSWORD, username, () -> !BCrypt.checkpw(password, loginUser.getPassword()));
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
SaLoginParameter model = IAuthStrategy.buildLoginParameter(client);
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
|
||||
@@ -49,13 +49,7 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
||||
loginService.checkLogin(LoginType.SMS, loginUser.getUsername(), () -> !validateSmsCode(phoneNumber, smsCode));
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
SaLoginParameter model = IAuthStrategy.buildLoginParameter(client);
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
|
||||
@@ -69,13 +69,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
||||
LoginUser loginUser = remoteUserService.getUserInfo(socialVo.getUserId());
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
SaLoginParameter model = IAuthStrategy.buildLoginParameter(client);
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
|
||||
@@ -70,13 +70,7 @@ public class XcxAuthStrategy implements IAuthStrategy {
|
||||
loginUser.setClientKey(client.getClientKey());
|
||||
loginUser.setDeviceType(client.getDeviceType());
|
||||
|
||||
SaLoginParameter model = new SaLoginParameter();
|
||||
model.setDeviceType(client.getDeviceType());
|
||||
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
|
||||
// 例如: 后台用户30分钟过期 app用户1天过期
|
||||
model.setTimeout(client.getTimeout());
|
||||
model.setActiveTimeout(client.getActiveTimeout());
|
||||
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
|
||||
SaLoginParameter model = IAuthStrategy.buildLoginParameter(client);
|
||||
// 生成token
|
||||
LoginHelper.login(loginUser, model);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.regex.RegexUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -52,7 +53,8 @@ public class NetUtils extends NetUtil {
|
||||
public static boolean isInnerIPv6(String ip) {
|
||||
try {
|
||||
// 判断是否为IPv6地址
|
||||
if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) {
|
||||
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||
if (inetAddress instanceof Inet6Address inet6Address) {
|
||||
// isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断
|
||||
// isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断
|
||||
// isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机
|
||||
@@ -81,4 +83,69 @@ public class NetUtils extends NetUtil {
|
||||
return RegexUtils.isMatch(PatternPool.IPV4, ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配IP规则,支持精确值、通配符与CIDR。
|
||||
*
|
||||
* @param rule IP规则
|
||||
* @param clientIp 客户端IP
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean isMatchIpRule(String rule, String clientIp) {
|
||||
if (StringUtils.isBlank(rule) || StringUtils.isBlank(clientIp)) {
|
||||
return false;
|
||||
}
|
||||
String ipRule = StringUtils.trim(rule);
|
||||
if (StringUtils.equals(ipRule, clientIp)) {
|
||||
return true;
|
||||
}
|
||||
if (ipRule.contains("/")) {
|
||||
return isMatchCidr(ipRule, clientIp);
|
||||
}
|
||||
if (StringUtils.containsAny(ipRule, "*", "?")) {
|
||||
String regex = ipRule
|
||||
.replace(".", "\\.")
|
||||
.replace("*", ".*")
|
||||
.replace("?", ".");
|
||||
return clientIp.matches(regex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配CIDR网段。
|
||||
*
|
||||
* @param cidr CIDR规则
|
||||
* @param clientIp 客户端IP
|
||||
* @return 是否命中
|
||||
*/
|
||||
public static boolean isMatchCidr(String cidr, String clientIp) {
|
||||
try {
|
||||
String[] parts = cidr.split("/");
|
||||
if (parts.length != 2) {
|
||||
return false;
|
||||
}
|
||||
InetAddress networkAddress = InetAddress.getByName(parts[0]);
|
||||
InetAddress currentAddress = InetAddress.getByName(clientIp);
|
||||
byte[] networkBytes = networkAddress.getAddress();
|
||||
byte[] currentBytes = currentAddress.getAddress();
|
||||
if (networkBytes.length != currentBytes.length) {
|
||||
return false;
|
||||
}
|
||||
int prefixLength = Integer.parseInt(parts[1]);
|
||||
int maxPrefix = networkBytes.length * 8;
|
||||
if (prefixLength < 0 || prefixLength > maxPrefix) {
|
||||
return false;
|
||||
}
|
||||
BigInteger mask = prefixLength == 0
|
||||
? BigInteger.ZERO
|
||||
: BigInteger.ONE.shiftLeft(prefixLength).subtract(BigInteger.ONE).shiftLeft(maxPrefix - prefixLength);
|
||||
BigInteger network = new BigInteger(1, networkBytes);
|
||||
BigInteger current = new BigInteger(1, currentBytes);
|
||||
return network.and(mask).equals(current.and(mask));
|
||||
} catch (UnknownHostException | NumberFormatException e) {
|
||||
log.debug("IP白名单CIDR规则解析失败: {}", cidr, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ public class LoginHelper {
|
||||
public static final String DEPT_NAME_KEY = "deptName";
|
||||
public static final String DEPT_CATEGORY_KEY = "deptCategory";
|
||||
public static final String CLIENT_KEY = "clientid";
|
||||
public static final String CLIENT_ACCESS_PATH_KEY = "clientAccessPath";
|
||||
public static final String CLIENT_IP_WHITELIST_KEY = "clientIpWhitelist";
|
||||
|
||||
/**
|
||||
* 登录系统 基于 设备类型
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.dromara.gateway.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.filter.SaServletFilter;
|
||||
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
@@ -8,21 +9,21 @@ 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.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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.gateway.config.properties.IgnoreWhiteProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 拦截器配置
|
||||
@@ -32,6 +33,8 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
@Configuration
|
||||
public class AuthFilter implements WebMvcConfigurer {
|
||||
|
||||
private static final String CLIENT_RULE_SEPARATOR_REGEX = "[,;\\r\\n]+";
|
||||
|
||||
private final IgnoreWhiteProperties ignoreWhite;
|
||||
|
||||
public AuthFilter(IgnoreWhiteProperties ignoreWhite) {
|
||||
@@ -60,6 +63,7 @@ public class AuthFilter implements WebMvcConfigurer {
|
||||
"-100", "客户端ID与Token不匹配",
|
||||
StpUtil.getTokenValue());
|
||||
}
|
||||
validateClientAccessRules(request);
|
||||
})))
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns("/favicon.ico", "/actuator", "/actuator/**", "/resource/sse" , "/error");
|
||||
@@ -86,4 +90,36 @@ public class AuthFilter implements WebMvcConfigurer {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 按客户端配置校验接口访问路径与来源IP。
|
||||
*/
|
||||
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扩展信息,兼容空值场景。
|
||||
*/
|
||||
private String getTokenExtra(String key) {
|
||||
Object extra = StpUtil.getExtra(key);
|
||||
return extra == null ? null : extra.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,6 +54,16 @@ public class SysClient extends BaseEntity {
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
private String accessPath;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
private String ipWhitelist;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,26 @@ public class SysClientBo implements Serializable {
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
private String accessPath;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
private List<String> accessPathList;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
private String ipWhitelist;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
private List<String> ipWhitelistList;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
|
||||
@@ -66,6 +66,28 @@ public class SysClientVo implements Serializable {
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 允许访问路径
|
||||
*/
|
||||
@ExcelProperty(value = "允许访问路径")
|
||||
private String accessPath;
|
||||
|
||||
/**
|
||||
* 允许访问路径列表
|
||||
*/
|
||||
private List<String> accessPathList;
|
||||
|
||||
/**
|
||||
* IP白名单
|
||||
*/
|
||||
@ExcelProperty(value = "IP白名单")
|
||||
private String ipWhitelist;
|
||||
|
||||
/**
|
||||
* IP白名单列表
|
||||
*/
|
||||
private List<String> ipWhitelistList;
|
||||
|
||||
/**
|
||||
* token活跃超时时间
|
||||
*/
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* 客户端管理Service业务层处理
|
||||
@@ -36,6 +37,8 @@ import java.util.List;
|
||||
@Service
|
||||
public class SysClientServiceImpl implements ISysClientService {
|
||||
|
||||
private static final String CLIENT_RULE_SEPARATOR_REGEX = "[,;\\r\\n]+";
|
||||
|
||||
private final SysClientMapper baseMapper;
|
||||
|
||||
/**
|
||||
@@ -44,7 +47,7 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
@Override
|
||||
public SysClientVo queryById(Long id) {
|
||||
SysClientVo vo = baseMapper.selectVoById(id);
|
||||
vo.setGrantTypeList(StringUtils.splitList(vo.getGrantType()));
|
||||
fillClientRuleFields(vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@@ -54,7 +57,9 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
@Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId")
|
||||
@Override
|
||||
public SysClientVo queryByClientId(String clientId) {
|
||||
return baseMapper.selectVoOne(new LambdaQueryWrapper<SysClient>().eq(SysClient::getClientId, clientId));
|
||||
SysClientVo vo = baseMapper.selectVoOne(new LambdaQueryWrapper<SysClient>().eq(SysClient::getClientId, clientId));
|
||||
fillClientRuleFields(vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +69,7 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
public PageResult<SysClientVo> queryPageList(SysClientBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SysClient> lqw = buildQueryWrapper(bo);
|
||||
Page<SysClientVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
result.getRecords().forEach(r -> r.setGrantTypeList(StringUtils.splitList(r.getGrantType())));
|
||||
result.getRecords().forEach(this::fillClientRuleFields);
|
||||
return PageResult.build(result.getRecords(), result.getTotal());
|
||||
}
|
||||
|
||||
@@ -74,7 +79,9 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
@Override
|
||||
public List<SysClientVo> queryList(SysClientBo bo) {
|
||||
LambdaQueryWrapper<SysClient> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
List<SysClientVo> list = baseMapper.selectVoList(lqw);
|
||||
list.forEach(this::fillClientRuleFields);
|
||||
return list;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SysClient> buildQueryWrapper(SysClientBo bo) {
|
||||
@@ -94,6 +101,8 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
public Boolean insertByBo(SysClientBo bo) {
|
||||
SysClient add = MapstructUtils.convert(bo, SysClient.class);
|
||||
add.setGrantType(CollUtil.join(bo.getGrantTypeList(), StringUtils.SEPARATOR));
|
||||
add.setAccessPath(resolveRuleValue(bo.getAccessPath(), bo.getAccessPathList(), this::normalizeAccessPath));
|
||||
add.setIpWhitelist(resolveRuleValue(bo.getIpWhitelist(), bo.getIpWhitelistList(), UnaryOperator.identity()));
|
||||
// 生成clientId
|
||||
String clientKey = bo.getClientKey();
|
||||
String clientSecret = bo.getClientSecret();
|
||||
@@ -113,6 +122,8 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
public Boolean updateByBo(SysClientBo bo) {
|
||||
SysClient update = MapstructUtils.convert(bo, SysClient.class);
|
||||
update.setGrantType(StringUtils.joinComma(bo.getGrantTypeList()));
|
||||
update.setAccessPath(resolveRuleValue(bo.getAccessPath(), bo.getAccessPathList(), this::normalizeAccessPath));
|
||||
update.setIpWhitelist(resolveRuleValue(bo.getIpWhitelist(), bo.getIpWhitelistList(), UnaryOperator.identity()));
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@@ -151,4 +162,59 @@ public class SysClientServiceImpl implements ISysClientService {
|
||||
return !exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回填客户端扩展规则字段,便于前端直接展示和编辑。
|
||||
*/
|
||||
private void fillClientRuleFields(SysClientVo vo) {
|
||||
if (ObjectUtil.isNull(vo)) {
|
||||
return;
|
||||
}
|
||||
vo.setGrantTypeList(StringUtils.splitList(vo.getGrantType()));
|
||||
vo.setAccessPathList(parseRuleList(vo.getAccessPath(), this::normalizeAccessPath));
|
||||
vo.setIpWhitelistList(parseRuleList(vo.getIpWhitelist(), UnaryOperator.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理白名单与路径规则的入库格式。
|
||||
*/
|
||||
private String resolveRuleValue(String rawValue, List<String> listValue, UnaryOperator<String> normalizer) {
|
||||
List<String> rules = CollUtil.isNotEmpty(listValue)
|
||||
? listValue
|
||||
: StringUtils.str2List(rawValue, CLIENT_RULE_SEPARATOR_REGEX, true, true);
|
||||
if (CollUtil.isEmpty(rules)) {
|
||||
return listValue != null || rawValue != null ? "" : null;
|
||||
}
|
||||
return CollUtil.join(rules.stream()
|
||||
.map(normalizer)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toList(), StringUtils.SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将规则串转换为列表。
|
||||
*/
|
||||
private List<String> parseRuleList(String value, UnaryOperator<String> normalizer) {
|
||||
return StringUtils.str2List(value, CLIENT_RULE_SEPARATOR_REGEX, true, true).stream()
|
||||
.map(normalizer)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一补齐路径前导斜杠,避免配置成 app/** 时无法命中。
|
||||
*/
|
||||
private String normalizeAccessPath(String path) {
|
||||
if (StringUtils.isBlank(path)) {
|
||||
return null;
|
||||
}
|
||||
String accessPath = StringUtils.trim(path);
|
||||
if (StringUtils.isBlank(accessPath)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.equals(accessPath, "*") || StringUtils.equals(accessPath, "/**")) {
|
||||
return "/**";
|
||||
}
|
||||
return accessPath.startsWith(StringUtils.SLASH) ? accessPath : StringUtils.SLASH + accessPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1286,6 +1286,8 @@ create table sys_client (
|
||||
client_secret varchar2(255) default null,
|
||||
grant_type varchar2(255) default null,
|
||||
device_type varchar2(32) default null,
|
||||
access_path varchar2(1024) default null,
|
||||
ip_whitelist varchar2(1024) default null,
|
||||
active_timeout number(11) default 1800,
|
||||
timeout number(11) default 604800,
|
||||
status char(1) default '0',
|
||||
@@ -1306,6 +1308,8 @@ comment on column sys_client.client_key is '客户端key';
|
||||
comment on column sys_client.client_secret is '客户端秘钥';
|
||||
comment on column sys_client.grant_type is '授权类型';
|
||||
comment on column sys_client.device_type is '设备类型';
|
||||
comment on column sys_client.access_path is '允许访问路径';
|
||||
comment on column sys_client.ip_whitelist is 'IP白名单';
|
||||
comment on column sys_client.active_timeout is 'token活跃超时时间';
|
||||
comment on column sys_client.timeout is 'token固定超时';
|
||||
comment on column sys_client.status is '状态(0正常 1停用)';
|
||||
@@ -1316,8 +1320,8 @@ comment on column sys_client.create_time is '创建时间';
|
||||
comment on column sys_client.update_by is '更新者';
|
||||
comment on column sys_client.update_time is '更新时间';
|
||||
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate, 1761100000000000001, sysdate);
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate, 1761100000000000001, sysdate);
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate, 1761100000000000001, sysdate);
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate, 1761100000000000001, sysdate);
|
||||
|
||||
create table test_demo (
|
||||
id number(20) not null,
|
||||
|
||||
@@ -1285,6 +1285,8 @@ create table sys_client (
|
||||
client_secret varchar(255) default ''::varchar,
|
||||
grant_type varchar(255) default ''::varchar,
|
||||
device_type varchar(32) default ''::varchar,
|
||||
access_path varchar(1024) default ''::varchar,
|
||||
ip_whitelist varchar(1024) default ''::varchar,
|
||||
active_timeout int4 default 1800,
|
||||
timeout int4 default 604800,
|
||||
status char(1) default '0'::bpchar,
|
||||
@@ -1304,6 +1306,8 @@ comment on column sys_client.client_key is '客户端key';
|
||||
comment on column sys_client.client_secret is '客户端秘钥';
|
||||
comment on column sys_client.grant_type is '授权类型';
|
||||
comment on column sys_client.device_type is '设备类型';
|
||||
comment on column sys_client.access_path is '允许访问路径';
|
||||
comment on column sys_client.ip_whitelist is 'IP白名单';
|
||||
comment on column sys_client.active_timeout is 'token活跃超时时间';
|
||||
comment on column sys_client.timeout is 'token固定超时';
|
||||
comment on column sys_client.status is '状态(0正常 1停用)';
|
||||
@@ -1314,8 +1318,8 @@ comment on column sys_client.create_time is '创建时间';
|
||||
comment on column sys_client.update_by is '更新者';
|
||||
comment on column sys_client.update_time is '更新时间';
|
||||
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, now(), 1761100000000000001, now());
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, now(), 1761100000000000001, now());
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, now(), 1761100000000000001, now());
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, now(), 1761100000000000001, now());
|
||||
|
||||
create table if not exists test_demo
|
||||
(
|
||||
|
||||
@@ -929,6 +929,8 @@ create table sys_client (
|
||||
client_secret varchar(255) default null comment '客户端秘钥',
|
||||
grant_type varchar(255) default null comment '授权类型',
|
||||
device_type varchar(32) default null comment '设备类型',
|
||||
access_path varchar(1024) default null comment '允许访问路径',
|
||||
ip_whitelist varchar(1024) default null comment 'IP白名单',
|
||||
active_timeout int(11) default 1800 comment 'token活跃超时时间',
|
||||
timeout int(11) default 604800 comment 'token固定超时',
|
||||
status char(1) default '0' comment '状态(0正常 1停用)',
|
||||
@@ -941,8 +943,8 @@ create table sys_client (
|
||||
primary key (id)
|
||||
) engine=innodb comment='系统授权表';
|
||||
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate(), 1761100000000000001, sysdate());
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate(), 1761100000000000001, sysdate());
|
||||
insert into sys_client values (1762000000000000001, 'e5cd7e4891bf95d1d19206ce24a7b32e', 'pc', 'pc123', 'password,social', 'pc', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate(), 1761100000000000001, sysdate());
|
||||
insert into sys_client values (1762000000000000002, '428a8310cd442757ae699df5d894f051', 'app', 'app123', 'password,sms,social', 'android', null, null, 1800, 604800, 0, 0, 1761000000000000103, 1761100000000000001, sysdate(), 1761100000000000001, sysdate());
|
||||
|
||||
|
||||
CREATE TABLE test_demo
|
||||
|
||||
Reference in New Issue
Block a user