This commit is contained in:
wol
2025-08-15 00:24:30 +08:00
parent 92246a6767
commit 4c4f1acf32
29 changed files with 460 additions and 285 deletions

View File

@@ -18,7 +18,7 @@ public interface BasicEnum<T>{
* 获取枚举的描述
* @return 描述
*/
String description();
String getDesc();
}

View File

@@ -3,7 +3,6 @@ package com.agileboot.common.core.enums;
import cn.hutool.core.convert.Convert;
import com.agileboot.common.core.exception.ApiException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.common.core.enums.BasicEnum;
import java.util.Objects;
@@ -55,7 +54,7 @@ public class BasicEnumUtil {
public static <E extends Enum<E>> String getDescriptionByValue(Class<E> enumClass, Object value) {
E basicEnum = fromValueSafely(enumClass, value);
if (basicEnum != null) {
return ((BasicEnum<?>) basicEnum).description();
return ((BasicEnum<?>) basicEnum).getDesc();
}
return UNKNOWN;
}

View File

@@ -10,6 +10,6 @@ public interface DictionaryEnum<T> extends BasicEnum<T> {
* 获取css标签
* @return css标签
*/
String cssTag();
String getCssTag();
}

View File

@@ -42,12 +42,12 @@ public enum BusinessTypeEnum implements DictionaryEnum<Integer> {
}
@Override
public String description() {
public String getDesc() {
return description;
}
@Override
public String cssTag() {
public String getCssTag() {
return cssTag;
}

View File

@@ -35,12 +35,12 @@ public enum GenderEnum implements DictionaryEnum<Integer> {
}
@Override
public String description() {
public String getDesc() {
return description;
}
@Override
public String cssTag() {
public String getCssTag() {
return cssTag;
}

View File

@@ -1,15 +1,20 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 用户状态
*
* @author valarchie
*/
// TODO 表记得改成LoginLog
@Dictionary(name = "sysLoginLog.status")
@Getter
@AllArgsConstructor
public enum LoginStatusEnum implements DictionaryEnum<Integer> {
/**
* status of user
@@ -20,27 +25,6 @@ public enum LoginStatusEnum implements DictionaryEnum<Integer> {
LOGIN_FAIL(0, "登录失败", CssTag.DANGER);
private final int value;
private final String msg;
private final String desc;
private final String cssTag;
LoginStatusEnum(int status, String msg, String cssTag) {
this.value = status;
this.msg = msg;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return msg;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -1,36 +1,25 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.BasicEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
* @author valarchie
*/
@Deprecated
@Getter
@AllArgsConstructor
public enum MenuComponentEnum implements BasicEnum<Integer> {
/**
* 菜单组件类型
*/
LAYOUT(1,"Layout"),
PARENT_VIEW(2,"ParentView"),
INNER_LINK(3,"InnerLink");
LAYOUT(1, "Layout"),
PARENT_VIEW(2, "ParentView"),
INNER_LINK(3, "InnerLink");
private final int value;
private final String description;
private final String desc;
MenuComponentEnum(int value, String description) {
this.value = value;
this.description = description;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
}

View File

@@ -1,11 +1,15 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.BasicEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author valarchie
* 对应 sys_menu表的menu_type字段
*/
@Getter
@AllArgsConstructor
public enum MenuTypeEnum implements BasicEnum<Integer> {
/**
@@ -17,22 +21,6 @@ public enum MenuTypeEnum implements BasicEnum<Integer> {
OUTSIDE_LINK_REDIRECT(4, "外链跳转");
private final int value;
private final String description;
MenuTypeEnum(int value, String description) {
this.value = value;
this.description = description;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
private final String desc;
}

View File

@@ -1,14 +1,19 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 对应sys_notice的 status字段
*
* @author valarchie
*/
@Dictionary(name = "sysNotice.status")
@Getter
@AllArgsConstructor
public enum NoticeStatusEnum implements DictionaryEnum<Integer> {
/**
@@ -18,28 +23,7 @@ public enum NoticeStatusEnum implements DictionaryEnum<Integer> {
CLOSE(0, "关闭", CssTag.DANGER);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
NoticeStatusEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -1,16 +1,21 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 对应sys_notice的 notice_type字段
* 名称一般由对应的表名.字段构成
* 全局的话使用common作为表名
*
* @author valarchie
*/
@Dictionary(name = "sysNotice.noticeType")
@Getter
@AllArgsConstructor
public enum NoticeTypeEnum implements DictionaryEnum<Integer> {
/**
@@ -20,28 +25,7 @@ public enum NoticeTypeEnum implements DictionaryEnum<Integer> {
ANNOUNCEMENT(2, "公告", CssTag.SUCCESS);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
NoticeTypeEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -1,14 +1,19 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 对应sys_operation_log的status字段
*
* @author valarchie
*/
@Dictionary(name = "sysOperationLog.status")
@Getter
@AllArgsConstructor
public enum OperationStatusEnum implements DictionaryEnum<Integer> {
/**
@@ -18,28 +23,7 @@ public enum OperationStatusEnum implements DictionaryEnum<Integer> {
FAIL(0, "失败", CssTag.DANGER);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
OperationStatusEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -1,12 +1,17 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.BasicEnum;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 操作者类型
*
* @author valarchie
*/
@Getter
@AllArgsConstructor
@Dictionary(name = "sysOperationLog.operatorType")
public enum OperatorTypeEnum implements BasicEnum<Integer> {
@@ -18,22 +23,7 @@ public enum OperatorTypeEnum implements BasicEnum<Integer> {
MOBILE(3, "手机端用户");
private final int value;
private final String description;
OperatorTypeEnum(int value, String description) {
this.value = value;
this.description = description;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
private final String desc;
}

View File

@@ -1,11 +1,16 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.BasicEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Http Method
*
* @author valarchie
*/
@Getter
@AllArgsConstructor
public enum RequestMethodEnum implements BasicEnum<Integer> {
/**
@@ -18,22 +23,7 @@ public enum RequestMethodEnum implements BasicEnum<Integer> {
UNKNOWN(-1, "UNKNOWN");
private final int value;
private final String description;
RequestMethodEnum(int value, String description) {
this.value = value;
this.description = description;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
private final String desc;
}

View File

@@ -1,14 +1,19 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 除非表有特殊指明的话,一般用这个枚举代表 status字段
*
* @author valarchie
*/
@Dictionary(name = "common.status")
@Getter
@AllArgsConstructor
public enum StatusEnum implements DictionaryEnum<Integer> {
/**
* 开关状态
@@ -17,28 +22,7 @@ public enum StatusEnum implements DictionaryEnum<Integer> {
DISABLE(0, "停用", CssTag.DANGER);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
StatusEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -34,7 +34,7 @@ public enum UserStatusEnum implements DictionaryEnum<Integer> {
}
@Override
public String description() {
public String getDesc() {
return this.description;
}
@@ -43,7 +43,7 @@ public enum UserStatusEnum implements DictionaryEnum<Integer> {
}
@Override
public String cssTag() {
public String getCssTag() {
return null;
}
}

View File

@@ -1,15 +1,20 @@
package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import com.agileboot.common.core.enums.DictionaryEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 对应sys_menu表的is_visible字段
*
* @author valarchie
*/
@Deprecated
@Dictionary(name = "sysMenu.isVisible")
@Getter
@AllArgsConstructor
public enum VisibleStatusEnum implements DictionaryEnum<Integer> {
/**
@@ -19,28 +24,7 @@ public enum VisibleStatusEnum implements DictionaryEnum<Integer> {
HIDE(0, "隐藏", CssTag.DANGER);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
VisibleStatusEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -3,12 +3,17 @@ package com.agileboot.common.core.enums.common;
import com.agileboot.common.core.enums.DictionaryEnum;
import com.agileboot.common.core.enums.dictionary.CssTag;
import com.agileboot.common.core.enums.dictionary.Dictionary;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统内代表是与否的枚举
*
* @author valarchie
*/
@Dictionary(name = "common.yesOrNo")
@Getter
@AllArgsConstructor
public enum YesOrNoEnum implements DictionaryEnum<Integer> {
/**
* 是与否
@@ -17,30 +22,7 @@ public enum YesOrNoEnum implements DictionaryEnum<Integer> {
NO(0, "", CssTag.DANGER);
private final int value;
private final String description;
private final String desc;
private final String cssTag;
YesOrNoEnum(int value, String description, String cssTag) {
this.value = value;
this.description = description;
this.cssTag = cssTag;
}
@Override
public Integer getValue() {
return value;
}
@Override
public String description() {
return description;
}
@Override
public String cssTag() {
return cssTag;
}
}

View File

@@ -17,9 +17,9 @@ public class DictionaryData {
@SuppressWarnings("rawtypes")
public DictionaryData(DictionaryEnum enumType) {
if (enumType != null) {
this.label = enumType.description();
this.label = enumType.getDesc();
this.value = (Integer) enumType.getValue();
this.cssTag = enumType.cssTag();
this.cssTag = enumType.getCssTag();
}
}

View File

@@ -1,6 +1,7 @@
package com.agileboot.common.core.exception;
import cn.hutool.core.text.StrFormatter;
import com.agileboot.common.core.exception.error.ErrorCodeInterface;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -34,6 +35,11 @@ public class BizException extends RuntimeException {
this.message = message;
}
public BizException(ErrorCodeInterface errorCode) {
this.code = errorCode.code();
this.message = errorCode.message();
}
public BizException(String message, Integer code) {
this.message = message;
this.code = code;

View File

@@ -23,7 +23,7 @@ public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
private String id;
private Long id;
/**
* 搜索值

View File

@@ -1,10 +1,10 @@
package com.agileboot.system.config.controller;
import com.agileboot.common.core.core.R;
import com.agileboot.common.core.enums.common.BusinessTypeEnum;
import com.agileboot.common.mybatis.core.page.PageR;
import com.agileboot.system.config.pojo.dto.ConfigDTO;
import com.agileboot.system.config.pojo.dto.ConfigQuery;
import com.agileboot.system.config.pojo.dto.ConfigUpdate;
import com.agileboot.system.config.service.ISysConfigService;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
@@ -49,19 +49,9 @@ public class SysConfigController {
/**
* 修改参数配置
*/
@PostMapping(value = "/config/{configId}")
public R<Void> edit(@NotNull @Positive @PathVariable Long configId, @RequestBody ConfigUpdateCommand config) {
config.setConfigId(configId);
sysConfigService.updateConfig(config);
return R.ok();
}
/**
* 刷新参数缓存
*/
@PostMapping("/configs/cache")
public R<Void> refreshCache() {
CacheCenter.configCache.invalidateAll();
@PostMapping(value = "/config/{id}")
public R<Void> edit(@NotNull @Positive @PathVariable Long id, @RequestBody ConfigUpdate config) {
sysConfigService.updateConfig(id, config);
return R.ok();
}
}

View File

@@ -17,7 +17,7 @@ import java.util.List;
@Data
public class ConfigDTO {
private String configId;
private Long configId;
private String configName;
private String configKey;
private String configValue;

View File

@@ -0,0 +1,18 @@
package com.agileboot.system.config.pojo.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
/**
* @author valarchie
*/
@Data
public class ConfigUpdate {
@NotNull
@NotEmpty
private String configValue;
}

View File

@@ -3,6 +3,7 @@ package com.agileboot.system.config.service;
import com.agileboot.common.mybatis.core.page.PageR;
import com.agileboot.system.config.pojo.dto.ConfigDTO;
import com.agileboot.system.config.pojo.dto.ConfigQuery;
import com.agileboot.system.config.pojo.dto.ConfigUpdate;
/**
* <p>
@@ -26,5 +27,5 @@ public interface ISysConfigService {
ConfigDTO getConfigInfo(Long id);
void updateConfig(ConfigUpdateCommand updateCommand);
void updateConfig(Long id, ConfigUpdate update);
}

View File

@@ -1,9 +1,11 @@
package com.agileboot.system.config.service.impl;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.mybatis.core.page.PageR;
import com.agileboot.system.config.mapper.SysConfigMapper;
import com.agileboot.system.config.pojo.dto.ConfigDTO;
import com.agileboot.system.config.pojo.dto.ConfigQuery;
import com.agileboot.system.config.pojo.dto.ConfigUpdate;
import com.agileboot.system.config.pojo.entity.SysConfig;
import com.agileboot.system.config.service.ISysConfigService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -51,11 +53,13 @@ public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig
}
@Override
public void updateConfig(ConfigUpdateCommand updateCommand) {
ConfigModel configModel = configModelFactory.loadById(updateCommand.getConfigId());
configModel.loadUpdateCommand(updateCommand);
configModel.checkCanBeModify();
configModel.updateById();
CacheCenter.configCache.invalidate(configModel.getConfigKey());
public void updateConfig(Long id, ConfigUpdate update) {
if (StringUtils.isBlank(update.getConfigValue())) {
throw new BizException("");
}
SysConfig entity = new SysConfig();
entity.setId(id);
entity.setConfigValue(update.getConfigValue());
this.updateById(entity);
}
}

View File

@@ -1,12 +1,18 @@
package com.agileboot.common.core.enums.common;
package com.agileboot.system.enums;
import com.agileboot.common.core.enums.BasicEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 系统配置
*
* @author valarchie
* 对应 sys_config表的config_key字段
*/
@Getter
@AllArgsConstructor
public enum ConfigKeyEnum implements BasicEnum<String> {
/**
@@ -19,22 +25,5 @@ public enum ConfigKeyEnum implements BasicEnum<String> {
REGISTER("sys.account.registerUser", "注册开放功能");
private final String value;
private final String description;
ConfigKeyEnum(String value, String description) {
this.value = value;
this.description = description;
}
@Override
public String getValue() {
return value;
}
@Override
public String description() {
return description;
}
private final String desc;
}

View File

@@ -0,0 +1,105 @@
package com.agileboot.system.login.controller;
import com.agileboot.common.core.core.R;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.system.config.pojo.dto.ConfigDTO;
import com.agileboot.system.login.pojo.vo.CaptchaVO;
import com.agileboot.system.login.service.LoginService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 登录相关接口
*
* @author valarchie
*/
@RestController
@RequiredArgsConstructor
public class LoginController {
private final LoginService loginService;
private final MenuAService menuService;
private final UserService userService;
/**
* 获取系统的内置配置
*
* @return 配置信息
*/
@GetMapping("/getConfig")
public R<ConfigDTO> getConfig() {
return R.ok(loginService.getConfig());
}
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public R<CaptchaVO> getCaptchaImg() {
return R.ok(loginService.generateCaptchaImg());
}
/**
* 登录方法
*
* @param loginCommand 登录信息
* @return 结果
*/
@PostMapping("/login")
public R<TokenDTO> login(@RequestBody LoginCommand loginCommand) {
// 生成令牌
String token = loginService.login(loginCommand);
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
CurrentLoginUserDTO currentUserDTO = userService.getLoginUserInfo(loginUser);
return R.ok(new TokenDTO(token, currentUserDTO));
}
/**
* 获取当前登录用户信息
*
* @return 用户信息
*/
@GetMapping("/getLoginUserInfo")
public R<CurrentLoginUserDTO> getLoginUserInfo() {
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
CurrentLoginUserDTO currentUserDTO = userService.getLoginUserInfo(loginUser);
return R.ok(currentUserDTO);
}
/**
* 获取用户对应的菜单路由 用于动态生成路由
* TODO 如果要在前端开启路由缓存的话 需要在ServerConfig.json 中 设置CachingAsyncRoutes=true 避免一直重复请求路由接口
*
* @return 路由信息
*/
@GetMapping("/getRouters")
public R<List<RouterDTO>> getRouters() {
SystemLoginUser loginUser = AuthenticationUtils.getSystemLoginUser();
List<RouterDTO> routerTree = menuService.getRouterTree(loginUser);
return R.ok(routerTree);
}
/**
* 注册接口
*
* @param command
* @return
*/
@PostMapping("/register")
public R<Void> register(@RequestBody AddUserCommand command) {
throw new BizException(ErrorCode.Business.COMMON_UNSUPPORTED_OPERATION);
}
}

View File

@@ -0,0 +1,15 @@
package com.agileboot.system.login.pojo.vo;
import lombok.Data;
/**
* @author valarchie
*/
@Data
public class CaptchaVO {
private Boolean isCaptchaOn;
private String captchaCodeKey;
private String captchaCodeImg;
}

View File

@@ -0,0 +1,205 @@
package com.agileboot.system.login.service;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.extra.servlet.ServletUtil;
import com.agileboot.common.core.config.AgileBootConfig;
import com.agileboot.common.core.enums.common.LoginStatusEnum;
import com.agileboot.common.core.exception.BizException;
import com.agileboot.common.core.exception.error.ErrorCode;
import com.agileboot.common.core.utils.ServletHolderUtil;
import com.agileboot.system.config.pojo.dto.ConfigDTO;
import com.agileboot.system.enums.ConfigKeyEnum;
import com.agileboot.system.login.pojo.vo.CaptchaVO;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import java.awt.image.BufferedImage;
/**
* 登录校验方法
*
* @author ruoyi
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class LoginService {
private final TokenService tokenService;
private final RedisCacheService redisCache;
private final GuavaCacheService guavaCache;
private final AuthenticationManager authenticationManager;
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
/**
* 登录验证
*
* @param loginCommand 登录参数
* @return 结果
*/
public String login(LoginCommand loginCommand) {
// 验证码开关
if (isCaptchaOn()) {
validateCaptcha(loginCommand.getUsername(), loginCommand.getCaptchaCode(), loginCommand.getCaptchaCodeKey());
}
// 用户验证
Authentication authentication;
String decryptPassword = decryptPassword(loginCommand.getPassword());
try {
// 该方法会去调用UserDetailsServiceImpl#loadUserByUsername 校验用户名和密码 认证鉴权
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginCommand.getUsername(), decryptPassword));
} catch (BadCredentialsException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL,
MessageUtils.message("Business.LOGIN_WRONG_USER_PASSWORD")));
throw new ApiException(e, ErrorCode.Business.LOGIN_WRONG_USER_PASSWORD);
} catch (AuthenticationException e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, ErrorCode.Business.LOGIN_ERROR, e.getMessage());
} catch (Exception e) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginCommand.getUsername(), LoginStatusEnum.LOGIN_FAIL, e.getMessage()));
throw new ApiException(e, Business.LOGIN_ERROR, e.getMessage());
}
// 把当前登录用户 放入上下文中
SecurityContextHolder.getContext().setAuthentication(authentication);
// 这里获取的loginUser是UserDetailsServiceImpl#loadUserByUsername方法返回的LoginUser
SystemLoginUser loginUser = (SystemLoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser);
// 生成token
return tokenService.createTokenAndPutUserInCache(loginUser);
}
/**
* 获取验证码 data
*
* @return {@link ConfigDTO}
*/
public ConfigDTO getConfig() {
ConfigDTO configDTO = new ConfigDTO();
boolean isCaptchaOn = isCaptchaOn();
configDTO.setIsCaptchaOn(isCaptchaOn);
configDTO.setDictionary(MapCache.dictionaryCache());
return configDTO;
}
/**
* 获取验证码 data
*
* @return 验证码
*/
public CaptchaVO generateCaptchaImg() {
CaptchaVO captchaVO = new CaptchaVO();
boolean isCaptchaOn = isCaptchaOn();
captchaVO.setIsCaptchaOn(isCaptchaOn);
if (isCaptchaOn) {
String expression;
String answer = null;
BufferedImage image = null;
// 生成验证码
String captchaType = AgileBootConfig.getCaptchaType();
if (Captcha.MATH_TYPE.equals(captchaType)) {
String capText = captchaProducerMath.createText();
String[] expressionAndAnswer = capText.split("@");
expression = expressionAndAnswer[0];
answer = expressionAndAnswer[1];
image = captchaProducerMath.createImage(expression);
}
if (Captcha.CHAR_TYPE.equals(captchaType)) {
expression = answer = captchaProducer.createText();
image = captchaProducer.createImage(expression);
}
if (image == null) {
throw new BizException(ErrorCode.Internal.LOGIN_CAPTCHA_GENERATE_FAIL);
}
// 保存验证码信息
String imgKey = IdUtil.simpleUUID();
redisCache.captchaCache.set(imgKey, answer);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
ImgUtil.writeJpg(image, os);
captchaVO.setCaptchaCodeKey(imgKey);
captchaVO.setCaptchaCodeImg(Base64.encode(os.toByteArray()));
}
return captchaVO;
}
/**
* 校验验证码
*
* @param username 用户名
* @param captchaCode 验证码
* @param captchaCodeKey 验证码对应的缓存key
*/
public void validateCaptcha(String username, String captchaCode, String captchaCodeKey) {
String captcha = redisCache.captchaCache.getObjectById(captchaCodeKey);
redisCache.captchaCache.delete(captchaCodeKey);
if (captcha == null) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(username, LoginStatusEnum.LOGIN_FAIL,
ErrorCode.Business.LOGIN_CAPTCHA_CODE_EXPIRE.message()));
throw new BizException(ErrorCode.Business.LOGIN_CAPTCHA_CODE_EXPIRE);
}
if (!captchaCode.equalsIgnoreCase(captcha)) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(username, LoginStatusEnum.LOGIN_FAIL,
ErrorCode.Business.LOGIN_CAPTCHA_CODE_WRONG.message()));
throw new BizException(ErrorCode.Business.LOGIN_CAPTCHA_CODE_WRONG);
}
}
/**
* 记录登录信息
* @param loginUser 登录用户
*/
public void recordLoginInfo(SystemLoginUser loginUser) {
ThreadPoolManager.execute(AsyncTaskFactory.loginInfoTask(loginUser.getUsername(), LoginStatusEnum.LOGIN_SUCCESS,
LoginStatusEnum.LOGIN_SUCCESS.getDesc()));
SysUserEntity entity = redisCache.userCache.getObjectById(loginUser.getUserId());
entity.setLoginIp(ServletUtil.getClientIP(ServletHolderUtil.getRequest()));
entity.setLoginDate(DateUtil.date());
entity.updateById();
}
public String decryptPassword(String originalPassword) {
byte[] decryptBytes = SecureUtil.rsa(AgileBootConfig.getRsaPrivateKey(), null)
.decrypt(Base64.decode(originalPassword), KeyType.PrivateKey);
return StrUtil.str(decryptBytes, CharsetUtil.CHARSET_UTF_8);
}
private boolean isCaptchaOn() {
return Convert.toBool(guavaCache.configCache.get(ConfigKeyEnum.CAPTCHA.getValue()));
}
}