新增用户安全相关的
This commit is contained in:
@@ -0,0 +1,106 @@
|
|||||||
|
package com.vetti.web.controller.hotake;
|
||||||
|
|
||||||
|
import com.vetti.common.core.domain.AjaxResult;
|
||||||
|
import com.vetti.common.core.domain.R;
|
||||||
|
import com.vetti.common.utils.MessageUtils;
|
||||||
|
import com.vetti.hotake.domain.HotakeSecuritySettings;
|
||||||
|
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
|
||||||
|
import com.vetti.hotake.domain.vo.SecuritySessionVo;
|
||||||
|
import com.vetti.hotake.service.IHotakeSecurityService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import io.swagger.annotations.ApiParam;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全设置控制器
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Api(tags = "安全设置模块")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/security")
|
||||||
|
public class HotakeSecurityController
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private IHotakeSecurityService securityService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的安全设置
|
||||||
|
*/
|
||||||
|
@ApiOperation("获取当前用户的安全设置")
|
||||||
|
@GetMapping("/settings")
|
||||||
|
public R<HotakeSecuritySettings> getSecuritySettings()
|
||||||
|
{
|
||||||
|
HotakeSecuritySettings settings = securityService.getCurrentUserSecuritySettings();
|
||||||
|
// 脱敏处理,不返回敏感信息
|
||||||
|
settings.setTwoFactorSecret(null);
|
||||||
|
settings.setBackupCodes(null);
|
||||||
|
settings.setPasswordResetToken(null);
|
||||||
|
return R.ok(settings, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新两步验证设置
|
||||||
|
*/
|
||||||
|
@ApiOperation("更新两步验证设置")
|
||||||
|
@PutMapping("/two-factor")
|
||||||
|
public AjaxResult updateTwoFactorEnabled(
|
||||||
|
@ApiParam(value = "是否启用", required = true)
|
||||||
|
@RequestParam Boolean enabled)
|
||||||
|
{
|
||||||
|
securityService.updateTwoFactorEnabled(enabled);
|
||||||
|
return AjaxResult.success(MessageUtils.messageCustomize("HotakeSecurityController10001"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*/
|
||||||
|
@ApiOperation("修改密码")
|
||||||
|
@PostMapping("/change-password")
|
||||||
|
public AjaxResult changePassword(@Validated @RequestBody SecurityChangePasswordDto dto)
|
||||||
|
{
|
||||||
|
securityService.changePassword(dto);
|
||||||
|
return AjaxResult.success(MessageUtils.messageCustomize("HotakeSecurityController10002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活跃会话列表
|
||||||
|
*/
|
||||||
|
@ApiOperation("获取活跃会话列表")
|
||||||
|
@GetMapping("/sessions")
|
||||||
|
public R<List<SecuritySessionVo>> getActiveSessions()
|
||||||
|
{
|
||||||
|
List<SecuritySessionVo> sessions = securityService.getActiveSessions();
|
||||||
|
return R.ok(sessions, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止指定会话
|
||||||
|
*/
|
||||||
|
@ApiOperation("终止指定会话")
|
||||||
|
@DeleteMapping("/sessions/{sessionId}")
|
||||||
|
public AjaxResult terminateSession(
|
||||||
|
@ApiParam(value = "会话ID", required = true)
|
||||||
|
@PathVariable Long sessionId)
|
||||||
|
{
|
||||||
|
securityService.terminateSession(sessionId);
|
||||||
|
return AjaxResult.success(MessageUtils.messageCustomize("HotakeSecurityController10003"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止所有其他会话
|
||||||
|
*/
|
||||||
|
@ApiOperation("终止所有其他会话")
|
||||||
|
@DeleteMapping("/sessions/terminate-all")
|
||||||
|
public AjaxResult terminateAllOtherSessions()
|
||||||
|
{
|
||||||
|
securityService.terminateAllOtherSessions();
|
||||||
|
return AjaxResult.success(MessageUtils.messageCustomize("HotakeSecurityController10004"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,28 @@ public class SysLoginController
|
|||||||
LoginDto loginDto = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
|
LoginDto loginDto = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
|
||||||
loginBody.getUuid());
|
loginBody.getUuid());
|
||||||
|
|
||||||
|
// TODO: 安全功能集成 - 记录登录会话到安全表
|
||||||
|
// 在用户成功登录后,记录会话信息到hotake_security_login_sessions表
|
||||||
|
// 用于支持"Login Sessions"功能,显示用户的所有活跃会话
|
||||||
|
try {
|
||||||
|
String ipAddress = com.vetti.common.utils.ip.IpUtils.getIpAddr();
|
||||||
|
String userAgent = com.vetti.common.utils.ServletUtils.getRequest().getHeader("User-Agent");
|
||||||
|
// 使用Spring的ApplicationContext来获取bean,避免循环依赖
|
||||||
|
try {
|
||||||
|
Object securityService = com.vetti.common.utils.spring.SpringUtils.getBean("hotakeSecurityServiceImpl");
|
||||||
|
if (securityService != null) {
|
||||||
|
// 使用反射调用方法
|
||||||
|
java.lang.reflect.Method method = securityService.getClass().getMethod(
|
||||||
|
"recordLoginSession", Long.class, String.class, String.class, String.class);
|
||||||
|
method.invoke(securityService, loginDto.getUserId(), loginDto.getToken(), ipAddress, userAgent);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 安全服务不存在或调用失败,不影响登录流程
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 记录会话失败不影响登录流程
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是候选者,查询是否有简历
|
// 如果是候选者,查询是否有简历
|
||||||
if (loginDto.getUser() != null && "candidate".equals(loginDto.getUser().getSysUserType())) {
|
if (loginDto.getUser() != null && "candidate".equals(loginDto.getUser().getSysUserType())) {
|
||||||
HotakeCvInfo query = new HotakeCvInfo();
|
HotakeCvInfo query = new HotakeCvInfo();
|
||||||
@@ -180,11 +202,7 @@ public class SysLoginController
|
|||||||
public AjaxResult logout()
|
public AjaxResult logout()
|
||||||
{
|
{
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
if (loginUser != null)
|
loginService.logout(loginUser);
|
||||||
{
|
|
||||||
// 删除用户缓存记录
|
|
||||||
tokenService.delLoginUser(loginUser.getToken());
|
|
||||||
}
|
|
||||||
return AjaxResult.success("退出成功");
|
return AjaxResult.success("退出成功");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
# 服务器的HTTP端口,默认为8080
|
||||||
port: 8080
|
port: 8686
|
||||||
servlet:
|
servlet:
|
||||||
# 应用的访问路径
|
# 应用的访问路径
|
||||||
context-path: /
|
context-path: /
|
||||||
|
|||||||
@@ -65,4 +65,24 @@ VerificationEmailTiTle = Your verification code
|
|||||||
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.
|
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.
|
||||||
|
|
||||||
|
|
||||||
HotakeRolesApplyInfoServiceImpl10001 = You have already applied for this position
|
HotakeRolesApplyInfoServiceImpl10001 = You have already applied for this position
|
||||||
|
|
||||||
|
# Security settings related messages
|
||||||
|
HotakeSecurityServiceImpl10001 = New password and confirm password do not match
|
||||||
|
HotakeSecurityServiceImpl10002 = Current password is incorrect
|
||||||
|
HotakeSecurityServiceImpl10003 = New password cannot be the same as the last 5 passwords used
|
||||||
|
HotakeSecurityServiceImpl10004 = Session does not exist or no permission to operate
|
||||||
|
HotakeSecurityServiceImpl10005 = Failed to change password: current password is incorrect
|
||||||
|
HotakeSecurityServiceImpl10006 = Two-factor authentication enabled
|
||||||
|
HotakeSecurityServiceImpl10007 = Two-factor authentication disabled
|
||||||
|
HotakeSecurityServiceImpl10008 = Password changed successfully
|
||||||
|
HotakeSecurityServiceImpl10009 = Terminate session
|
||||||
|
HotakeSecurityServiceImpl10010 = Terminate all other sessions
|
||||||
|
|
||||||
|
HotakeSecurityController10001 = Two-factor authentication settings updated
|
||||||
|
HotakeSecurityController10002 = Password changed successfully
|
||||||
|
HotakeSecurityController10003 = Session terminated
|
||||||
|
HotakeSecurityController10004 = All other sessions terminated
|
||||||
|
|
||||||
|
# Logout related
|
||||||
|
HotakeSecurityServiceImpl10011 = User logged out
|
||||||
@@ -62,4 +62,24 @@ HotakeRolesInfoServiceImpl10001 = 岗位信息异常,请稍后再试
|
|||||||
VerificationEmailTiTle = 你的验证码
|
VerificationEmailTiTle = 你的验证码
|
||||||
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。
|
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。
|
||||||
|
|
||||||
HotakeRolesApplyInfoServiceImpl10001 = 您已申请该职位
|
HotakeRolesApplyInfoServiceImpl10001 = 您已申请该职位
|
||||||
|
|
||||||
|
# 安全设置相关消息
|
||||||
|
HotakeSecurityServiceImpl10001 = 新密码和确认密码不一致
|
||||||
|
HotakeSecurityServiceImpl10002 = 当前密码错误
|
||||||
|
HotakeSecurityServiceImpl10003 = 新密码不能与最近5次使用过的密码相同
|
||||||
|
HotakeSecurityServiceImpl10004 = 会话不存在或无权操作
|
||||||
|
HotakeSecurityServiceImpl10005 = 修改密码失败:当前密码错误
|
||||||
|
HotakeSecurityServiceImpl10006 = 启用两步验证
|
||||||
|
HotakeSecurityServiceImpl10007 = 禁用两步验证
|
||||||
|
HotakeSecurityServiceImpl10008 = 修改密码成功
|
||||||
|
HotakeSecurityServiceImpl10009 = 终止会话
|
||||||
|
HotakeSecurityServiceImpl10010 = 终止所有其他会话
|
||||||
|
|
||||||
|
HotakeSecurityController10001 = 两步验证设置已更新
|
||||||
|
HotakeSecurityController10002 = 密码修改成功
|
||||||
|
HotakeSecurityController10003 = 会话已终止
|
||||||
|
HotakeSecurityController10004 = 所有其他会话已终止
|
||||||
|
|
||||||
|
# 退出登录相关
|
||||||
|
HotakeSecurityServiceImpl10011 = 用户退出登录
|
||||||
@@ -65,4 +65,24 @@ VerificationEmailTiTle = Your verification code
|
|||||||
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.
|
VerificationEmailContent = Your verification code is: {0}, valid for {1} minutes.
|
||||||
|
|
||||||
|
|
||||||
HotakeRolesApplyInfoServiceImpl10001 = You have already applied for this position
|
HotakeRolesApplyInfoServiceImpl10001 = You have already applied for this position
|
||||||
|
|
||||||
|
# Security settings related messages
|
||||||
|
HotakeSecurityServiceImpl10001 = New password and confirm password do not match
|
||||||
|
HotakeSecurityServiceImpl10002 = Current password is incorrect
|
||||||
|
HotakeSecurityServiceImpl10003 = New password cannot be the same as the last 5 passwords used
|
||||||
|
HotakeSecurityServiceImpl10004 = Session does not exist or no permission to operate
|
||||||
|
HotakeSecurityServiceImpl10005 = Failed to change password: current password is incorrect
|
||||||
|
HotakeSecurityServiceImpl10006 = Two-factor authentication enabled
|
||||||
|
HotakeSecurityServiceImpl10007 = Two-factor authentication disabled
|
||||||
|
HotakeSecurityServiceImpl10008 = Password changed successfully
|
||||||
|
HotakeSecurityServiceImpl10009 = Terminate session
|
||||||
|
HotakeSecurityServiceImpl10010 = Terminate all other sessions
|
||||||
|
|
||||||
|
HotakeSecurityController10001 = Two-factor authentication settings updated
|
||||||
|
HotakeSecurityController10002 = Password changed successfully
|
||||||
|
HotakeSecurityController10003 = Session terminated
|
||||||
|
HotakeSecurityController10004 = All other sessions terminated
|
||||||
|
|
||||||
|
# Logout related
|
||||||
|
HotakeSecurityServiceImpl10011 = User logged out
|
||||||
@@ -62,4 +62,24 @@ HotakeRolesInfoServiceImpl10001 = 岗位信息异常,请稍后再试
|
|||||||
VerificationEmailTiTle = 你的验证码
|
VerificationEmailTiTle = 你的验证码
|
||||||
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。
|
VerificationEmailContent = 你的验证码是: {0},有效期为 {1} 分钟。
|
||||||
|
|
||||||
HotakeRolesApplyInfoServiceImpl10001 = 您已申请该职位
|
HotakeRolesApplyInfoServiceImpl10001 = 您已申请该职位
|
||||||
|
|
||||||
|
# 安全设置相关消息
|
||||||
|
HotakeSecurityServiceImpl10001 = 新密码和确认密码不一致
|
||||||
|
HotakeSecurityServiceImpl10002 = 当前密码错误
|
||||||
|
HotakeSecurityServiceImpl10003 = 新密码不能与最近5次使用过的密码相同
|
||||||
|
HotakeSecurityServiceImpl10004 = 会话不存在或无权操作
|
||||||
|
HotakeSecurityServiceImpl10005 = 修改密码失败:当前密码错误
|
||||||
|
HotakeSecurityServiceImpl10006 = 启用两步验证
|
||||||
|
HotakeSecurityServiceImpl10007 = 禁用两步验证
|
||||||
|
HotakeSecurityServiceImpl10008 = 修改密码成功
|
||||||
|
HotakeSecurityServiceImpl10009 = 终止会话
|
||||||
|
HotakeSecurityServiceImpl10010 = 终止所有其他会话
|
||||||
|
|
||||||
|
HotakeSecurityController10001 = 两步验证设置已更新
|
||||||
|
HotakeSecurityController10002 = 密码修改成功
|
||||||
|
HotakeSecurityController10003 = 会话已终止
|
||||||
|
HotakeSecurityController10004 = 所有其他会话已终止
|
||||||
|
|
||||||
|
# 退出登录相关
|
||||||
|
HotakeSecurityServiceImpl10011 = 用户退出登录
|
||||||
@@ -233,4 +233,35 @@ public class SysLoginService
|
|||||||
userService.resetUserPwd(sysUser.getUserId(), SecurityUtils.encryptPassword(password));
|
userService.resetUserPwd(sysUser.getUserId(), SecurityUtils.encryptPassword(password));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*
|
||||||
|
* @param loginUser 登录用户信息
|
||||||
|
*/
|
||||||
|
public void logout(LoginUser loginUser)
|
||||||
|
{
|
||||||
|
if (loginUser != null)
|
||||||
|
{
|
||||||
|
String token = loginUser.getToken();
|
||||||
|
|
||||||
|
// TODO: 安全功能集成 - 更新会话状态为已登出
|
||||||
|
// 在用户退出登录时,更新hotake_security_login_sessions表中的会话状态
|
||||||
|
// 记录登出时间,并将is_active设置为0
|
||||||
|
try {
|
||||||
|
Object securityService = com.vetti.common.utils.spring.SpringUtils.getBean("hotakeSecurityServiceImpl");
|
||||||
|
if (securityService != null) {
|
||||||
|
// 使用反射调用更新会话状态的方法
|
||||||
|
java.lang.reflect.Method method = securityService.getClass().getMethod(
|
||||||
|
"updateSessionLogout", String.class);
|
||||||
|
method.invoke(securityService, token);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 更新会话状态失败不影响退出流程
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户缓存记录
|
||||||
|
tokenService.delLoginUser(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package com.vetti.hotake.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.vetti.common.annotation.Excel;
|
||||||
|
import com.vetti.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录会话对象 hotake_security_login_sessions
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel("用户登录会话")
|
||||||
|
public class HotakeSecurityLoginSessions extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 用户ID */
|
||||||
|
@ApiModelProperty("用户ID")
|
||||||
|
@Excel(name = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 会话ID */
|
||||||
|
@ApiModelProperty("会话ID")
|
||||||
|
@Excel(name = "会话ID")
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
/** 会话令牌 */
|
||||||
|
@ApiModelProperty("会话令牌")
|
||||||
|
private String sessionToken;
|
||||||
|
|
||||||
|
/** 设备类型 Desktop/Mobile/Tablet */
|
||||||
|
@ApiModelProperty("设备类型")
|
||||||
|
@Excel(name = "设备类型")
|
||||||
|
private String deviceType;
|
||||||
|
|
||||||
|
/** 设备名称 */
|
||||||
|
@ApiModelProperty("设备名称")
|
||||||
|
@Excel(name = "设备名称")
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
|
/** 浏览器 */
|
||||||
|
@ApiModelProperty("浏览器")
|
||||||
|
@Excel(name = "浏览器")
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/** 浏览器版本 */
|
||||||
|
@ApiModelProperty("浏览器版本")
|
||||||
|
private String browserVersion;
|
||||||
|
|
||||||
|
/** 操作系统 */
|
||||||
|
@ApiModelProperty("操作系统")
|
||||||
|
@Excel(name = "操作系统")
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
/** 操作系统版本 */
|
||||||
|
@ApiModelProperty("操作系统版本")
|
||||||
|
private String osVersion;
|
||||||
|
|
||||||
|
/** IP地址 */
|
||||||
|
@ApiModelProperty("IP地址")
|
||||||
|
@Excel(name = "IP地址")
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
/** 国家 */
|
||||||
|
@ApiModelProperty("国家")
|
||||||
|
@Excel(name = "国家")
|
||||||
|
private String locationCountry;
|
||||||
|
|
||||||
|
/** 城市 */
|
||||||
|
@ApiModelProperty("城市")
|
||||||
|
@Excel(name = "城市")
|
||||||
|
private String locationCity;
|
||||||
|
|
||||||
|
/** 是否活跃 0-否 1-是 */
|
||||||
|
@ApiModelProperty("是否活跃")
|
||||||
|
@Excel(name = "是否活跃", readConverterExp = "0=否,1=是")
|
||||||
|
private Integer isActive;
|
||||||
|
|
||||||
|
/** 最后活动时间 */
|
||||||
|
@ApiModelProperty("最后活动时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "最后活动时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date lastActivityTime;
|
||||||
|
|
||||||
|
/** 登录时间 */
|
||||||
|
@ApiModelProperty("登录时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date loginTime;
|
||||||
|
|
||||||
|
/** 登出时间 */
|
||||||
|
@ApiModelProperty("登出时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "登出时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date logoutTime;
|
||||||
|
|
||||||
|
/** 会话过期时间 */
|
||||||
|
@ApiModelProperty("会话过期时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "会话过期时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date expiresAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.vetti.hotake.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.vetti.common.annotation.Excel;
|
||||||
|
import com.vetti.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码历史记录对象 hotake_security_password_history
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel("密码历史记录")
|
||||||
|
public class HotakeSecurityPasswordHistory extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 用户ID */
|
||||||
|
@ApiModelProperty("用户ID")
|
||||||
|
@Excel(name = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 密码哈希值 */
|
||||||
|
@ApiModelProperty("密码哈希值")
|
||||||
|
private String passwordHash;
|
||||||
|
|
||||||
|
/** 修改时间 */
|
||||||
|
@ApiModelProperty("修改时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "修改时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date changedAt;
|
||||||
|
|
||||||
|
/** 修改方式 USER/ADMIN/RESET */
|
||||||
|
@ApiModelProperty("修改方式")
|
||||||
|
@Excel(name = "修改方式")
|
||||||
|
private String changedBy;
|
||||||
|
|
||||||
|
/** IP地址 */
|
||||||
|
@ApiModelProperty("IP地址")
|
||||||
|
@Excel(name = "IP地址")
|
||||||
|
private String ipAddress;
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.vetti.hotake.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.vetti.common.annotation.Excel;
|
||||||
|
import com.vetti.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全日志对象 hotake_security_security_logs
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel("安全日志")
|
||||||
|
public class HotakeSecuritySecurityLogs extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 用户ID */
|
||||||
|
@ApiModelProperty("用户ID")
|
||||||
|
@Excel(name = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 事件类型 */
|
||||||
|
@ApiModelProperty("事件类型")
|
||||||
|
@Excel(name = "事件类型")
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
/** 事件描述 */
|
||||||
|
@ApiModelProperty("事件描述")
|
||||||
|
@Excel(name = "事件描述")
|
||||||
|
private String eventDescription;
|
||||||
|
|
||||||
|
/** IP地址 */
|
||||||
|
@ApiModelProperty("IP地址")
|
||||||
|
@Excel(name = "IP地址")
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
/** 用户代理 */
|
||||||
|
@ApiModelProperty("用户代理")
|
||||||
|
private String userAgent;
|
||||||
|
|
||||||
|
/** 地理位置 */
|
||||||
|
@ApiModelProperty("地理位置")
|
||||||
|
@Excel(name = "地理位置")
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/** 状态 SUCCESS/FAILED/BLOCKED */
|
||||||
|
@ApiModelProperty("状态")
|
||||||
|
@Excel(name = "状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 元数据(JSON格式) */
|
||||||
|
@ApiModelProperty("元数据")
|
||||||
|
private String metadata;
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.vetti.hotake.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.vetti.common.annotation.Excel;
|
||||||
|
import com.vetti.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户安全设置对象 hotake_security_settings
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel("用户安全设置")
|
||||||
|
public class HotakeSecuritySettings extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 用户ID */
|
||||||
|
@ApiModelProperty("用户ID")
|
||||||
|
@Excel(name = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 是否启用两步验证 0-否 1-是 */
|
||||||
|
@ApiModelProperty("是否启用两步验证 0-否 1-是")
|
||||||
|
@Excel(name = "是否启用两步验证", readConverterExp = "0=否,1=是")
|
||||||
|
private Integer twoFactorEnabled;
|
||||||
|
|
||||||
|
/** 两步验证密钥 */
|
||||||
|
@ApiModelProperty("两步验证密钥")
|
||||||
|
private String twoFactorSecret;
|
||||||
|
|
||||||
|
/** 两步验证类型 TOTP/SMS/EMAIL */
|
||||||
|
@ApiModelProperty("两步验证类型")
|
||||||
|
@Excel(name = "两步验证类型")
|
||||||
|
private String twoFactorType;
|
||||||
|
|
||||||
|
/** 备份验证码(JSON格式) */
|
||||||
|
@ApiModelProperty("备份验证码")
|
||||||
|
private String backupCodes;
|
||||||
|
|
||||||
|
/** 密码最后修改时间 */
|
||||||
|
@ApiModelProperty("密码最后修改时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "密码最后修改时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date passwordChangedAt;
|
||||||
|
|
||||||
|
/** 密码重置令牌 */
|
||||||
|
@ApiModelProperty("密码重置令牌")
|
||||||
|
private String passwordResetToken;
|
||||||
|
|
||||||
|
/** 密码重置令牌过期时间 */
|
||||||
|
@ApiModelProperty("密码重置令牌过期时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date passwordResetExpires;
|
||||||
|
|
||||||
|
/** 失败登录尝试次数 */
|
||||||
|
@ApiModelProperty("失败登录尝试次数")
|
||||||
|
@Excel(name = "失败登录尝试次数")
|
||||||
|
private Integer failedLoginAttempts;
|
||||||
|
|
||||||
|
/** 账户锁定截止时间 */
|
||||||
|
@ApiModelProperty("账户锁定截止时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "账户锁定截止时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date accountLockedUntil;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.vetti.hotake.domain;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.vetti.common.annotation.Excel;
|
||||||
|
import com.vetti.common.core.domain.BaseEntity;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可信设备对象 hotake_security_trusted_devices
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel("可信设备")
|
||||||
|
public class HotakeSecurityTrustedDevices extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键ID */
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 用户ID */
|
||||||
|
@ApiModelProperty("用户ID")
|
||||||
|
@Excel(name = "用户ID")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/** 设备唯一标识 */
|
||||||
|
@ApiModelProperty("设备唯一标识")
|
||||||
|
@Excel(name = "设备唯一标识")
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
/** 设备名称 */
|
||||||
|
@ApiModelProperty("设备名称")
|
||||||
|
@Excel(name = "设备名称")
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
|
/** 设备类型 */
|
||||||
|
@ApiModelProperty("设备类型")
|
||||||
|
@Excel(name = "设备类型")
|
||||||
|
private String deviceType;
|
||||||
|
|
||||||
|
/** 浏览器 */
|
||||||
|
@ApiModelProperty("浏览器")
|
||||||
|
@Excel(name = "浏览器")
|
||||||
|
private String browser;
|
||||||
|
|
||||||
|
/** 操作系统 */
|
||||||
|
@ApiModelProperty("操作系统")
|
||||||
|
@Excel(name = "操作系统")
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
/** IP地址 */
|
||||||
|
@ApiModelProperty("IP地址")
|
||||||
|
@Excel(name = "IP地址")
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
/** 地理位置 */
|
||||||
|
@ApiModelProperty("地理位置")
|
||||||
|
@Excel(name = "地理位置")
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/** 是否可信 0-否 1-是 */
|
||||||
|
@ApiModelProperty("是否可信")
|
||||||
|
@Excel(name = "是否可信", readConverterExp = "0=否,1=是")
|
||||||
|
private Integer isTrusted;
|
||||||
|
|
||||||
|
/** 信任过期时间 */
|
||||||
|
@ApiModelProperty("信任过期时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
@Excel(name = "信任过期时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date trustExpiresAt;
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.vetti.hotake.service;
|
||||||
|
|
||||||
|
import com.vetti.hotake.domain.HotakeSecuritySettings;
|
||||||
|
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
|
||||||
|
import com.vetti.hotake.domain.vo.SecuritySessionVo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全设置Service接口
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
public interface IHotakeSecurityService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 获取当前用户的安全设置
|
||||||
|
* 如果不存在则自动创建默认设置
|
||||||
|
*
|
||||||
|
* @return 安全设置
|
||||||
|
*/
|
||||||
|
HotakeSecuritySettings getCurrentUserSecuritySettings();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新两步验证设置
|
||||||
|
*
|
||||||
|
* @param enabled 是否启用
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateTwoFactorEnabled(boolean enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*
|
||||||
|
* @param dto 修改密码DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
void changePassword(SecurityChangePasswordDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的活跃会话列表
|
||||||
|
*
|
||||||
|
* @return 会话列表
|
||||||
|
*/
|
||||||
|
List<SecuritySessionVo> getActiveSessions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止指定会话
|
||||||
|
*
|
||||||
|
* @param sessionId 会话ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int terminateSession(Long sessionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止所有其他会话(除了当前会话)
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int terminateAllOtherSessions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录登录会话
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @param ipAddress IP地址
|
||||||
|
* @param userAgent 用户代理
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int recordLoginSession(Long userId, String sessionToken, String ipAddress, String userAgent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新会话活动时间
|
||||||
|
*
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateSessionActivity(String sessionToken);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录安全日志
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param eventType 事件类型
|
||||||
|
* @param eventDescription 事件描述
|
||||||
|
* @param status 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int recordSecurityLog(Long userId, String eventType, String eventDescription, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新会话为已登出状态
|
||||||
|
*
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateSessionLogout(String sessionToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
package com.vetti.hotake.service.impl;
|
||||||
|
|
||||||
|
import com.vetti.common.core.domain.model.LoginUser;
|
||||||
|
import com.vetti.common.core.service.BaseServiceImpl;
|
||||||
|
import com.vetti.common.enums.FillTypeEnum;
|
||||||
|
import com.vetti.common.exception.ServiceException;
|
||||||
|
import com.vetti.common.utils.MessageUtils;
|
||||||
|
import com.vetti.common.utils.SecurityUtils;
|
||||||
|
import com.vetti.common.utils.ServletUtils;
|
||||||
|
import com.vetti.common.utils.StringUtils;
|
||||||
|
import com.vetti.common.utils.ip.AddressUtils;
|
||||||
|
import com.vetti.common.utils.ip.IpUtils;
|
||||||
|
import com.vetti.common.constant.CacheConstants;
|
||||||
|
import com.vetti.common.core.redis.RedisCache;
|
||||||
|
import com.vetti.hotake.domain.HotakeSecurityLoginSessions;
|
||||||
|
import com.vetti.hotake.domain.HotakeSecurityPasswordHistory;
|
||||||
|
import com.vetti.hotake.domain.HotakeSecuritySecurityLogs;
|
||||||
|
import com.vetti.hotake.domain.HotakeSecuritySettings;
|
||||||
|
import com.vetti.hotake.domain.dto.SecurityChangePasswordDto;
|
||||||
|
import com.vetti.hotake.domain.vo.SecuritySessionVo;
|
||||||
|
import com.vetti.hotake.mapper.HotakeSecurityLoginSessionsMapper;
|
||||||
|
import com.vetti.hotake.mapper.HotakeSecurityPasswordHistoryMapper;
|
||||||
|
import com.vetti.hotake.mapper.HotakeSecuritySecurityLogsMapper;
|
||||||
|
import com.vetti.hotake.mapper.HotakeSecuritySettingsMapper;
|
||||||
|
import com.vetti.hotake.service.IHotakeSecurityService;
|
||||||
|
import com.vetti.system.service.ISysUserService;
|
||||||
|
import eu.bitwalker.useragentutils.Browser;
|
||||||
|
import eu.bitwalker.useragentutils.OperatingSystem;
|
||||||
|
import eu.bitwalker.useragentutils.UserAgent;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全设置Service业务层处理
|
||||||
|
*
|
||||||
|
* @author vetti
|
||||||
|
* @date 2026-02-02
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class HotakeSecurityServiceImpl extends BaseServiceImpl implements IHotakeSecurityService
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private HotakeSecuritySettingsMapper securitySettingsMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HotakeSecurityLoginSessionsMapper loginSessionsMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HotakeSecurityPasswordHistoryMapper passwordHistoryMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HotakeSecuritySecurityLogsMapper securityLogsMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISysUserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
|
@Value("${token.expireTime}")
|
||||||
|
private int expireTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的安全设置
|
||||||
|
* 如果不存在则自动创建默认设置
|
||||||
|
*
|
||||||
|
* @return 安全设置
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public HotakeSecuritySettings getCurrentUserSecuritySettings()
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
|
||||||
|
// 查询用户的安全设置
|
||||||
|
HotakeSecuritySettings settings = securitySettingsMapper.selectHotakeSecuritySettingsByUserId(userId);
|
||||||
|
|
||||||
|
// 如果不存在,创建默认设置
|
||||||
|
if (settings == null)
|
||||||
|
{
|
||||||
|
settings = new HotakeSecuritySettings();
|
||||||
|
settings.setUserId(userId);
|
||||||
|
settings.setTwoFactorEnabled(0);
|
||||||
|
settings.setTwoFactorType("TOTP");
|
||||||
|
settings.setFailedLoginAttempts(0);
|
||||||
|
|
||||||
|
fill(FillTypeEnum.INSERT.getCode(), settings);
|
||||||
|
securitySettingsMapper.insertHotakeSecuritySettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新两步验证设置
|
||||||
|
*
|
||||||
|
* @param enabled 是否启用
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int updateTwoFactorEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
HotakeSecuritySettings settings = getCurrentUserSecuritySettings();
|
||||||
|
|
||||||
|
settings.setTwoFactorEnabled(enabled ? 1 : 0);
|
||||||
|
fill(FillTypeEnum.UPDATE.getCode(), settings);
|
||||||
|
|
||||||
|
// 记录安全日志
|
||||||
|
String eventType = enabled ? "2FA_ENABLED" : "2FA_DISABLED";
|
||||||
|
String eventDesc = enabled ?
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10006") :
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10007");
|
||||||
|
recordSecurityLog(userId, eventType, eventDesc, "SUCCESS");
|
||||||
|
|
||||||
|
return securitySettingsMapper.updateHotakeSecuritySettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改密码
|
||||||
|
*
|
||||||
|
* @param dto 修改密码DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void changePassword(SecurityChangePasswordDto dto)
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
String username = SecurityUtils.getUsername();
|
||||||
|
|
||||||
|
// 验证新密码和确认密码是否一致
|
||||||
|
if (!dto.getNewPassword().equals(dto.getConfirmPassword()))
|
||||||
|
{
|
||||||
|
throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10001"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证当前密码是否正确
|
||||||
|
if (!SecurityUtils.matchesPassword(dto.getCurrentPassword(), SecurityUtils.getLoginUser().getPassword()))
|
||||||
|
{
|
||||||
|
// 记录失败日志
|
||||||
|
recordSecurityLog(userId, "PASSWORD_CHANGE",
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10005"), "FAILED");
|
||||||
|
throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 安全功能集成 - 检查新密码是否与历史密码重复
|
||||||
|
// 查询最近5次的密码历史
|
||||||
|
List<HotakeSecurityPasswordHistory> historyList = passwordHistoryMapper.selectRecentPasswordHistory(userId, 5);
|
||||||
|
String newPasswordHash = SecurityUtils.encryptPassword(dto.getNewPassword());
|
||||||
|
|
||||||
|
for (HotakeSecurityPasswordHistory history : historyList)
|
||||||
|
{
|
||||||
|
if (SecurityUtils.matchesPassword(dto.getNewPassword(), history.getPasswordHash()))
|
||||||
|
{
|
||||||
|
throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10003"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码
|
||||||
|
userService.resetUserPwd(userId, newPasswordHash);
|
||||||
|
|
||||||
|
// 记录密码历史
|
||||||
|
HotakeSecurityPasswordHistory history = new HotakeSecurityPasswordHistory();
|
||||||
|
history.setUserId(userId);
|
||||||
|
history.setPasswordHash(newPasswordHash);
|
||||||
|
history.setChangedAt(new Date());
|
||||||
|
history.setChangedBy("USER");
|
||||||
|
history.setIpAddress(IpUtils.getIpAddr());
|
||||||
|
fill(FillTypeEnum.INSERT.getCode(), history);
|
||||||
|
passwordHistoryMapper.insertHotakeSecurityPasswordHistory(history);
|
||||||
|
|
||||||
|
// 更新安全设置中的密码修改时间
|
||||||
|
HotakeSecuritySettings settings = getCurrentUserSecuritySettings();
|
||||||
|
settings.setPasswordChangedAt(new Date());
|
||||||
|
fill(FillTypeEnum.UPDATE.getCode(), settings);
|
||||||
|
securitySettingsMapper.updateHotakeSecuritySettings(settings);
|
||||||
|
|
||||||
|
// 记录安全日志
|
||||||
|
recordSecurityLog(userId, "PASSWORD_CHANGE",
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10008"), "SUCCESS");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户的活跃会话列表
|
||||||
|
*
|
||||||
|
* @return 会话列表
|
||||||
|
*/
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
@Override
|
||||||
|
public List<SecuritySessionVo> getActiveSessions()
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
String currentToken = loginUser.getToken();
|
||||||
|
|
||||||
|
// 查询用户的活跃会话
|
||||||
|
List<HotakeSecurityLoginSessions> sessionsList = loginSessionsMapper.selectActiveSessionsByUserId(userId);
|
||||||
|
|
||||||
|
// 转换为VO
|
||||||
|
List<SecuritySessionVo> voList = new ArrayList<>();
|
||||||
|
for (HotakeSecurityLoginSessions session : sessionsList)
|
||||||
|
{
|
||||||
|
SecuritySessionVo vo = new SecuritySessionVo();
|
||||||
|
BeanUtils.copyProperties(session, vo);
|
||||||
|
|
||||||
|
// 组合设备名称
|
||||||
|
String deviceName = "";
|
||||||
|
if (StringUtils.isNotEmpty(session.getBrowser()))
|
||||||
|
{
|
||||||
|
deviceName = session.getBrowser();
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(session.getOs()))
|
||||||
|
{
|
||||||
|
deviceName += (StringUtils.isNotEmpty(deviceName) ? " / " : "") + session.getOs();
|
||||||
|
}
|
||||||
|
vo.setDeviceName(deviceName);
|
||||||
|
|
||||||
|
// 组合地理位置
|
||||||
|
String location = "";
|
||||||
|
if (StringUtils.isNotEmpty(session.getLocationCity()))
|
||||||
|
{
|
||||||
|
location = session.getLocationCity();
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(session.getLocationCountry()))
|
||||||
|
{
|
||||||
|
location += (StringUtils.isNotEmpty(location) ? ", " : "") + session.getLocationCountry();
|
||||||
|
}
|
||||||
|
vo.setLocation(location);
|
||||||
|
|
||||||
|
// 判断是否为当前会话
|
||||||
|
vo.setIsCurrent(session.getSessionToken().equals(currentToken));
|
||||||
|
vo.setIsActive(session.getIsActive() == 1);
|
||||||
|
|
||||||
|
voList.add(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return voList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止指定会话
|
||||||
|
*
|
||||||
|
* @param sessionId 会话ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int terminateSession(Long sessionId)
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
|
||||||
|
// 查询会话信息
|
||||||
|
HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsById(sessionId);
|
||||||
|
if (session == null || !session.getUserId().equals(userId))
|
||||||
|
{
|
||||||
|
throw new ServiceException(MessageUtils.messageCustomize("HotakeSecurityServiceImpl10004"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 安全功能集成 - 从Redis中删除对应的token缓存
|
||||||
|
// 这样可以立即使该会话失效
|
||||||
|
// 使用RedisCache直接删除,避免依赖TokenService
|
||||||
|
String userKey = CacheConstants.LOGIN_TOKEN_KEY + session.getSessionToken();
|
||||||
|
redisCache.deleteObject(userKey);
|
||||||
|
|
||||||
|
// 更新会话状态
|
||||||
|
int result = loginSessionsMapper.terminateSession(sessionId, userId);
|
||||||
|
|
||||||
|
// 记录安全日志
|
||||||
|
recordSecurityLog(userId, "SESSION_TERMINATE",
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10009") + ":" + session.getDeviceName(),
|
||||||
|
"SUCCESS");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止所有其他会话(除了当前会话)
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int terminateAllOtherSessions()
|
||||||
|
{
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
String currentToken = loginUser.getToken();
|
||||||
|
|
||||||
|
// TODO: 安全功能集成 - 从Redis中批量删除其他会话的token缓存
|
||||||
|
// 查询所有其他活跃会话
|
||||||
|
List<HotakeSecurityLoginSessions> otherSessions = loginSessionsMapper.selectActiveSessionsByUserId(userId);
|
||||||
|
for (HotakeSecurityLoginSessions session : otherSessions)
|
||||||
|
{
|
||||||
|
if (!session.getSessionToken().equals(currentToken))
|
||||||
|
{
|
||||||
|
// 使用RedisCache直接删除,避免依赖TokenService
|
||||||
|
String userKey = CacheConstants.LOGIN_TOKEN_KEY + session.getSessionToken();
|
||||||
|
redisCache.deleteObject(userKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新会话状态
|
||||||
|
int result = loginSessionsMapper.terminateOtherSessions(userId, currentToken);
|
||||||
|
|
||||||
|
// 记录安全日志
|
||||||
|
recordSecurityLog(userId, "SESSION_TERMINATE_ALL",
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10010"),
|
||||||
|
"SUCCESS");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录登录会话
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @param ipAddress IP地址
|
||||||
|
* @param userAgent 用户代理
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int recordLoginSession(Long userId, String sessionToken, String ipAddress, String userAgent)
|
||||||
|
{
|
||||||
|
// 解析User-Agent
|
||||||
|
UserAgent ua = UserAgent.parseUserAgentString(userAgent);
|
||||||
|
Browser browser = ua.getBrowser();
|
||||||
|
OperatingSystem os = ua.getOperatingSystem();
|
||||||
|
|
||||||
|
// 获取地理位置
|
||||||
|
String location = AddressUtils.getRealAddressByIP(ipAddress);
|
||||||
|
String[] locationParts = location.split(" ");
|
||||||
|
String country = locationParts.length > 0 ? locationParts[0] : "";
|
||||||
|
String city = locationParts.length > 1 ? locationParts[1] : "";
|
||||||
|
|
||||||
|
// 判断设备类型
|
||||||
|
String deviceType = "Desktop";
|
||||||
|
if (os.getDeviceType() != null)
|
||||||
|
{
|
||||||
|
deviceType = os.getDeviceType().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建会话记录
|
||||||
|
HotakeSecurityLoginSessions session = new HotakeSecurityLoginSessions();
|
||||||
|
session.setUserId(userId);
|
||||||
|
session.setSessionId(sessionToken);
|
||||||
|
session.setSessionToken(sessionToken);
|
||||||
|
session.setDeviceType(deviceType);
|
||||||
|
session.setDeviceName(browser.getName() + " / " + os.getName());
|
||||||
|
session.setBrowser(browser.getName());
|
||||||
|
session.setBrowserVersion(ua.getBrowserVersion() != null ? ua.getBrowserVersion().getVersion() : "");
|
||||||
|
session.setOs(os.getName());
|
||||||
|
session.setOsVersion("");
|
||||||
|
session.setIpAddress(ipAddress);
|
||||||
|
session.setLocationCountry(country);
|
||||||
|
session.setLocationCity(city);
|
||||||
|
session.setIsActive(1);
|
||||||
|
session.setLoginTime(new Date());
|
||||||
|
session.setLastActivityTime(new Date());
|
||||||
|
|
||||||
|
// 计算过期时间
|
||||||
|
Date expiresAt = new Date(System.currentTimeMillis() + expireTime * 60 * 1000L);
|
||||||
|
session.setExpiresAt(expiresAt);
|
||||||
|
|
||||||
|
fill(FillTypeEnum.INSERT.getCode(), session);
|
||||||
|
return loginSessionsMapper.insertHotakeSecurityLoginSessions(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新会话活动时间
|
||||||
|
*
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int updateSessionActivity(String sessionToken)
|
||||||
|
{
|
||||||
|
HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsByToken(sessionToken);
|
||||||
|
if (session != null)
|
||||||
|
{
|
||||||
|
session.setLastActivityTime(new Date());
|
||||||
|
fill(FillTypeEnum.UPDATE.getCode(), session);
|
||||||
|
return loginSessionsMapper.updateHotakeSecurityLoginSessions(session);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录安全日志
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param eventType 事件类型
|
||||||
|
* @param eventDescription 事件描述
|
||||||
|
* @param status 状态
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int recordSecurityLog(Long userId, String eventType, String eventDescription, String status)
|
||||||
|
{
|
||||||
|
HotakeSecuritySecurityLogs log = new HotakeSecuritySecurityLogs();
|
||||||
|
log.setUserId(userId);
|
||||||
|
log.setEventType(eventType);
|
||||||
|
log.setEventDescription(eventDescription);
|
||||||
|
log.setIpAddress(IpUtils.getIpAddr());
|
||||||
|
log.setUserAgent(StringUtils.substring(ServletUtils.getRequest().getHeader("User-Agent"), 0, 500));
|
||||||
|
log.setLocation(AddressUtils.getRealAddressByIP(IpUtils.getIpAddr()));
|
||||||
|
log.setStatus(status);
|
||||||
|
|
||||||
|
fill(FillTypeEnum.INSERT.getCode(), log);
|
||||||
|
return securityLogsMapper.insertHotakeSecuritySecurityLogs(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新会话为已登出状态
|
||||||
|
*
|
||||||
|
* @param sessionToken 会话令牌
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@Override
|
||||||
|
public int updateSessionLogout(String sessionToken)
|
||||||
|
{
|
||||||
|
// 查询会话信息
|
||||||
|
HotakeSecurityLoginSessions session = loginSessionsMapper.selectHotakeSecurityLoginSessionsByToken(sessionToken);
|
||||||
|
if (session != null && session.getIsActive() == 1)
|
||||||
|
{
|
||||||
|
// 更新会话状态
|
||||||
|
session.setIsActive(0);
|
||||||
|
session.setLogoutTime(new Date());
|
||||||
|
fill(FillTypeEnum.UPDATE.getCode(), session);
|
||||||
|
|
||||||
|
// 记录安全日志
|
||||||
|
recordSecurityLog(session.getUserId(), "LOGOUT",
|
||||||
|
MessageUtils.messageCustomize("HotakeSecurityServiceImpl10011"), "SUCCESS");
|
||||||
|
|
||||||
|
return loginSessionsMapper.updateHotakeSecurityLoginSessions(session);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.vetti.hotake.mapper.HotakeSecurityLoginSessionsMapper">
|
||||||
|
|
||||||
|
<resultMap type="HotakeSecurityLoginSessions" id="HotakeSecurityLoginSessionsResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="sessionId" column="session_id" />
|
||||||
|
<result property="sessionToken" column="session_token" />
|
||||||
|
<result property="deviceType" column="device_type" />
|
||||||
|
<result property="deviceName" column="device_name" />
|
||||||
|
<result property="browser" column="browser" />
|
||||||
|
<result property="browserVersion" column="browser_version" />
|
||||||
|
<result property="os" column="os" />
|
||||||
|
<result property="osVersion" column="os_version" />
|
||||||
|
<result property="ipAddress" column="ip_address" />
|
||||||
|
<result property="locationCountry" column="location_country" />
|
||||||
|
<result property="locationCity" column="location_city" />
|
||||||
|
<result property="isActive" column="is_active" />
|
||||||
|
<result property="lastActivityTime" column="last_activity_time" />
|
||||||
|
<result property="loginTime" column="login_time" />
|
||||||
|
<result property="logoutTime" column="logout_time" />
|
||||||
|
<result property="expiresAt" column="expires_at" />
|
||||||
|
<result property="delFlag" column="del_flag" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectHotakeSecurityLoginSessionsVo">
|
||||||
|
select id, user_id, session_id, session_token, device_type, device_name,
|
||||||
|
browser, browser_version, os, os_version, ip_address,
|
||||||
|
location_country, location_city, is_active, last_activity_time,
|
||||||
|
login_time, logout_time, expires_at,
|
||||||
|
del_flag, create_by, create_time, update_by, update_time, remark
|
||||||
|
from hotake_security_login_sessions
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecurityLoginSessionsList" parameterType="HotakeSecurityLoginSessions" resultMap="HotakeSecurityLoginSessionsResult">
|
||||||
|
<include refid="selectHotakeSecurityLoginSessionsVo"/>
|
||||||
|
<where>
|
||||||
|
del_flag = '0'
|
||||||
|
<if test="userId != null"> and user_id = #{userId}</if>
|
||||||
|
<if test="sessionToken != null and sessionToken != ''"> and session_token = #{sessionToken}</if>
|
||||||
|
<if test="isActive != null"> and is_active = #{isActive}</if>
|
||||||
|
</where>
|
||||||
|
order by login_time desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecurityLoginSessionsById" parameterType="Long" resultMap="HotakeSecurityLoginSessionsResult">
|
||||||
|
<include refid="selectHotakeSecurityLoginSessionsVo"/>
|
||||||
|
where id = #{id} and del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecurityLoginSessionsByToken" parameterType="String" resultMap="HotakeSecurityLoginSessionsResult">
|
||||||
|
<include refid="selectHotakeSecurityLoginSessionsVo"/>
|
||||||
|
where session_token = #{sessionToken} and del_flag = '0'
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectActiveSessionsByUserId" parameterType="Long" resultMap="HotakeSecurityLoginSessionsResult">
|
||||||
|
<include refid="selectHotakeSecurityLoginSessionsVo"/>
|
||||||
|
where user_id = #{userId}
|
||||||
|
and is_active = 1
|
||||||
|
and del_flag = '0'
|
||||||
|
and expires_at > now()
|
||||||
|
order by login_time desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertHotakeSecurityLoginSessions" parameterType="HotakeSecurityLoginSessions" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into hotake_security_login_sessions
|
||||||
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id,</if>
|
||||||
|
<if test="sessionId != null">session_id,</if>
|
||||||
|
<if test="sessionToken != null">session_token,</if>
|
||||||
|
<if test="deviceType != null">device_type,</if>
|
||||||
|
<if test="deviceName != null">device_name,</if>
|
||||||
|
<if test="browser != null">browser,</if>
|
||||||
|
<if test="browserVersion != null">browser_version,</if>
|
||||||
|
<if test="os != null">os,</if>
|
||||||
|
<if test="osVersion != null">os_version,</if>
|
||||||
|
<if test="ipAddress != null">ip_address,</if>
|
||||||
|
<if test="locationCountry != null">location_country,</if>
|
||||||
|
<if test="locationCity != null">location_city,</if>
|
||||||
|
<if test="isActive != null">is_active,</if>
|
||||||
|
<if test="lastActivityTime != null">last_activity_time,</if>
|
||||||
|
<if test="loginTime != null">login_time,</if>
|
||||||
|
<if test="logoutTime != null">logout_time,</if>
|
||||||
|
<if test="expiresAt != null">expires_at,</if>
|
||||||
|
<if test="delFlag != null">del_flag,</if>
|
||||||
|
<if test="createBy != null">create_by,</if>
|
||||||
|
<if test="createTime != null">create_time,</if>
|
||||||
|
<if test="updateBy != null">update_by,</if>
|
||||||
|
<if test="updateTime != null">update_time,</if>
|
||||||
|
<if test="remark != null">remark,</if>
|
||||||
|
</trim>
|
||||||
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">#{userId},</if>
|
||||||
|
<if test="sessionId != null">#{sessionId},</if>
|
||||||
|
<if test="sessionToken != null">#{sessionToken},</if>
|
||||||
|
<if test="deviceType != null">#{deviceType},</if>
|
||||||
|
<if test="deviceName != null">#{deviceName},</if>
|
||||||
|
<if test="browser != null">#{browser},</if>
|
||||||
|
<if test="browserVersion != null">#{browserVersion},</if>
|
||||||
|
<if test="os != null">#{os},</if>
|
||||||
|
<if test="osVersion != null">#{osVersion},</if>
|
||||||
|
<if test="ipAddress != null">#{ipAddress},</if>
|
||||||
|
<if test="locationCountry != null">#{locationCountry},</if>
|
||||||
|
<if test="locationCity != null">#{locationCity},</if>
|
||||||
|
<if test="isActive != null">#{isActive},</if>
|
||||||
|
<if test="lastActivityTime != null">#{lastActivityTime},</if>
|
||||||
|
<if test="loginTime != null">#{loginTime},</if>
|
||||||
|
<if test="logoutTime != null">#{logoutTime},</if>
|
||||||
|
<if test="expiresAt != null">#{expiresAt},</if>
|
||||||
|
<if test="delFlag != null">#{delFlag},</if>
|
||||||
|
<if test="createBy != null">#{createBy},</if>
|
||||||
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
|
<if test="remark != null">#{remark},</if>
|
||||||
|
</trim>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateHotakeSecurityLoginSessions" parameterType="HotakeSecurityLoginSessions">
|
||||||
|
update hotake_security_login_sessions
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id = #{userId},</if>
|
||||||
|
<if test="sessionId != null">session_id = #{sessionId},</if>
|
||||||
|
<if test="sessionToken != null">session_token = #{sessionToken},</if>
|
||||||
|
<if test="deviceType != null">device_type = #{deviceType},</if>
|
||||||
|
<if test="deviceName != null">device_name = #{deviceName},</if>
|
||||||
|
<if test="browser != null">browser = #{browser},</if>
|
||||||
|
<if test="browserVersion != null">browser_version = #{browserVersion},</if>
|
||||||
|
<if test="os != null">os = #{os},</if>
|
||||||
|
<if test="osVersion != null">os_version = #{osVersion},</if>
|
||||||
|
<if test="ipAddress != null">ip_address = #{ipAddress},</if>
|
||||||
|
<if test="locationCountry != null">location_country = #{locationCountry},</if>
|
||||||
|
<if test="locationCity != null">location_city = #{locationCity},</if>
|
||||||
|
<if test="isActive != null">is_active = #{isActive},</if>
|
||||||
|
<if test="lastActivityTime != null">last_activity_time = #{lastActivityTime},</if>
|
||||||
|
<if test="loginTime != null">login_time = #{loginTime},</if>
|
||||||
|
<if test="logoutTime != null">logout_time = #{logoutTime},</if>
|
||||||
|
<if test="expiresAt != null">expires_at = #{expiresAt},</if>
|
||||||
|
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||||
|
<if test="createBy != null">create_by = #{createBy},</if>
|
||||||
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="terminateOtherSessions">
|
||||||
|
update hotake_security_login_sessions
|
||||||
|
set is_active = 0,
|
||||||
|
logout_time = now(),
|
||||||
|
update_time = now()
|
||||||
|
where user_id = #{userId}
|
||||||
|
and session_token != #{currentSessionToken}
|
||||||
|
and is_active = 1
|
||||||
|
and del_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<update id="terminateSession">
|
||||||
|
update hotake_security_login_sessions
|
||||||
|
set is_active = 0,
|
||||||
|
logout_time = now(),
|
||||||
|
update_time = now()
|
||||||
|
where id = #{id}
|
||||||
|
and user_id = #{userId}
|
||||||
|
and del_flag = '0'
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecurityLoginSessionsById" parameterType="Long">
|
||||||
|
update hotake_security_login_sessions set del_flag = '2' where id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecurityLoginSessionsByIds" parameterType="String">
|
||||||
|
update hotake_security_login_sessions set del_flag = '2' where id in
|
||||||
|
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.vetti.hotake.mapper.HotakeSecurityPasswordHistoryMapper">
|
||||||
|
|
||||||
|
<resultMap type="HotakeSecurityPasswordHistory" id="HotakeSecurityPasswordHistoryResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="passwordHash" column="password_hash" />
|
||||||
|
<result property="changedAt" column="changed_at" />
|
||||||
|
<result property="changedBy" column="changed_by" />
|
||||||
|
<result property="ipAddress" column="ip_address" />
|
||||||
|
<result property="delFlag" column="del_flag" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectHotakeSecurityPasswordHistoryVo">
|
||||||
|
select id, user_id, password_hash, changed_at, changed_by, ip_address,
|
||||||
|
del_flag, create_by, create_time, update_by, update_time, remark
|
||||||
|
from hotake_security_password_history
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecurityPasswordHistoryList" parameterType="HotakeSecurityPasswordHistory" resultMap="HotakeSecurityPasswordHistoryResult">
|
||||||
|
<include refid="selectHotakeSecurityPasswordHistoryVo"/>
|
||||||
|
<where>
|
||||||
|
del_flag = '0'
|
||||||
|
<if test="userId != null"> and user_id = #{userId}</if>
|
||||||
|
</where>
|
||||||
|
order by changed_at desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecurityPasswordHistoryById" parameterType="Long" resultMap="HotakeSecurityPasswordHistoryResult">
|
||||||
|
<include refid="selectHotakeSecurityPasswordHistoryVo"/>
|
||||||
|
where id = #{id} and del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectRecentPasswordHistory" resultMap="HotakeSecurityPasswordHistoryResult">
|
||||||
|
<include refid="selectHotakeSecurityPasswordHistoryVo"/>
|
||||||
|
where user_id = #{userId} and del_flag = '0'
|
||||||
|
order by changed_at desc
|
||||||
|
limit #{limit}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertHotakeSecurityPasswordHistory" parameterType="HotakeSecurityPasswordHistory" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into hotake_security_password_history
|
||||||
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id,</if>
|
||||||
|
<if test="passwordHash != null">password_hash,</if>
|
||||||
|
<if test="changedAt != null">changed_at,</if>
|
||||||
|
<if test="changedBy != null">changed_by,</if>
|
||||||
|
<if test="ipAddress != null">ip_address,</if>
|
||||||
|
<if test="delFlag != null">del_flag,</if>
|
||||||
|
<if test="createBy != null">create_by,</if>
|
||||||
|
<if test="createTime != null">create_time,</if>
|
||||||
|
<if test="updateBy != null">update_by,</if>
|
||||||
|
<if test="updateTime != null">update_time,</if>
|
||||||
|
<if test="remark != null">remark,</if>
|
||||||
|
</trim>
|
||||||
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">#{userId},</if>
|
||||||
|
<if test="passwordHash != null">#{passwordHash},</if>
|
||||||
|
<if test="changedAt != null">#{changedAt},</if>
|
||||||
|
<if test="changedBy != null">#{changedBy},</if>
|
||||||
|
<if test="ipAddress != null">#{ipAddress},</if>
|
||||||
|
<if test="delFlag != null">#{delFlag},</if>
|
||||||
|
<if test="createBy != null">#{createBy},</if>
|
||||||
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
|
<if test="remark != null">#{remark},</if>
|
||||||
|
</trim>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateHotakeSecurityPasswordHistory" parameterType="HotakeSecurityPasswordHistory">
|
||||||
|
update hotake_security_password_history
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id = #{userId},</if>
|
||||||
|
<if test="passwordHash != null">password_hash = #{passwordHash},</if>
|
||||||
|
<if test="changedAt != null">changed_at = #{changedAt},</if>
|
||||||
|
<if test="changedBy != null">changed_by = #{changedBy},</if>
|
||||||
|
<if test="ipAddress != null">ip_address = #{ipAddress},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecurityPasswordHistoryById" parameterType="Long">
|
||||||
|
update hotake_security_password_history set del_flag = '2' where id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecurityPasswordHistoryByIds" parameterType="String">
|
||||||
|
update hotake_security_password_history set del_flag = '2' where id in
|
||||||
|
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.vetti.hotake.mapper.HotakeSecuritySecurityLogsMapper">
|
||||||
|
|
||||||
|
<resultMap type="HotakeSecuritySecurityLogs" id="HotakeSecuritySecurityLogsResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="eventType" column="event_type" />
|
||||||
|
<result property="eventDescription" column="event_description" />
|
||||||
|
<result property="ipAddress" column="ip_address" />
|
||||||
|
<result property="userAgent" column="user_agent" />
|
||||||
|
<result property="location" column="location" />
|
||||||
|
<result property="status" column="status" />
|
||||||
|
<result property="metadata" column="metadata" />
|
||||||
|
<result property="delFlag" column="del_flag" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectHotakeSecuritySecurityLogsVo">
|
||||||
|
select id, user_id, event_type, event_description, ip_address, user_agent,
|
||||||
|
location, status, metadata,
|
||||||
|
del_flag, create_by, create_time, update_by, update_time, remark
|
||||||
|
from hotake_security_security_logs
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecuritySecurityLogsList" parameterType="HotakeSecuritySecurityLogs" resultMap="HotakeSecuritySecurityLogsResult">
|
||||||
|
<include refid="selectHotakeSecuritySecurityLogsVo"/>
|
||||||
|
<where>
|
||||||
|
del_flag = '0'
|
||||||
|
<if test="userId != null"> and user_id = #{userId}</if>
|
||||||
|
<if test="eventType != null and eventType != ''"> and event_type = #{eventType}</if>
|
||||||
|
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||||
|
</where>
|
||||||
|
order by create_time desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecuritySecurityLogsById" parameterType="Long" resultMap="HotakeSecuritySecurityLogsResult">
|
||||||
|
<include refid="selectHotakeSecuritySecurityLogsVo"/>
|
||||||
|
where id = #{id} and del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertHotakeSecuritySecurityLogs" parameterType="HotakeSecuritySecurityLogs" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into hotake_security_security_logs
|
||||||
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id,</if>
|
||||||
|
<if test="eventType != null">event_type,</if>
|
||||||
|
<if test="eventDescription != null">event_description,</if>
|
||||||
|
<if test="ipAddress != null">ip_address,</if>
|
||||||
|
<if test="userAgent != null">user_agent,</if>
|
||||||
|
<if test="location != null">location,</if>
|
||||||
|
<if test="status != null">status,</if>
|
||||||
|
<if test="metadata != null">metadata,</if>
|
||||||
|
<if test="delFlag != null">del_flag,</if>
|
||||||
|
<if test="createBy != null">create_by,</if>
|
||||||
|
<if test="createTime != null">create_time,</if>
|
||||||
|
<if test="updateBy != null">update_by,</if>
|
||||||
|
<if test="updateTime != null">update_time,</if>
|
||||||
|
<if test="remark != null">remark,</if>
|
||||||
|
</trim>
|
||||||
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">#{userId},</if>
|
||||||
|
<if test="eventType != null">#{eventType},</if>
|
||||||
|
<if test="eventDescription != null">#{eventDescription},</if>
|
||||||
|
<if test="ipAddress != null">#{ipAddress},</if>
|
||||||
|
<if test="userAgent != null">#{userAgent},</if>
|
||||||
|
<if test="location != null">#{location},</if>
|
||||||
|
<if test="status != null">#{status},</if>
|
||||||
|
<if test="metadata != null">#{metadata},</if>
|
||||||
|
<if test="delFlag != null">#{delFlag},</if>
|
||||||
|
<if test="createBy != null">#{createBy},</if>
|
||||||
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
|
<if test="remark != null">#{remark},</if>
|
||||||
|
</trim>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateHotakeSecuritySecurityLogs" parameterType="HotakeSecuritySecurityLogs">
|
||||||
|
update hotake_security_security_logs
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id = #{userId},</if>
|
||||||
|
<if test="eventType != null">event_type = #{eventType},</if>
|
||||||
|
<if test="eventDescription != null">event_description = #{eventDescription},</if>
|
||||||
|
<if test="ipAddress != null">ip_address = #{ipAddress},</if>
|
||||||
|
<if test="userAgent != null">user_agent = #{userAgent},</if>
|
||||||
|
<if test="location != null">location = #{location},</if>
|
||||||
|
<if test="status != null">status = #{status},</if>
|
||||||
|
<if test="metadata != null">metadata = #{metadata},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecuritySecurityLogsById" parameterType="Long">
|
||||||
|
update hotake_security_security_logs set del_flag = '2' where id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecuritySecurityLogsByIds" parameterType="String">
|
||||||
|
update hotake_security_security_logs set del_flag = '2' where id in
|
||||||
|
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.vetti.hotake.mapper.HotakeSecuritySettingsMapper">
|
||||||
|
|
||||||
|
<resultMap type="HotakeSecuritySettings" id="HotakeSecuritySettingsResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="userId" column="user_id" />
|
||||||
|
<result property="twoFactorEnabled" column="two_factor_enabled" />
|
||||||
|
<result property="twoFactorSecret" column="two_factor_secret" />
|
||||||
|
<result property="twoFactorType" column="two_factor_type" />
|
||||||
|
<result property="backupCodes" column="backup_codes" />
|
||||||
|
<result property="passwordChangedAt" column="password_changed_at" />
|
||||||
|
<result property="passwordResetToken" column="password_reset_token" />
|
||||||
|
<result property="passwordResetExpires" column="password_reset_expires" />
|
||||||
|
<result property="failedLoginAttempts" column="failed_login_attempts" />
|
||||||
|
<result property="accountLockedUntil" column="account_locked_until" />
|
||||||
|
<result property="delFlag" column="del_flag" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectHotakeSecuritySettingsVo">
|
||||||
|
select id, user_id, two_factor_enabled, two_factor_secret, two_factor_type, backup_codes,
|
||||||
|
password_changed_at, password_reset_token, password_reset_expires,
|
||||||
|
failed_login_attempts, account_locked_until,
|
||||||
|
del_flag, create_by, create_time, update_by, update_time, remark
|
||||||
|
from hotake_security_settings
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecuritySettingsList" parameterType="HotakeSecuritySettings" resultMap="HotakeSecuritySettingsResult">
|
||||||
|
<include refid="selectHotakeSecuritySettingsVo"/>
|
||||||
|
<where>
|
||||||
|
del_flag = '0'
|
||||||
|
<if test="userId != null"> and user_id = #{userId}</if>
|
||||||
|
<if test="twoFactorEnabled != null"> and two_factor_enabled = #{twoFactorEnabled}</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecuritySettingsById" parameterType="Long" resultMap="HotakeSecuritySettingsResult">
|
||||||
|
<include refid="selectHotakeSecuritySettingsVo"/>
|
||||||
|
where id = #{id} and del_flag = '0'
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectHotakeSecuritySettingsByUserId" parameterType="Long" resultMap="HotakeSecuritySettingsResult">
|
||||||
|
<include refid="selectHotakeSecuritySettingsVo"/>
|
||||||
|
where user_id = #{userId} and del_flag = '0'
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertHotakeSecuritySettings" parameterType="HotakeSecuritySettings" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into hotake_security_settings
|
||||||
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id,</if>
|
||||||
|
<if test="twoFactorEnabled != null">two_factor_enabled,</if>
|
||||||
|
<if test="twoFactorSecret != null">two_factor_secret,</if>
|
||||||
|
<if test="twoFactorType != null">two_factor_type,</if>
|
||||||
|
<if test="backupCodes != null">backup_codes,</if>
|
||||||
|
<if test="passwordChangedAt != null">password_changed_at,</if>
|
||||||
|
<if test="passwordResetToken != null">password_reset_token,</if>
|
||||||
|
<if test="passwordResetExpires != null">password_reset_expires,</if>
|
||||||
|
<if test="failedLoginAttempts != null">failed_login_attempts,</if>
|
||||||
|
<if test="accountLockedUntil != null">account_locked_until,</if>
|
||||||
|
<if test="delFlag != null">del_flag,</if>
|
||||||
|
<if test="createBy != null">create_by,</if>
|
||||||
|
<if test="createTime != null">create_time,</if>
|
||||||
|
<if test="updateBy != null">update_by,</if>
|
||||||
|
<if test="updateTime != null">update_time,</if>
|
||||||
|
<if test="remark != null">remark,</if>
|
||||||
|
</trim>
|
||||||
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="userId != null">#{userId},</if>
|
||||||
|
<if test="twoFactorEnabled != null">#{twoFactorEnabled},</if>
|
||||||
|
<if test="twoFactorSecret != null">#{twoFactorSecret},</if>
|
||||||
|
<if test="twoFactorType != null">#{twoFactorType},</if>
|
||||||
|
<if test="backupCodes != null">#{backupCodes},</if>
|
||||||
|
<if test="passwordChangedAt != null">#{passwordChangedAt},</if>
|
||||||
|
<if test="passwordResetToken != null">#{passwordResetToken},</if>
|
||||||
|
<if test="passwordResetExpires != null">#{passwordResetExpires},</if>
|
||||||
|
<if test="failedLoginAttempts != null">#{failedLoginAttempts},</if>
|
||||||
|
<if test="accountLockedUntil != null">#{accountLockedUntil},</if>
|
||||||
|
<if test="delFlag != null">#{delFlag},</if>
|
||||||
|
<if test="createBy != null">#{createBy},</if>
|
||||||
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
|
<if test="remark != null">#{remark},</if>
|
||||||
|
</trim>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateHotakeSecuritySettings" parameterType="HotakeSecuritySettings">
|
||||||
|
update hotake_security_settings
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="userId != null">user_id = #{userId},</if>
|
||||||
|
<if test="twoFactorEnabled != null">two_factor_enabled = #{twoFactorEnabled},</if>
|
||||||
|
<if test="twoFactorSecret != null">two_factor_secret = #{twoFactorSecret},</if>
|
||||||
|
<if test="twoFactorType != null">two_factor_type = #{twoFactorType},</if>
|
||||||
|
<if test="backupCodes != null">backup_codes = #{backupCodes},</if>
|
||||||
|
<if test="passwordChangedAt != null">password_changed_at = #{passwordChangedAt},</if>
|
||||||
|
<if test="passwordResetToken != null">password_reset_token = #{passwordResetToken},</if>
|
||||||
|
<if test="passwordResetExpires != null">password_reset_expires = #{passwordResetExpires},</if>
|
||||||
|
<if test="failedLoginAttempts != null">failed_login_attempts = #{failedLoginAttempts},</if>
|
||||||
|
<if test="accountLockedUntil != null">account_locked_until = #{accountLockedUntil},</if>
|
||||||
|
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||||
|
<if test="createBy != null">create_by = #{createBy},</if>
|
||||||
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecuritySettingsById" parameterType="Long">
|
||||||
|
update hotake_security_settings set del_flag = '2' where id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteHotakeSecuritySettingsByIds" parameterType="String">
|
||||||
|
update hotake_security_settings set del_flag = '2' where id in
|
||||||
|
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user