mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-19 09:55:16 +08:00
Pre Merge pull request !578 from knightdreams6/dev
This commit is contained in:
commit
05a99e3b3b
8
pom.xml
8
pom.xml
@ -40,6 +40,7 @@
|
||||
<justauth.version>1.16.6</justauth.version>
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<googleauth.version>1.4.0</googleauth.version>
|
||||
<undertow.version>2.3.15.Final</undertow.version>
|
||||
|
||||
<!-- OSS 配置 -->
|
||||
@ -312,6 +313,13 @@
|
||||
<version>${ip2region.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OTP谷歌验证 -->
|
||||
<dependency>
|
||||
<groupId>com.warrenstrange</groupId>
|
||||
<artifactId>googleauth</artifactId>
|
||||
<version>${googleauth.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
|
||||
@ -5,6 +5,7 @@ import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.constant.Constants;
|
||||
@ -46,6 +47,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
||||
private final CaptchaProperties captchaProperties;
|
||||
private final SysLoginService loginService;
|
||||
private final SysUserMapper userMapper;
|
||||
private final GoogleAuthenticator googleAuthenticator;
|
||||
|
||||
@Override
|
||||
public LoginVo login(String body, SysClientVo client) {
|
||||
@ -65,6 +67,13 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
||||
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
|
||||
SysUserVo user = loadUserByUsername(username);
|
||||
loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
|
||||
|
||||
// 校验otp code
|
||||
boolean otpAuthorize = googleAuthenticator.authorize(user.getOtpSecret(), loginBody.getOtpCode());
|
||||
if (!otpAuthorize) {
|
||||
throw new UserException("user.password.otp.code.error");
|
||||
}
|
||||
|
||||
// 此处可根据登录用户的数据不同 自行创建 loginUser
|
||||
return loginService.buildLoginUser(user);
|
||||
});
|
||||
|
||||
@ -16,6 +16,8 @@ user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,
|
||||
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
||||
user.password.not.blank=用户密码不能为空
|
||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||
user.password.otp.code.not.blank=OTP验证码不能为空
|
||||
user.password.otp.code.error=OTP验证码错误
|
||||
user.password.not.valid=* 5-50个字符
|
||||
user.email.not.valid=邮箱格式错误
|
||||
user.email.not.blank=邮箱不能为空
|
||||
|
||||
@ -17,6 +17,8 @@ user.username.length.valid=Account length must be between {min} and {max} charac
|
||||
user.password.not.blank=Password cannot be empty
|
||||
user.password.length.valid=Password length must be between {min} and {max} characters
|
||||
user.password.not.valid=* 5-50 characters
|
||||
user.password.otp.code.not.blank=OTP code cannot be empty
|
||||
user.password.otp.code.error=OTP code error
|
||||
user.email.not.valid=Mailbox format error
|
||||
user.email.not.blank=Mailbox cannot be blank
|
||||
user.phonenumber.not.blank=Phone number cannot be blank
|
||||
|
||||
@ -17,6 +17,8 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
||||
user.password.not.blank=用户密码不能为空
|
||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||
user.password.not.valid=* 5-50个字符
|
||||
user.password.otp.code.not.blank=OTP验证码不能为空
|
||||
user.password.otp.code.error=OTP验证码错误
|
||||
user.email.not.valid=邮箱格式错误
|
||||
user.email.not.blank=邮箱不能为空
|
||||
user.phonenumber.not.blank=用户手机号不能为空
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
@ -30,4 +31,10 @@ public class PasswordLoginBody extends LoginBody {
|
||||
@Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* OTP验证码
|
||||
*/
|
||||
@NotNull(message = "{user.password.otp.code.not.blank}")
|
||||
private Integer otpCode;
|
||||
|
||||
}
|
||||
|
||||
@ -70,6 +70,11 @@
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.warrenstrange</groupId>
|
||||
<artifactId>googleauth</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package org.dromara.common.web.config;
|
||||
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 谷歌验证配置
|
||||
*
|
||||
* @author knight
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class GoogleAuthConfig {
|
||||
|
||||
@Bean
|
||||
public GoogleAuthenticator googleAuthenticator() {
|
||||
return new GoogleAuthenticator();
|
||||
}
|
||||
|
||||
}
|
||||
@ -221,6 +221,18 @@ public class SysUserController extends BaseController {
|
||||
return R.ok(userService.selectUserByIds(userIds == null ? null : List.of(userIds), deptId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置OTP秘钥
|
||||
*/
|
||||
@ApiEncrypt
|
||||
@SaCheckPermission("system:user:resetPwd")
|
||||
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/resetOtpSecret/{userId}")
|
||||
public R<Void> resetOtpSecret(@PathVariable Long userId) {
|
||||
userService.checkUserDataScope(userId);
|
||||
return toAjax(userService.resetOtpSecret(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
|
||||
@ -103,6 +103,15 @@ public class SysUser extends TenantEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* One-Time Password 秘钥
|
||||
*/
|
||||
private String otpSecret;
|
||||
|
||||
/**
|
||||
* One-Time Password 秘钥绑定链接
|
||||
*/
|
||||
private String otpUrl;
|
||||
|
||||
public SysUser(Long userId) {
|
||||
this.userId = userId;
|
||||
|
||||
@ -103,6 +103,16 @@ public class SysUserVo implements Serializable {
|
||||
*/
|
||||
private Date loginDate;
|
||||
|
||||
/**
|
||||
* One-Time Password 秘钥
|
||||
*/
|
||||
private String otpSecret;
|
||||
|
||||
/**
|
||||
* One-Time Password 秘钥绑定链接
|
||||
*/
|
||||
private String otpUrl;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@ -187,6 +187,14 @@ public interface ISysUserService {
|
||||
*/
|
||||
boolean updateUserAvatar(Long userId, Long avatar);
|
||||
|
||||
/**
|
||||
* 重置用户OTP秘钥
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
int resetOtpSecret(Long userId);
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*
|
||||
|
||||
@ -11,12 +11,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticator;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticatorKey;
|
||||
import com.warrenstrange.googleauth.GoogleAuthenticatorQRGenerator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.config.RuoYiConfig;
|
||||
import org.dromara.common.core.constant.CacheNames;
|
||||
import org.dromara.common.core.constant.UserConstants;
|
||||
import org.dromara.common.core.domain.dto.UserDTO;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.exception.user.UserException;
|
||||
import org.dromara.common.core.service.UserService;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
@ -59,6 +64,8 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
private final SysPostMapper postMapper;
|
||||
private final SysUserRoleMapper userRoleMapper;
|
||||
private final SysUserPostMapper userPostMapper;
|
||||
private final GoogleAuthenticator googleAuthenticator;
|
||||
private final RuoYiConfig ruoYiConfig;
|
||||
|
||||
@Override
|
||||
public TableDataInfo<SysUserVo> selectPageUserList(SysUserBo user, PageQuery pageQuery) {
|
||||
@ -304,6 +311,13 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int insertUser(SysUserBo user) {
|
||||
SysUser sysUser = MapstructUtils.convert(user, SysUser.class);
|
||||
|
||||
GoogleAuthenticatorKey credentials = googleAuthenticator.createCredentials();
|
||||
String otpAuthTotpURL = GoogleAuthenticatorQRGenerator
|
||||
.getOtpAuthTotpURL(ruoYiConfig.getName(), sysUser.getNickName(), credentials);
|
||||
sysUser.setOtpSecret(credentials.getKey());
|
||||
sysUser.setOtpUrl(otpAuthTotpURL);
|
||||
|
||||
// 新增用户信息
|
||||
int rows = baseMapper.insert(sysUser);
|
||||
user.setUserId(sysUser.getUserId());
|
||||
@ -412,6 +426,30 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
|
||||
.eq(SysUser::getUserId, userId)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户OTP秘钥
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int resetOtpSecret(Long userId) {
|
||||
SysUser sysUser = baseMapper.selectById(userId);
|
||||
if (ObjectUtil.isNull(sysUser)) {
|
||||
throw new UserException("user.not.exists", userId);
|
||||
}
|
||||
|
||||
GoogleAuthenticatorKey credentials = googleAuthenticator.createCredentials();
|
||||
String otpAuthTotpURL = GoogleAuthenticatorQRGenerator
|
||||
.getOtpAuthTotpURL(ruoYiConfig.getName(), sysUser.getNickName(), credentials);
|
||||
|
||||
return baseMapper.update(null,
|
||||
new LambdaUpdateWrapper<SysUser>()
|
||||
.set(SysUser::getOtpSecret, credentials.getKey())
|
||||
.set(SysUser::getOtpUrl, otpAuthTotpURL)
|
||||
.eq(SysUser::getUserId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
*
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
</if>
|
||||
<if test="ew.getSqlSelect == null">
|
||||
u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.otp_secret, u.otp_url, u.create_by, u.create_time,
|
||||
u.remark
|
||||
</if>
|
||||
from sys_user u
|
||||
${ew.getCustomSqlSegment}
|
||||
@ -31,7 +32,8 @@
|
||||
</if>
|
||||
<if test="ew.getSqlSelect == null">
|
||||
u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.otp_secret, u.otp_url, u.create_by, u.create_time,
|
||||
u.remark
|
||||
</if>
|
||||
from sys_user u
|
||||
${ew.getCustomSqlSegment}
|
||||
@ -39,7 +41,7 @@
|
||||
|
||||
<select id="selectUserExportList" resultMap="SysUserExportResult">
|
||||
select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex,
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark,
|
||||
u.status, u.del_flag, u.login_ip, u.login_date, u.otp_secret, u.otp_url, u.create_by, u.create_time, u.remark,
|
||||
d.dept_name, d.leader, u1.user_name as leaderName
|
||||
from sys_user u
|
||||
left join sys_dept d on u.dept_id = d.dept_id
|
||||
|
||||
@ -152,6 +152,8 @@ create table sys_user (
|
||||
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
|
||||
login_ip varchar(128) default '' comment '最后登录IP',
|
||||
login_date datetime comment '最后登录时间',
|
||||
otp_secret varchar(100) default '' comment 'One-Time Password 秘钥',
|
||||
otp_url varchar(200) default '' comment 'One-Time Password 秘钥绑定链接',
|
||||
create_dept bigint(20) default null comment '创建部门',
|
||||
create_by bigint(20) default null comment '创建者',
|
||||
create_time datetime comment '创建时间',
|
||||
@ -164,9 +166,9 @@ create table sys_user (
|
||||
-- ----------------------------
|
||||
-- 初始化-用户信息表数据
|
||||
-- ----------------------------
|
||||
insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), null, null, '管理员');
|
||||
insert into sys_user values(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 3, sysdate(), null);
|
||||
insert into sys_user values(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), 103, 1, sysdate(), 4, sysdate(), null);
|
||||
insert into sys_user values(1, '000000', 103, 'admin', '疯狂的狮子Li', 'sys_user', 'crazyLionLi@163.com', '15888888888', '1', null, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'E3NX2Q3F2WE2ASGE', 'otpauth://totp/RuoYi-Vue-Plus:%E7%96%AF%E7%8B%82%E7%9A%84%E7%8B%AE%E5%AD%90Li?secret=E3NX2Q3F2WE2ASGE&issuer=RuoYi-Vue-Plus&algorithm=SHA1&digits=6&period=30', 103, 1, sysdate(), null, null, '管理员');
|
||||
insert into sys_user values(3, '000000', 108, 'test', '本部门及以下 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), '', '', 1, 103,sysdate(), 3, sysdate(), null);
|
||||
insert into sys_user values(4, '000000', 102, 'test1', '仅本人 密码666666', 'sys_user', '', '', '0', null, '$2a$10$b8yUzN0C71sbz.PhNOCgJe.Tu1yWC3RNrTyjSQ8p1W0.aaUXUJ.Ne', '0', '0', '127.0.0.1', sysdate(), '', '', 103, 1, sysdate(), 4, sysdate(), null);
|
||||
|
||||
-- ----------------------------
|
||||
-- 3、岗位信息表
|
||||
|
||||
Loading…
Reference in New Issue
Block a user