Compare commits

...

4 Commits

25 changed files with 712 additions and 133 deletions

View File

@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<img src="https://img.shields.io/badge/Release-V4.3.0-green.svg" alt="Downloads"> <img src="https://img.shields.io/badge/Release-V4.5.0-green.svg" alt="Downloads">
<img src="https://img.shields.io/badge/JDK-17+-green.svg" alt="Build Status"> <img src="https://img.shields.io/badge/JDK-17+-green.svg" alt="Build Status">
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status"> <img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
<img src="https://img.shields.io/badge/Spring%20Cloud-2023-blue.svg" alt="Coverage Status"> <img src="https://img.shields.io/badge/Spring%20Cloud-2023-blue.svg" alt="Coverage Status">
@ -33,8 +33,8 @@
|----------------------|------------| |----------------------|------------|
| Java | 17+ | | Java | 17+ |
| NodeJS | 18+ | | NodeJS | 18+ |
| Spring | 6.1.14 | | Spring | 6.1.15 |
| Spring Boot | 3.2.10 | | Spring Boot | 3.2.12 |
| Spring Cloud | 2023.0.3 | | Spring Cloud | 2023.0.3 |
| Spring Cloud Alibaba | 2023.0.1.2 | | Spring Cloud Alibaba | 2023.0.1.2 |
| Nacos Alibaba | 2.3.2 | | Nacos Alibaba | 2.3.2 |
@ -81,19 +81,20 @@ SpringBlade
## 官方产品 ## 官方产品
| 简介 | 演示地址 | | 简介 | 演示地址 |
|---------------|------------------------------------------------------| |-----------------|------------------------------------------------------|
| BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) | | BladeX企业级开发平台 | [https://saber3.bladex.cn](https://saber3.bladex.cn) |
| BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) | | BladeX可视化数据大屏 | [https://data.bladex.cn](https://data.bladex.cn) |
| BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) | | BladeX物联网开发平台 | [https://iot.bladex.cn](https://iot.bladex.cn) |
| BladeXAI大模型平台 | [https://aigc.bladex.cn/](https://aigc.bladex.cn/) |
## 前端项目 ## 前端项目
| 简介 | 地址 | | 简介 | 地址 |
|--------------------|----------------------------------------------------------------------------------------------------| |--------------------|------------------------------------------------------------------------------|
| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) | | 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber) |
| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber) | | 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber2](https://gitee.com/smallc/Saber/tree/vue2/) |
| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber/tree/3.x/) | | 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
## 后端项目 ## 后端项目
| 简介 | 地址 | | 简介 | 地址 |

View File

@ -37,6 +37,10 @@
<groupId>org.springblade</groupId> <groupId>org.springblade</groupId>
<artifactId>blade-starter-social</artifactId> <artifactId>blade-starter-social</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springblade</groupId> <groupId>org.springblade</groupId>
<artifactId>blade-user-api</artifactId> <artifactId>blade-user-api</artifactId>
@ -45,6 +49,10 @@
<groupId>org.springblade</groupId> <groupId>org.springblade</groupId>
<artifactId>blade-starter-swagger</artifactId> <artifactId>blade-starter-swagger</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot-autoconfigure</artifactId>
</dependency>
<!-- Captcha --> <!-- Captcha -->
<dependency> <dependency>
<groupId>com.github.whvcse</groupId> <groupId>com.github.whvcse</groupId>

View File

@ -25,11 +25,11 @@ import org.springblade.auth.granter.TokenGranterBuilder;
import org.springblade.auth.granter.TokenParameter; import org.springblade.auth.granter.TokenParameter;
import org.springblade.auth.utils.TokenUtil; import org.springblade.auth.utils.TokenUtil;
import org.springblade.common.cache.CacheNames; import org.springblade.common.cache.CacheNames;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.AuthInfo; import org.springblade.core.secure.AuthInfo;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv; import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.RedisUtil;
import org.springblade.core.tool.utils.WebUtil; import org.springblade.core.tool.utils.WebUtil;
import org.springblade.system.user.entity.UserInfo; import org.springblade.system.user.entity.UserInfo;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -50,7 +50,7 @@ import java.util.concurrent.TimeUnit;
@Tag(name = "用户授权认证", description = "授权接口") @Tag(name = "用户授权认证", description = "授权接口")
public class AuthController { public class AuthController {
private RedisUtil redisUtil; private BladeRedis bladeRedis;
@PostMapping("token") @PostMapping("token")
@Operation(summary = "获取认证token", description = "传入租户ID:tenantId,账号:account,密码:password") @Operation(summary = "获取认证token", description = "传入租户ID:tenantId,账号:account,密码:password")
@ -87,7 +87,7 @@ public class AuthController {
String verCode = specCaptcha.text().toLowerCase(); String verCode = specCaptcha.text().toLowerCase();
String key = UUID.randomUUID().toString(); String key = UUID.randomUUID().toString();
// 存入redis并设置过期时间为30分钟 // 存入redis并设置过期时间为30分钟
redisUtil.set(CacheNames.CAPTCHA_KEY + key, verCode, 30L, TimeUnit.MINUTES); bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, 30L, TimeUnit.MINUTES);
// 将key和base64返回给前端 // 将key和base64返回给前端
return R.data(Kv.init().set("key", key).set("image", specCaptcha.toBase64())); return R.data(Kv.init().set("key", key).set("image", specCaptcha.toBase64()));
} }

View File

@ -16,10 +16,12 @@
package org.springblade.auth.granter; package org.springblade.auth.granter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.auth.enums.BladeUserEnum; import org.springblade.auth.enums.BladeUserEnum;
import org.springblade.auth.utils.TokenUtil; import org.springblade.auth.utils.TokenUtil;
import org.springblade.common.cache.CacheNames; import org.springblade.common.cache.CacheNames;
import org.springblade.core.log.exception.ServiceException; import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.props.BladeAuthProperties; import org.springblade.core.secure.props.BladeAuthProperties;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.*; import org.springblade.core.tool.utils.*;
@ -29,19 +31,23 @@ import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.time.Duration;
/** /**
* 验证码TokenGranter * 验证码TokenGranter
* *
* @author Chill * @author Chill
*/ */
@Slf4j
@Component @Component
@AllArgsConstructor @AllArgsConstructor
public class CaptchaTokenGranter implements ITokenGranter { public class CaptchaTokenGranter implements ITokenGranter {
public static final String GRANT_TYPE = "captcha"; public static final String GRANT_TYPE = "captcha";
public static final Integer FAIL_COUNT = 5;
private IUserClient userClient; private IUserClient userClient;
private RedisUtil redisUtil; private BladeRedis bladeRedis;
private BladeAuthProperties authProperties; private BladeAuthProperties authProperties;
@ -52,7 +58,7 @@ public class CaptchaTokenGranter implements ITokenGranter {
String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY); String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE); String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
// 获取验证码 // 获取验证码
String redisCode = String.valueOf(redisUtil.get(CacheNames.CAPTCHA_KEY + key)); String redisCode = Func.toStr(bladeRedis.getAndDel(CacheNames.CAPTCHA_KEY + key));
// 判断验证码 // 判断验证码
if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) { if (code == null || !StringUtil.equalsIgnoreCase(redisCode, code)) {
throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT); throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT);
@ -61,6 +67,14 @@ public class CaptchaTokenGranter implements ITokenGranter {
String tenantId = tokenParameter.getArgs().getStr("tenantId"); String tenantId = tokenParameter.getArgs().getStr("tenantId");
String account = tokenParameter.getArgs().getStr("account"); String account = tokenParameter.getArgs().getStr("account");
String password = tokenParameter.getArgs().getStr("password"); String password = tokenParameter.getArgs().getStr("password");
// 判断登录是否锁定
int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account)), 0);
if (cnt >= FAIL_COUNT) {
log.error("用户登录失败次数过多, 账号:{}, IP:{}", account, WebUtil.getIP());
throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
}
UserInfo userInfo = null; UserInfo userInfo = null;
if (Func.isNoneBlank(account, password)) { if (Func.isNoneBlank(account, password)) {
// 获取用户类型 // 获取用户类型
@ -79,6 +93,14 @@ public class CaptchaTokenGranter implements ITokenGranter {
} }
userInfo = result.isSuccess() ? result.getData() : null; userInfo = result.isSuccess() ? result.getData() : null;
} }
if (userInfo == null || userInfo.getUser() == null) {
// 增加错误锁定次数
bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account), cnt + 1, Duration.ofMinutes(30));
} else {
// 成功则清除登录缓存
bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account));
}
return userInfo; return userInfo;
} }

View File

@ -16,28 +16,38 @@
package org.springblade.auth.granter; package org.springblade.auth.granter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.auth.enums.BladeUserEnum; import org.springblade.auth.enums.BladeUserEnum;
import org.springblade.auth.utils.TokenUtil; import org.springblade.auth.utils.TokenUtil;
import org.springblade.common.cache.CacheNames;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.props.BladeAuthProperties; import org.springblade.core.secure.props.BladeAuthProperties;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DigestUtil; import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.system.user.entity.UserInfo; import org.springblade.system.user.entity.UserInfo;
import org.springblade.system.user.feign.IUserClient; import org.springblade.system.user.feign.IUserClient;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
/** /**
* PasswordTokenGranter * PasswordTokenGranter
* *
* @author Chill * @author Chill
*/ */
@Slf4j
@Component @Component
@AllArgsConstructor @AllArgsConstructor
public class PasswordTokenGranter implements ITokenGranter { public class PasswordTokenGranter implements ITokenGranter {
public static final String GRANT_TYPE = "password"; public static final String GRANT_TYPE = "password";
public static final Integer FAIL_COUNT = 5;
private IUserClient userClient; private IUserClient userClient;
private BladeRedis bladeRedis;
private BladeAuthProperties authProperties; private BladeAuthProperties authProperties;
@ -46,6 +56,14 @@ public class PasswordTokenGranter implements ITokenGranter {
String tenantId = tokenParameter.getArgs().getStr("tenantId"); String tenantId = tokenParameter.getArgs().getStr("tenantId");
String account = tokenParameter.getArgs().getStr("account"); String account = tokenParameter.getArgs().getStr("account");
String password = tokenParameter.getArgs().getStr("password"); String password = tokenParameter.getArgs().getStr("password");
// 判断登录是否锁定
int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account)), 0);
if (cnt >= FAIL_COUNT) {
log.error("用户登录失败次数过多, 账号:{}, IP:{}", account, WebUtil.getIP());
throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
}
UserInfo userInfo = null; UserInfo userInfo = null;
if (Func.isNoneBlank(account, password)) { if (Func.isNoneBlank(account, password)) {
// 获取用户类型 // 获取用户类型
@ -64,6 +82,14 @@ public class PasswordTokenGranter implements ITokenGranter {
} }
userInfo = result.isSuccess() ? result.getData() : null; userInfo = result.isSuccess() ? result.getData() : null;
} }
if (userInfo == null || userInfo.getUser() == null) {
// 增加错误锁定次数
bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account), cnt + 1, Duration.ofMinutes(30));
} else {
// 成功则清除登录缓存
bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, account));
}
return userInfo; return userInfo;
} }

View File

@ -47,6 +47,7 @@ public class TokenUtil {
public final static String HEADER_KEY = "Authorization"; public final static String HEADER_KEY = "Authorization";
public final static String HEADER_PREFIX = "Basic "; public final static String HEADER_PREFIX = "Basic ";
public final static String ENCRYPT_PREFIX = "04"; public final static String ENCRYPT_PREFIX = "04";
public final static String USER_HAS_TOO_MANY_FAILS = "用户登录失败次数过多";
public final static String DEFAULT_AVATAR = "https://bladex.cn/images/logo.png"; public final static String DEFAULT_AVATAR = "https://bladex.cn/images/logo.png";
/** /**

View File

@ -27,6 +27,26 @@ public interface CacheNames {
String DICT_VALUE = "dict:value"; String DICT_VALUE = "dict:value";
String DICT_LIST = "dict:list"; String DICT_LIST = "dict:list";
String CAPTCHA_KEY = "blade:auth::captcha:"; /**
* 验证码key
*/
String CAPTCHA_KEY = "blade:auth::blade:captcha:";
/**
* 登录失败key
*/
String USER_FAIL_KEY = "blade:user::blade:fail:";
/**
* 返回租户格式的key
*
* @param tenantId 租户编号
* @param cacheKey 缓存key
* @param cacheKeyValue 缓存key值
* @return tenantKey
*/
static String tenantKey(String tenantId, String cacheKey, String cacheKeyValue) {
return tenantId.concat(":").concat(cacheKey).concat(cacheKeyValue);
}
} }

View File

@ -18,20 +18,13 @@ package org.springblade.gateway.config;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springblade.gateway.filter.GatewayFilter;
import org.springblade.gateway.props.AuthProperties; import org.springblade.gateway.props.AuthProperties;
import org.springblade.gateway.props.RequestProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/** /**
* 路由配置信息 * 路由配置信息
@ -41,41 +34,15 @@ import reactor.core.publisher.Mono;
@Slf4j @Slf4j
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@AllArgsConstructor @AllArgsConstructor
@EnableConfigurationProperties({AuthProperties.class}) @EnableConfigurationProperties({AuthProperties.class, RequestProperties.class})
public class RouterFunctionConfiguration { public class RouterFunctionConfiguration {
/** /**
* 这里为支持的请求头如果有自定义的header字段请自己添加 * 全局配置
*/
private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion";
private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
private static final String ALLOWED_ORIGIN = "*";
private static final String ALLOWED_EXPOSE = "*";
private static final String MAX_AGE = "18000L";
/**
* 跨域配置
*/ */
@Bean @Bean
public WebFilter corsFilter() { public WebFilter gatewayFilter(RequestProperties requestProperties) {
return (ServerWebExchange ctx, WebFilterChain chain) -> { return new GatewayFilter(requestProperties);
ServerHttpRequest request = ctx.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = ctx.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(ctx);
};
} }
} }

View File

@ -0,0 +1,161 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.gateway.filter;
import lombok.RequiredArgsConstructor;
import org.springblade.gateway.props.RequestProperties;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Objects;
/**
* 全局拦截器
*
* @author Chill
*/
@RequiredArgsConstructor
public class GatewayFilter implements WebFilter, Ordered {
/**
* 请求配置
*/
private final RequestProperties requestProperties;
/**
* 路径匹配
*/
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
/**
* 默认拦截地址
*/
private final List<String> defaultBlockUrl = List.of("/**/actuator/**", "/health/**");
/**
* 默认白名单
*/
private final List<String> defaultWhiteList = List.of("127.0.0.1", "172.30.*.*", "192.168.*.*", "10.*.*.*", "0:0:0:0:0:0:0:1");
/**
* 默认提示信息
*/
private final static String DEFAULT_MESSAGE = "当前请求被拒绝,请联系管理员!";
/**
* 这里为支持的请求头如果有自定义的header字段请自己添加
*/
private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion";
private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
private static final String ALLOWED_ORIGIN = "*";
private static final String ALLOWED_EXPOSE = "*";
private static final String MAX_AGE = "18000L";
@NonNull
@Override
public Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 处理跨域请求
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
headers.add("Access-Control-Max-Age", MAX_AGE);
headers.add("Access-Control-Allow-Credentials", "true");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
// 处理黑白名单与拦截请求
if (requestProperties.getEnabled()) {
String path = request.getPath().value();
String ip = Objects.requireNonNull(request.getRemoteAddress()).getHostString();
if (isRequestBlock(path, ip)) {
throw new RuntimeException(DEFAULT_MESSAGE);
}
}
return chain.filter(exchange);
}
/**
* 是否白名单
*
* @param ip ip地址
* @return boolean
*/
private boolean isWhiteList(String ip) {
List<String> whiteList = requestProperties.getWhiteList();
String[] defaultWhiteIps = defaultWhiteList.toArray(new String[0]);
String[] whiteIps = whiteList.toArray(new String[0]);
return PatternMatchUtils.simpleMatch(defaultWhiteIps, ip) || PatternMatchUtils.simpleMatch(whiteIps, ip);
}
/**
* 是否黑名单
*
* @param ip ip地址
* @return boolean
*/
private boolean isBlackList(String ip) {
List<String> blackList = requestProperties.getBlackList();
String[] blackIps = blackList.toArray(new String[0]);
return PatternMatchUtils.simpleMatch(blackIps, ip);
}
/**
* 是否禁用请求访问
*
* @param path 请求路径
* @return boolean
*/
private boolean isRequestBlock(String path) {
List<String> blockUrl = requestProperties.getBlockUrl();
return defaultBlockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) ||
blockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
/**
* 是否拦截请求
*
* @param path 请求路径
* @param ip ip地址
* @return boolean
*/
private boolean isRequestBlock(String path, String ip) {
return (isRequestBlock(path) && !isWhiteList(ip)) || isBlackList(ip);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.gateway.props;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* Request配置类
*
* @author Chill
*/
@Data
@ConfigurationProperties("blade.request")
public class RequestProperties {
/**
* 开启自定义request
*/
private Boolean enabled = true;
/**
* 放行url
*/
private List<String> skipUrl = new ArrayList<>();
/**
* 禁用url
*/
private List<String> blockUrl = new ArrayList<>();
/**
* 白名单支持通配符例如10.20.0.8*10.20.0.*
*/
private List<String> whiteList = new ArrayList<>();
/**
* 黑名单支持通配符例如10.20.0.8*10.20.0.*
*/
private List<String> blackList = new ArrayList<>();
}

View File

@ -17,29 +17,25 @@ package org.springblade.core.log.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springblade.core.log.model.LogApi; import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.model.LogApiVo; import org.springblade.core.log.pojo.LogApiVO;
import org.springblade.core.log.service.ILogApiService; import org.springblade.core.log.service.ILogApiService;
import org.springblade.core.log.wrapper.LogApiWrapper;
import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query; import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool; import org.springblade.core.tool.utils.StringPool;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 控制器 * 控制器
@ -59,8 +55,10 @@ public class LogApiController {
* 查询单条 * 查询单条
*/ */
@GetMapping("/detail") @GetMapping("/detail")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<LogApi> detail(LogApi log) { public R<LogApi> detail(LogApi log) {
return R.data(logService.getOne(Condition.getQueryWrapper(log))); LogApi logApi = logService.getOne(Condition.getQueryWrapper(log));
return R.data(LogApiWrapper.build().entity(logApi));
} }
/** /**
@ -68,18 +66,11 @@ public class LogApiController {
*/ */
@GetMapping("/list") @GetMapping("/list")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN) @PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<IPage<LogApiVo>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> log, Query query) { public R<IPage<LogApiVO>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> log, Query query) {
query.setAscs("create_time"); query.setAscs("create_time");
query.setDescs(StringPool.EMPTY); query.setDescs(StringPool.EMPTY);
IPage<LogApi> pages = logService.page(Condition.getPage(query), Condition.getQueryWrapper(log, LogApi.class)); IPage<LogApi> pages = logService.page(Condition.getPage(query), Condition.getQueryWrapper(log, LogApi.class));
List<LogApiVo> records = pages.getRecords().stream().map(logApi -> { return R.data(LogApiWrapper.build().pageVO(pages));
LogApiVo vo = BeanUtil.copyProperties(logApi, LogApiVo.class);
vo.setStrId(Func.toStr(logApi.getId()));
return vo;
}).collect(Collectors.toList());
IPage<LogApiVo> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
pageVo.setRecords(records);
return R.data(pageVo);
} }
} }

View File

@ -17,29 +17,25 @@ package org.springblade.core.log.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springblade.core.log.model.LogError; import org.springblade.core.log.model.LogError;
import org.springblade.core.log.model.LogErrorVo; import org.springblade.core.log.pojo.LogErrorVO;
import org.springblade.core.log.service.ILogErrorService; import org.springblade.core.log.service.ILogErrorService;
import org.springblade.core.log.wrapper.LogErrorWrapper;
import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query; import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool; import org.springblade.core.tool.utils.StringPool;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 控制器 * 控制器
@ -59,8 +55,10 @@ public class LogErrorController {
* 查询单条 * 查询单条
*/ */
@GetMapping("/detail") @GetMapping("/detail")
public R<LogError> detail(LogError logError) { @PreAuth(RoleConstant.HAS_ROLE_ADMIN)
return R.data(errorLogService.getOne(Condition.getQueryWrapper(logError))); public R<LogError> detail(LogError log) {
LogError logError = errorLogService.getOne(Condition.getQueryWrapper(log));
return R.data(LogErrorWrapper.build().entity(logError));
} }
/** /**
@ -68,18 +66,11 @@ public class LogErrorController {
*/ */
@GetMapping("/list") @GetMapping("/list")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN) @PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<IPage<LogErrorVo>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> logError, Query query) { public R<IPage<LogErrorVO>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> logError, Query query) {
query.setAscs("create_time"); query.setAscs("create_time");
query.setDescs(StringPool.EMPTY); query.setDescs(StringPool.EMPTY);
IPage<LogError> pages = errorLogService.page(Condition.getPage(query), Condition.getQueryWrapper(logError, LogError.class)); IPage<LogError> pages = errorLogService.page(Condition.getPage(query), Condition.getQueryWrapper(logError, LogError.class));
List<LogErrorVo> records = pages.getRecords().stream().map(logApi -> { return R.data(LogErrorWrapper.build().pageVO(pages));
LogErrorVo vo = BeanUtil.copyProperties(logApi, LogErrorVo.class);
vo.setStrId(Func.toStr(logApi.getId()));
return vo;
}).collect(Collectors.toList());
IPage<LogErrorVo> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
pageVo.setRecords(records);
return R.data(pageVo);
} }
} }

View File

@ -17,29 +17,25 @@ package org.springblade.core.log.controller;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springblade.core.log.model.LogUsual; import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.model.LogUsualVo; import org.springblade.core.log.pojo.LogUsualVO;
import org.springblade.core.log.service.ILogUsualService; import org.springblade.core.log.service.ILogUsualService;
import org.springblade.core.log.wrapper.LogUsualWrapper;
import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query; import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.tool.api.R; import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool; import org.springblade.core.tool.utils.StringPool;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 控制器 * 控制器
@ -59,8 +55,10 @@ public class LogUsualController {
* 查询单条 * 查询单条
*/ */
@GetMapping("/detail") @GetMapping("/detail")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<LogUsual> detail(LogUsual log) { public R<LogUsual> detail(LogUsual log) {
return R.data(logService.getOne(Condition.getQueryWrapper(log))); LogUsual logUsual = logService.getOne(Condition.getQueryWrapper(log));
return R.data(LogUsualWrapper.build().entity(logUsual));
} }
/** /**
@ -68,18 +66,11 @@ public class LogUsualController {
*/ */
@GetMapping("/list") @GetMapping("/list")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN) @PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R<IPage<LogUsualVo>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> log, Query query) { public R<IPage<LogUsualVO>> list(@Parameter(hidden = true) @RequestParam Map<String, Object> log, Query query) {
query.setAscs("create_time"); query.setAscs("create_time");
query.setDescs(StringPool.EMPTY); query.setDescs(StringPool.EMPTY);
IPage<LogUsual> pages = logService.page(Condition.getPage(query), Condition.getQueryWrapper(log, LogUsual.class)); IPage<LogUsual> pages = logService.page(Condition.getPage(query), Condition.getQueryWrapper(log, LogUsual.class));
List<LogUsualVo> records = pages.getRecords().stream().map(logApi -> { return R.data(LogUsualWrapper.build().pageVO(pages));
LogUsualVo vo = BeanUtil.copyProperties(logApi, LogUsualVo.class);
vo.setStrId(Func.toStr(logApi.getId()));
return vo;
}).collect(Collectors.toList());
IPage<LogUsualVo> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
pageVo.setRecords(records);
return R.data(pageVo);
} }
} }

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.log.model.LogApi;
import java.io.Serial;
/**
* LogApiVO
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class LogApiVO extends LogApi {
@Serial
private static final long serialVersionUID = 1L;
/**
* 操作提交的数据
*/
@JsonIgnore
private String params;
}

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.log.model.LogError;
import java.io.Serial;
/**
* LogErrorVO
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class LogErrorVO extends LogError {
@Serial
private static final long serialVersionUID = 1L;
/**
* 操作提交的数据
*/
@JsonIgnore
private String params;
/**
* 堆栈信息
*/
@JsonIgnore
private String stackTrace;
/**
* 异常消息
*/
@JsonIgnore
private String message;
}

View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.pojo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.log.model.LogUsual;
import java.io.Serial;
/**
* LogUsualVO
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class LogUsualVO extends LogUsual {
@Serial
private static final long serialVersionUID = 1L;
/**
* 操作提交的数据
*/
@JsonIgnore
private String params;
/**
* 日志数据
*/
@JsonIgnore
private String logData;
}

View File

@ -0,0 +1,6 @@
/**
* Created by Blade.
*
* @author zhuangqian
*/
package org.springblade.core.log.pojo;

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.wrapper;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.pojo.LogApiVO;
import org.springblade.core.mp.support.BaseEntityWrapper;
import org.springblade.core.tool.sensitive.SensitiveUtil;
import org.springblade.core.tool.sensitive.SensitiveWord;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.StringUtil;
import java.util.Objects;
/**
* Log包装类,返回视图层所需的字段
*
* @author Chill
*/
public class LogApiWrapper extends BaseEntityWrapper<LogApi, LogApiVO> {
public static LogApiWrapper build() {
return new LogApiWrapper();
}
@Override
public LogApiVO entityVO(LogApi logApi) {
return Objects.requireNonNull(BeanUtil.copyProperties(logApi, LogApiVO.class));
}
public LogApi entity(LogApi logApi) {
String params = logApi.getParams();
if (StringUtil.isNotBlank(params)) {
logApi.setParams(SensitiveUtil.processWithWords(params, SensitiveWord.SECURE.getWords()));
}
return logApi;
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.wrapper;
import org.springblade.core.log.model.LogError;
import org.springblade.core.log.pojo.LogErrorVO;
import org.springblade.core.mp.support.BaseEntityWrapper;
import org.springblade.core.tool.sensitive.SensitiveUtil;
import org.springblade.core.tool.sensitive.SensitiveWord;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.StringUtil;
import java.util.Objects;
/**
* Log包装类,返回视图层所需的字段
*
* @author Chill
*/
public class LogErrorWrapper extends BaseEntityWrapper<LogError, LogErrorVO> {
public static LogErrorWrapper build() {
return new LogErrorWrapper();
}
@Override
public LogErrorVO entityVO(LogError logError) {
return Objects.requireNonNull(BeanUtil.copyProperties(logError, LogErrorVO.class));
}
public LogError entity(LogError logError) {
String params = logError.getParams();
String stackTrace = logError.getStackTrace();
String message = logError.getMessage();
if (StringUtil.isNotBlank(params)) {
logError.setParams(SensitiveUtil.processWithWords(params, SensitiveWord.SECURE.getWords()));
}
if (StringUtil.isNotBlank(stackTrace)) {
logError.setStackTrace(SensitiveUtil.processWithWords(stackTrace, SensitiveWord.SECURE.getWords()));
}
if (StringUtil.isNotBlank(message)) {
logError.setMessage(SensitiveUtil.processWithWords(message, SensitiveWord.SECURE.getWords()));
}
return logError;
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.wrapper;
import org.springblade.core.log.model.LogUsual;
import org.springblade.core.log.pojo.LogUsualVO;
import org.springblade.core.mp.support.BaseEntityWrapper;
import org.springblade.core.tool.sensitive.SensitiveUtil;
import org.springblade.core.tool.sensitive.SensitiveWord;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.StringUtil;
import java.util.Objects;
/**
* Log包装类,返回视图层所需的字段
*
* @author Chill
*/
public class LogUsualWrapper extends BaseEntityWrapper<LogUsual, LogUsualVO> {
public static LogUsualWrapper build() {
return new LogUsualWrapper();
}
@Override
public LogUsualVO entityVO(LogUsual logUsual) {
return Objects.requireNonNull(BeanUtil.copyProperties(logUsual, LogUsualVO.class));
}
public LogUsual entity(LogUsual logUsual) {
String params = logUsual.getParams();
String logData = logUsual.getLogData();
if (StringUtil.isNotBlank(params)) {
logUsual.setParams(SensitiveUtil.processWithWords(params, SensitiveWord.SECURE.getWords()));
}
if (StringUtil.isNotBlank(logData)) {
logUsual.setLogData(SensitiveUtil.processWithWords(logData, SensitiveWord.SECURE.getWords()));
}
return logUsual;
}
}

View File

@ -20,6 +20,7 @@ import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder; import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
@ -30,8 +31,10 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.springblade.common.cache.CacheNames;
import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query; import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser; import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.secure.utils.SecureUtil; import org.springblade.core.secure.utils.SecureUtil;
@ -39,6 +42,7 @@ import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.BladeConstant; import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.system.user.entity.User; import org.springblade.system.user.entity.User;
import org.springblade.system.excel.UserExcel; import org.springblade.system.excel.UserExcel;
import org.springblade.system.excel.UserImportListener; import org.springblade.system.excel.UserImportListener;
@ -69,6 +73,7 @@ import java.util.Map;
public class UserController { public class UserController {
private IUserService userService; private IUserService userService;
private BladeRedis bladeRedis;
/** /**
* 查询单条 * 查询单条
@ -85,7 +90,7 @@ public class UserController {
/** /**
* 查询单条 * 查询单条
*/ */
@ApiOperationSupport(order =2) @ApiOperationSupport(order = 2)
@Operation(summary = "查看详情", description = "传入id") @Operation(summary = "查看详情", description = "传入id")
@GetMapping("/info") @GetMapping("/info")
public R<UserVO> info(BladeUser user) { public R<UserVO> info(BladeUser user) {
@ -212,7 +217,7 @@ public class UserController {
@Operation(summary = "导入用户", description = "传入excel") @Operation(summary = "导入用户", description = "传入excel")
public R importUser(MultipartFile file, Integer isCovered) { public R importUser(MultipartFile file, Integer isCovered) {
String filename = file.getOriginalFilename(); String filename = file.getOriginalFilename();
if (StringUtils.isEmpty(filename)) { if (StringUtil.isBlank(filename)) {
throw new RuntimeException("请上传文件!"); throw new RuntimeException("请上传文件!");
} }
if ((!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) { if ((!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
@ -240,14 +245,14 @@ public class UserController {
@PreAuth(RoleConstant.HAS_ROLE_ADMIN) @PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public void exportUser(@Parameter(hidden = true) @RequestParam Map<String, Object> user, BladeUser bladeUser, HttpServletResponse response) { public void exportUser(@Parameter(hidden = true) @RequestParam Map<String, Object> user, BladeUser bladeUser, HttpServletResponse response) {
QueryWrapper<User> queryWrapper = Condition.getQueryWrapper(user, User.class); QueryWrapper<User> queryWrapper = Condition.getQueryWrapper(user, User.class);
if (!SecureUtil.isAdministrator()){ if (!SecureUtil.isAdministrator()) {
queryWrapper.lambda().eq(User::getTenantId, bladeUser.getTenantId()); queryWrapper.lambda().eq(User::getTenantId, bladeUser.getTenantId());
} }
queryWrapper.lambda().eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED); queryWrapper.lambda().eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED);
List<UserExcel> list = userService.exportUser(queryWrapper); List<UserExcel> list = userService.exportUser(queryWrapper);
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String fileName = URLEncoder.encode("用户数据导出", StandardCharsets.UTF_8.name()); String fileName = URLEncoder.encode("用户数据导出", StandardCharsets.UTF_8);
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), UserExcel.class).sheet("用户数据表").doWrite(list); EasyExcel.write(response.getOutputStream(), UserExcel.class).sheet("用户数据表").doWrite(list);
} }
@ -263,7 +268,7 @@ public class UserController {
List<UserExcel> list = new ArrayList<>(); List<UserExcel> list = new ArrayList<>();
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setCharacterEncoding(StandardCharsets.UTF_8.name());
String fileName = URLEncoder.encode("用户数据模板", StandardCharsets.UTF_8.name()); String fileName = URLEncoder.encode("用户数据模板", StandardCharsets.UTF_8);
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), UserExcel.class).sheet("用户数据表").doWrite(list); EasyExcel.write(response.getOutputStream(), UserExcel.class).sheet("用户数据表").doWrite(list);
} }
@ -279,4 +284,20 @@ public class UserController {
} }
/**
* 用户解锁
*/
@PostMapping("/unlock")
@ApiOperationSupport(order = 16)
@Operation(summary = "账号解锁")
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public R unlock(String userIds) {
if (StringUtil.isBlank(userIds)) {
return R.fail("请至少选择一个用户");
}
List<User> userList = userService.list(Wrappers.<User>lambdaQuery().in(User::getId, Func.toLongList(userIds)));
userList.forEach(user -> bladeRedis.del(CacheNames.tenantKey(user.getTenantId(), CacheNames.USER_FAIL_KEY, user.getAccount())));
return R.success("操作成功");
}
} }

View File

@ -67,13 +67,13 @@ knife4j:
language: zh_cn language: zh_cn
enableFooter: false enableFooter: false
enableFooterCustom: true enableFooterCustom: true
footerCustomContent: Copyright © 2024 SpringBlade All Rights Reserved footerCustomContent: Copyright © 2025 SpringBlade All Rights Reserved
#swagger配置信息 #swagger配置信息
swagger: swagger:
title: SpringBlade 接口文档系统 title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统 description: SpringBlade 接口文档系统
version: 4.3.0 version: 4.5.0
license: Powered By SpringBlade license: Powered By SpringBlade
licenseUrl: https://bladex.cn licenseUrl: https://bladex.cn
terms-of-service-url: https://bladex.cn terms-of-service-url: https://bladex.cn
@ -86,14 +86,14 @@ swagger:
blade: blade:
auth: auth:
#使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2验签,需和前端保持一致 #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2验签,需和前端保持一致
public-key: 请配置sm2公钥 public-key: ${BLADE_OAUTH2_PUBLIC_KEY}
#使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2解密,前端无需配置 #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2解密,前端无需配置
private-key: 请配置sm2私钥 private-key: ${BLADE_OAUTH2_PRIVATE_KEY}
token: token:
#使用 @org.springblade.test.SignKeyGenerator 获取 #使用 @org.springblade.test.SignKeyGenerator 获取
sign-key: 请配置32位签名 sign-key: ${BLADE_TOKEN_SIGN_KEY}
#使用 @org.springblade.test.SignKeyGenerator 获取 #使用 @org.springblade.test.SignKeyGenerator 获取
aes-key: 请配置cryptoKey aes-key: ${BLADE_TOKEN_CRYPTO_KEY}
xss: xss:
enabled: true enabled: true
skip-url: skip-url:

View File

@ -9,7 +9,9 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<revision>4.3.0</revision> <revision>4.5.0</revision>
<blade.tool.version>4.5.0</blade.tool.version>
<java.version>17</java.version> <java.version>17</java.version>
<maven.plugin.version>3.11.0</maven.plugin.version> <maven.plugin.version>3.11.0</maven.plugin.version>
@ -41,7 +43,7 @@
<dependency> <dependency>
<groupId>org.springblade</groupId> <groupId>org.springblade</groupId>
<artifactId>blade-core-bom</artifactId> <artifactId>blade-core-bom</artifactId>
<version>${revision}</version> <version>${blade.tool.version}</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>

View File

@ -1,2 +1,2 @@
REGISTER=192.168.0.157/blade REGISTER=192.168.0.157/blade
TAG=4.3.0 TAG=4.5.0

View File

@ -152,7 +152,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-admin - name: blade-admin
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-admin:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-admin:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -386,7 +386,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-auth - name: blade-auth
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-auth:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-auth:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -625,7 +625,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-desk - name: blade-desk
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-desk:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-desk:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -864,7 +864,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-develop - name: blade-develop
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-develop:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-develop:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -1096,7 +1096,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-gateway - name: blade-gateway
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-gateway:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-gateway:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -1331,7 +1331,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-log - name: blade-log
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-log:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-log:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -1565,7 +1565,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-report - name: blade-report
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-report:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-report:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -1799,7 +1799,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-resource - name: blade-resource
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-resource:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-resource:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -2033,7 +2033,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-system - name: blade-system
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-system:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-system:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -2262,7 +2262,7 @@ spec:
spec: spec:
containers: containers:
- name: saber-web - name: saber-web
image: 'swr.cn-east-2.myhuaweicloud.com/blade/saber-web:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/saber-web:4.5.0'
ports: ports:
- name: web - name: web
containerPort: 80 containerPort: 80
@ -2487,7 +2487,7 @@ spec:
spec: spec:
containers: containers:
- name: blade-swagger - name: blade-swagger
image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-swagger:4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/blade-swagger:4.5.0'
args: args:
- '--spring.profiles.active=${PROFILE}' - '--spring.profiles.active=${PROFILE}'
- '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}' - '--spring.cloud.nacos.config.server-addr=${NACOS_SERVER_ADDR}'
@ -3515,7 +3515,7 @@ spec:
spec: spec:
containers: containers:
- name: mysql - name: mysql
image: 'swr.cn-east-2.myhuaweicloud.com/blade/saber-db:v4.3.0' image: 'swr.cn-east-2.myhuaweicloud.com/blade/saber-db:v4.5.0'
ports: ports:
- name: mysql - name: mysql
containerPort: 3306 containerPort: 3306