forked from github/cool-admin-java
feat: 新增验证码配置管理功能
- 新增 UserCaptchaConfigEntity 实体类,含 side(admin/app) 端标识 - 新增 Admin/App Controller、Service、Mapper - BaseSysLoginServiceImpl 新增 isCaptchaEnabled() 查询数据库配置 - login/register 改用数据库配置替代 cool.captcha.enable 硬编码 - AdminBaseOpenController 新增 /captchaEnabled 接口 - menu.json 新增验证码管理菜单及权限 - base.json 新增 5 条预置数据(3条app+2条admin)
This commit is contained in:
@@ -125,4 +125,10 @@ public class AdminBaseOpenController {
|
||||
}
|
||||
return R.ok(Map.of("mode", "common"));
|
||||
}
|
||||
|
||||
@Operation(summary = "管理端验证码是否启用")
|
||||
@GetMapping("/captchaEnabled")
|
||||
public R captchaEnabled() {
|
||||
return R.ok(Map.of("enabled", baseSysLoginService.isCaptchaEnabled("admin", "adminLogin")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ public interface BaseSysLoginService {
|
||||
*/
|
||||
void captchaCheck(String captchaId, String code);
|
||||
|
||||
boolean isCaptchaEnabled(String side, String sceneKey);
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*
|
||||
|
||||
@@ -23,6 +23,8 @@ import com.cool.modules.base.mapper.sys.BaseSysRoleMapper;
|
||||
import com.cool.modules.base.mapper.sys.BaseSysUserMapper;
|
||||
import com.cool.modules.base.service.sys.BaseSysLoginService;
|
||||
import com.cool.modules.base.service.sys.BaseSysPermsService;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
import com.cool.modules.user.service.UserCaptchaConfigService;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import java.awt.Color;
|
||||
import java.util.HashMap;
|
||||
@@ -52,6 +54,8 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
|
||||
|
||||
private final BaseSysPermsService baseSysPermsService;
|
||||
|
||||
private final UserCaptchaConfigService userCaptchaConfigService;
|
||||
|
||||
private final org.springframework.security.crypto.password.PasswordEncoder passwordEncoder;
|
||||
|
||||
@org.springframework.beans.factory.annotation.Value("${cool.captcha.enable:true}")
|
||||
@@ -90,10 +94,21 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaptchaEnabled(String side, String sceneKey) {
|
||||
UserCaptchaConfigEntity config = userCaptchaConfigService.getOne(
|
||||
QueryWrapper.create()
|
||||
.eq(UserCaptchaConfigEntity::getSide, side)
|
||||
.eq(UserCaptchaConfigEntity::getSceneKey, sceneKey));
|
||||
if (config != null) {
|
||||
return ObjUtil.equals(config.getEnabled(), 1);
|
||||
}
|
||||
return captchaEnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object login(BaseSysLoginDto baseSysLoginDto) {
|
||||
// 1、检查验证码是否正确 2、执行登录操作
|
||||
if (captchaEnable) {
|
||||
if (isCaptchaEnabled("admin", "adminLogin")) {
|
||||
captchaCheck(baseSysLoginDto.getCaptchaId(), baseSysLoginDto.getVerifyCode());
|
||||
}
|
||||
UsernamePasswordAuthenticationToken upToken =
|
||||
@@ -109,7 +124,7 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
|
||||
CoolPreconditions.check(
|
||||
ObjectUtil.isEmpty(baseSysUserEntity) || baseSysUserEntity.getStatus() == 0, "用户已禁用");
|
||||
Long[] roleIds = baseSysPermsService.getRoles(baseSysUserEntity);
|
||||
if (captchaEnable) {
|
||||
if (isCaptchaEnabled("admin", "adminLogin")) {
|
||||
coolCache.del("verify:img:" + baseSysLoginDto.getCaptchaId());
|
||||
}
|
||||
return generateToken(roleIds, baseSysUserEntity, null);
|
||||
@@ -134,7 +149,7 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
|
||||
|
||||
@Override
|
||||
public Object register(BaseSysRegisterDto baseSysRegisterDto) {
|
||||
if (captchaEnable) {
|
||||
if (isCaptchaEnabled("admin", "adminRegister")) {
|
||||
captchaCheck(baseSysRegisterDto.getCaptchaId(), baseSysRegisterDto.getVerifyCode());
|
||||
}
|
||||
BaseSysUserEntity existUser = baseSysUserMapper.selectOneByQuery(
|
||||
@@ -160,7 +175,7 @@ public class BaseSysLoginServiceImpl implements BaseSysLoginService {
|
||||
userEntity.setDepartmentId(defaultDepartment.getId());
|
||||
baseSysUserMapper.insertSelective(userEntity);
|
||||
baseSysPermsService.updateUserRole(userEntity.getId(), new Long[]{normalRole.getId()});
|
||||
if (captchaEnable) {
|
||||
if (isCaptchaEnabled("admin", "adminRegister")) {
|
||||
coolCache.del("verify:img:" + baseSysRegisterDto.getCaptchaId());
|
||||
}
|
||||
return Dict.create().set("message", "注册成功");
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.cool.modules.user.controller.admin;
|
||||
|
||||
import static com.cool.modules.user.entity.table.UserCaptchaConfigEntityTableDef.USER_CAPTCHA_CONFIG_ENTITY;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.cool.core.annotation.CoolRestController;
|
||||
import com.cool.core.base.BaseController;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
import com.cool.modules.user.service.UserCaptchaConfigService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@Tag(name = "验证码配置", description = "管理验证码配置")
|
||||
@CoolRestController(api = {"add", "delete", "update", "page", "list", "info"}, cname = "captchaConfig")
|
||||
public class AdminUserCaptchaConfigController extends BaseController<UserCaptchaConfigService, UserCaptchaConfigEntity> {
|
||||
|
||||
@Override
|
||||
protected void init(HttpServletRequest request, JSONObject requestParams) {
|
||||
setPageOption(createOp().fieldEq(USER_CAPTCHA_CONFIG_ENTITY.SIDE, USER_CAPTCHA_CONFIG_ENTITY.CAPTCHA_TYPE, USER_CAPTCHA_CONFIG_ENTITY.ENABLED)
|
||||
.keyWordLikeFields(USER_CAPTCHA_CONFIG_ENTITY.LABEL));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.cool.modules.user.controller.app;
|
||||
|
||||
import com.cool.core.annotation.CoolRestController;
|
||||
import com.cool.core.annotation.TokenIgnore;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
import com.cool.modules.user.service.UserCaptchaConfigService;
|
||||
import com.cool.core.request.R;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "App验证码配置", description = "App端获取验证码配置")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AppUserCaptchaConfigController {
|
||||
|
||||
private final UserCaptchaConfigService userCaptchaConfigService;
|
||||
|
||||
@TokenIgnore
|
||||
@GetMapping("/app/user/captcha/config")
|
||||
public R<List<UserCaptchaConfigEntity>> getConfig() {
|
||||
return R.ok(userCaptchaConfigService.getEnabledConfigs("app"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.cool.modules.user.entity;
|
||||
|
||||
import com.cool.core.base.BaseEntity;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import com.cool.core.annotation.ColumnDefine;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Table(value = "user_captcha_config", comment = "验证码配置")
|
||||
public class UserCaptchaConfigEntity extends BaseEntity<UserCaptchaConfigEntity> {
|
||||
|
||||
@ColumnDefine(comment = "端标识 admin/app", notNull = true, length = 20)
|
||||
private String side;
|
||||
|
||||
@ColumnDefine(comment = "场景标识 smsLogin/smsRegister/bindPhone/passwordLogin/adminLogin/adminRegister", notNull = true, length = 50)
|
||||
private String sceneKey;
|
||||
|
||||
@ColumnDefine(comment = "场景名称", notNull = true, length = 50)
|
||||
private String label;
|
||||
|
||||
@ColumnDefine(comment = "验证码类型 image/sms/slider/rotate/wordClick", notNull = true, length = 30)
|
||||
private String captchaType;
|
||||
|
||||
@ColumnDefine(comment = "是否启用验证码 0-禁用 1-启用", defaultValue = "1")
|
||||
private Integer enabled;
|
||||
|
||||
@ColumnDefine(comment = "验证码长度", defaultValue = "4")
|
||||
private Integer codeLength;
|
||||
|
||||
@ColumnDefine(comment = "验证码过期时间(秒)", defaultValue = "1800")
|
||||
private Integer expireSeconds;
|
||||
|
||||
@ColumnDefine(comment = "验证码宽度(图片验证码)", defaultValue = "150")
|
||||
private Integer width;
|
||||
|
||||
@ColumnDefine(comment = "验证码高度(图片验证码)", defaultValue = "50")
|
||||
private Integer height;
|
||||
|
||||
@ColumnDefine(comment = "短信发送间隔(秒,短信验证码)", defaultValue = "60")
|
||||
private Integer smsInterval;
|
||||
|
||||
@ColumnDefine(comment = "每日发送上限(短信验证码)", defaultValue = "10")
|
||||
private Integer smsDailyLimit;
|
||||
|
||||
@ColumnDefine(comment = "排序号,越小越靠前", defaultValue = "0")
|
||||
private Integer orderNum;
|
||||
|
||||
@ColumnDefine(comment = "备注", length = 200)
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.cool.modules.user.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
|
||||
public interface UserCaptchaConfigMapper extends BaseMapper<UserCaptchaConfigEntity> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.cool.modules.user.service;
|
||||
|
||||
import com.cool.core.base.BaseService;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserCaptchaConfigService extends BaseService<UserCaptchaConfigEntity> {
|
||||
|
||||
List<UserCaptchaConfigEntity> getEnabledConfigs(String side);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.cool.modules.user.service.impl;
|
||||
|
||||
import com.cool.core.base.BaseServiceImpl;
|
||||
import com.cool.modules.user.entity.UserCaptchaConfigEntity;
|
||||
import com.cool.modules.user.mapper.UserCaptchaConfigMapper;
|
||||
import com.cool.modules.user.service.UserCaptchaConfigService;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import java.util.List;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class UserCaptchaConfigServiceImpl extends BaseServiceImpl<UserCaptchaConfigMapper, UserCaptchaConfigEntity>
|
||||
implements UserCaptchaConfigService {
|
||||
|
||||
@Override
|
||||
public List<UserCaptchaConfigEntity> getEnabledConfigs(String side) {
|
||||
return list(QueryWrapper.create()
|
||||
.eq(UserCaptchaConfigEntity::getSide, side)
|
||||
.eq(UserCaptchaConfigEntity::getEnabled, 1)
|
||||
.orderBy(UserCaptchaConfigEntity::getOrderNum, true));
|
||||
}
|
||||
}
|
||||
@@ -209,5 +209,87 @@
|
||||
"orderNum": 2,
|
||||
"isDefault": 0
|
||||
}
|
||||
],
|
||||
"user_captcha_config": [
|
||||
{
|
||||
"id": 1,
|
||||
"side": "app",
|
||||
"sceneKey": "smsLogin",
|
||||
"label": "短信登录验证码",
|
||||
"captchaType": "image",
|
||||
"enabled": 1,
|
||||
"codeLength": 4,
|
||||
"expireSeconds": 1800,
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"smsInterval": 60,
|
||||
"smsDailyLimit": 10,
|
||||
"orderNum": 1,
|
||||
"remark": "手机验证码登录前的图片验证码"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"side": "app",
|
||||
"sceneKey": "smsRegister",
|
||||
"label": "短信注册验证码",
|
||||
"captchaType": "image",
|
||||
"enabled": 1,
|
||||
"codeLength": 4,
|
||||
"expireSeconds": 1800,
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"smsInterval": 60,
|
||||
"smsDailyLimit": 10,
|
||||
"orderNum": 2,
|
||||
"remark": "注册时发送短信前的图片验证码"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"side": "app",
|
||||
"sceneKey": "bindPhone",
|
||||
"label": "绑定手机验证码",
|
||||
"captchaType": "image",
|
||||
"enabled": 1,
|
||||
"codeLength": 4,
|
||||
"expireSeconds": 1800,
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"smsInterval": 60,
|
||||
"smsDailyLimit": 10,
|
||||
"orderNum": 3,
|
||||
"remark": "绑定手机号时的短信验证码"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"side": "admin",
|
||||
"sceneKey": "adminLogin",
|
||||
"label": "管理员登录验证码",
|
||||
"captchaType": "image",
|
||||
"enabled": 1,
|
||||
"codeLength": 4,
|
||||
"expireSeconds": 1800,
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"smsInterval": 60,
|
||||
"smsDailyLimit": 10,
|
||||
"orderNum": 1,
|
||||
"remark": "管理端登录时的图片验证码"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"side": "admin",
|
||||
"sceneKey": "adminRegister",
|
||||
"label": "管理员注册验证码",
|
||||
"captchaType": "image",
|
||||
"enabled": 1,
|
||||
"codeLength": 4,
|
||||
"expireSeconds": 1800,
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"smsInterval": 60,
|
||||
"smsDailyLimit": 10,
|
||||
"orderNum": 2,
|
||||
"remark": "管理端注册时的图片验证码"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -992,6 +992,91 @@
|
||||
"childMenus": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "验证码管理",
|
||||
"router": "/user/captcha-config",
|
||||
"perms": null,
|
||||
"type": 1,
|
||||
"icon": "icon-menu",
|
||||
"orderNum": 3,
|
||||
"viewPath": "modules/user/views/captcha-config.vue",
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": [
|
||||
{
|
||||
"name": "删除",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:delete",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
},
|
||||
{
|
||||
"name": "修改",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:update,user:captchaConfig:info",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
},
|
||||
{
|
||||
"name": "单个信息",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:info",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
},
|
||||
{
|
||||
"name": "列表查询",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:list",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
},
|
||||
{
|
||||
"name": "分页查询",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:page",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
},
|
||||
{
|
||||
"name": "新增",
|
||||
"router": null,
|
||||
"perms": "user:captchaConfig:add",
|
||||
"type": 2,
|
||||
"icon": null,
|
||||
"orderNum": 0,
|
||||
"viewPath": null,
|
||||
"keepAlive": true,
|
||||
"isShow": true,
|
||||
"childMenus": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user