mirror of
https://github.com/chillzhuang/SpringBlade.git
synced 2025-04-19 19:19:18 +08:00
🎉 4.5.0.RELEASE 新增账号锁定与解锁功能,新增腾讯云对象存储支持
This commit is contained in:
parent
fb91e2627f
commit
bcd93a7a4e
11
README.md
11
README.md
@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/Release-V4.4.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">
|
||||||
@ -82,18 +82,19 @@ 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/) |
|
||||||
|
|
||||||
## 前端项目
|
## 前端项目
|
||||||
|
|
||||||
| 简介 | 地址 |
|
| 简介 | 地址 |
|
||||||
|--------------------|----------------------------------------------------------------------------------------------------|
|
|--------------------|------------------------------------------------------------------------------|
|
||||||
|
| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber) |
|
||||||
|
| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber2](https://gitee.com/smallc/Saber/tree/vue2/) |
|
||||||
| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
|
| 前端框架Sword(基于React) | [https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword) |
|
||||||
| 前端框架Saber(基于Vue2) | [https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber) |
|
|
||||||
| 前端框架Saber3(基于Vue3) | [https://gitee.com/smallc/Saber3](https://gitee.com/smallc/Saber/tree/3.x/) |
|
|
||||||
|
|
||||||
## 后端项目
|
## 后端项目
|
||||||
| 简介 | 地址 |
|
| 简介 | 地址 |
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
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;
|
||||||
@ -30,16 +31,20 @@ 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 BladeRedis bladeRedis;
|
private BladeRedis bladeRedis;
|
||||||
@ -53,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 = Func.toStr(bladeRedis.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);
|
||||||
@ -62,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)) {
|
||||||
// 获取用户类型
|
// 获取用户类型
|
||||||
@ -80,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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("操作成功");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.4.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
|
||||||
|
4
pom.xml
4
pom.xml
@ -9,9 +9,9 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>4.4.0</revision>
|
<revision>4.5.0</revision>
|
||||||
|
|
||||||
<blade.tool.version>4.4.2</blade.tool.version>
|
<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>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
REGISTER=192.168.0.157/blade
|
REGISTER=192.168.0.157/blade
|
||||||
TAG=4.4.0
|
TAG=4.5.0
|
||||||
|
@ -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.4.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.4.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.4.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.4.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.4.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.4.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.4.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.4.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.4.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.4.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.4.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.4.0'
|
image: 'swr.cn-east-2.myhuaweicloud.com/blade/saber-db:v4.5.0'
|
||||||
ports:
|
ports:
|
||||||
- name: mysql
|
- name: mysql
|
||||||
containerPort: 3306
|
containerPort: 3306
|
||||||
|
Loading…
x
Reference in New Issue
Block a user