feat:登录ukey改造

new-rd20250506
wangxy 2 months ago
parent 9028d9dc54
commit 99b1fefcd5

@ -3,7 +3,9 @@ package com.ruoyi.web.controller.system.system;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.security.RsaUtils;
import com.ruoyi.system.service.ISysUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
@ -22,6 +24,8 @@ import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.ConfigService;
import java.util.Objects;
/**
*
*
@ -39,6 +43,11 @@ public class SysLoginController extends BaseController
@Autowired
private ConfigService configService;
@Autowired
private ISysUserService userService;
@GetMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response, ModelMap mmap)
{
@ -76,6 +85,37 @@ public class SysLoginController extends BaseController
}
}
@PostMapping("/login/ukey")
@ResponseBody
public AjaxResult ukeyLogin(String ukeyId) {
try {
// 1. 参数校验
if (StringUtils.isEmpty(ukeyId)) {
return AjaxResult.error("UKey标识不能为空");
}
// 2. 查询用户
SysUser user = userService.lambdaQuery()
.eq(SysUser::getUkeyId, ukeyId)
.eq(SysUser::getStatus, "0")
.eq(SysUser::getDelFlag, "0")
.one();
if (Objects.isNull(user)) {
return AjaxResult.error("未找到UKey绑定的用户");
}
// 3. 复用原有登录逻辑
UsernamePasswordToken token = new UsernamePasswordToken(
user.getUserName(), "", true);
SecurityUtils.getSubject().login(token);
return success();
} catch (Exception e) {
String msg = "未识别到UKey";
if (StringUtils.isNotEmpty(e.getMessage())) {
msg = e.getMessage();
}
return error(msg);
}
}
@GetMapping("/unauth")
public String unauth()
{

@ -0,0 +1,105 @@
function customFireEvent(objId, eventName) {
var obj = document.getElementById(objId);
if (obj == undefined) {
return;
}
if (obj.fireEvent) {
obj.fireEvent("on" + eventName);
} else if (obj.dispatchEvent) {
var e = document.createEvent('HTMLEvents');
e.initEvent(eventName, false, false);
obj.dispatchEvent(e);
}
}
function FillDeviceList() {
GetAllDeviceSN(function (retObj) {
var obj = document.getElementById("id_device_list");
if (obj == undefined) {
return;
}
var i, n = obj.length;
for (i = 0; i < n; i++) {
obj.remove(0);
}
var strDeviceList = retObj.retVal;
while (true) {
var pos = strDeviceList.indexOf(";");
if (pos <= 0) {
break;
}
var strOneDevice = strDeviceList.substring(0, pos);
var objItem = new Option(strOneDevice, strOneDevice);
obj.options.add(objItem);
var len = strDeviceList.length;
strDeviceList = strDeviceList.substring(pos + 1, len);
}
customFireEvent('id_cert_list', 'change');
customFireEvent('id_device_list', 'change');
});
}
function select_cert(value) {
$("#id_selected_cert_id").val(value);
}
function select_device(value) {
$("#id_selected_device_sn").val(value);
}
SetUserCertList("id_cert_list");
FillDeviceList();
SetOnUsbKeyChangeCallBack(FillDeviceList);
$(function() {
// 检测UKey状态
function checkUKey() {
try {
var ukey_id = $("#id_selected_cert_id").val();
if (!ukey_id) {
$('#ukeyStatus').show().find('span').text('UKey插件未加载');
return false;
}
if (ukey_id) {
$('#ukeyStatus').show().removeClass('alert-info').addClass('alert-success')
.find('span').html('<b>检测到UKey已插入</b>');
return true;
} else {
$('#ukeyStatus').show().removeClass('alert-success').addClass('alert-info')
.find('span').text('请插入您的UKey设备');
return false;
}
} catch (e) {
$('#ukeyStatus').show().removeClass('alert-success').addClass('alert-danger')
.find('span').html('UKey驱动未安装<a href="/driver/BJCAClientV3.7.418.0052.exe" target="_blank">点击下载</a>');
return false;
}
}
// 每3秒检测一次
setInterval(checkUKey, 3000);
checkUKey();
// UKey登录处理
$('#ukeyLoginBtn').click(function() {
var ukeyId = $("#id_selected_cert_id").val();
if(!ukeyId){
$.modal.alertError("请插入UKey设备");
return false;
}
if (!checkUKey()) return;
$.modal.loading("系统正在登录,请稍后!");
try {
$.ajax({
type: "post",
url: ctx + "login/ukey",
data: { ukeyId: ukeyId },
success: function(r) {
if (r.code == web_status.SUCCESS) {
location.href = ctx + 'index';
} else {
$.modal.msg(r.msg);
}
$.modal.closeLoading();
}
});
} catch (e) {
$.modal.alertError('UKey操作失败: ' + e.message);
}
});
});

File diff suppressed because it is too large Load Diff

@ -65,9 +65,48 @@
</div>
<button class="btn btn-success btn-block" id="btnSubmit" data-loading="正在验证登录,请稍后...">登录</button>
</form>
<!-- 在原有登录表单下方添加 -->
<div class="ukey-login-area" style="margin-top: 20px;">
<div id="ukeyStatus" class="alert alert-info" style="display: none;">
<i class="fa fa-key"></i> <span id="ukeyStatusText"></span>
</div>
<button type="button" id="ukeyLoginBtn" class="btn btn-block btn-primary">
<i class="fa fa-usb"></i> UKey安全登录
</button>
<div style="display: none;">
<tr>
<td class="right"> 请选择证书:</td>
<td class="left">
<select class="StandardWidth" id="id_cert_list" method="post" onchange="select_cert(this.value)"
enctype="multipart/form-data" action="submit">
</select>
</td>
</tr>
<tr>
<td class="right"> 选择设备序列号:</td>
<td class="left">
<select class="StandardWidth" id="id_device_list" onchange="select_device(this.value)">
</select>
</td>
</tr>
<tr>
<td class="right"> 设备序列号:</td>
<td class="left">
<input type="text" class="StandardWidth" id="id_selected_device_sn">
</td>
</tr>
<tr>
<td class="right"> 选择的证书ID:</td>
<td class="left">
<input type="text" class="StandardWidth layui-input" method="post" id="id_selected_cert_id"
name="certid" enctype="multipart/form-data">
</td>
</tr>
</div>
</div>
<div class="signup-footer">
<div class="pull-right">
<!-- Copyright © 2018-2024 ZHKY All Rights Reserved. <br>-->
<!-- Copyright © 2018-2024 ZHKY All Rights Reserved. <br>-->
</div>
</div>
</div>
@ -88,6 +127,9 @@
<script src="../static/ajax/libs/blockUI/jquery.blockUI.js" th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script>
<script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.7}"></script>
<script src="../static/ruoyi/login.js" th:src="@{/ruoyi/login.js}"></script>
<script src="../static/ruoyi/xtxasyn.js" th:src="@{/ruoyi/xtxasyn.js}"></script>
<script src="../static/js/jsencrypt.min.js" th:src="@{/js/jsencrypt.min.js}"></script>
<!-- ukey登录 -->
<script src="../static/ruoyi/ukeylogin.js" th:src="@{/ruoyi/ukeylogin.js}"></script>
</body>
</html>

@ -241,6 +241,9 @@ public class SysUser extends BaseEntity
@Excel(name = "失效时间", width = 30, dateFormat = "yyyy-MM-dd")
private String enddate;
/** UKey唯一标识 */
private String ukeyId;
public String getHavePassport() {
return havePassport;
}

@ -296,10 +296,13 @@ public class ShiroConfig
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
filterChainDefinitionMap.put("/captcha/**", "anon");
filterChainDefinitionMap.put("/driver/**", "anon");
// 退出 logout地址shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login", "anon,captchaValidate");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login/ukey", "anon,captchaValidate");
// 注册相关
filterChainDefinitionMap.put("/register", "anon,captchaValidate");

@ -97,7 +97,7 @@ public class UserRealm extends AuthorizingRealm
SysUser user = null;
try
{
user = loginService.login(username, password);
user = loginService.login(username, password,upToken.isRememberMe());
}
catch (CaptchaException e)
{

@ -31,8 +31,7 @@ import com.ruoyi.system.service.ISysUserService;
* @author ruoyi
*/
@Component
public class SysLoginService
{
public class SysLoginService {
@Autowired
private SysPasswordService passwordService;
@ -48,40 +47,39 @@ public class SysLoginService
/**
*
*/
public SysUser login(String username, String password)
{
public SysUser login(String username, String password, boolean rememberMe) {
/**
*
*/
if (!rememberMe) {
// 验证码校验
if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
{
if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
{
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
throw new UserNotExistsException();
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// IP黑名单校验
String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp()))
{
if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp())) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
throw new BlackListException();
}
@ -101,14 +99,12 @@ public class SysLoginService
}
*/
if (user == null)
{
if (user == null) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
throw new UserNotExistsException();
}
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));
throw new UserDeleteException();
}
@ -116,20 +112,21 @@ public class SysLoginService
/**
*
*/
if (!ExamineState.ENABLE.getCode().equals(user.getExamine()))
{
if (!ExamineState.ENABLE.getCode().equals(user.getExamine())) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.examine.disabled")));
throw new UserDisableException();
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));
throw new UserBlockedException();
}
/**
*
*/
if (!rememberMe) {
passwordService.validate(user, password);
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
setRolePermission(user);
recordLoginInfo(user.getUserId());
@ -161,14 +158,11 @@ public class SysLoginService
*
* @param user
*/
public void setRolePermission(SysUser user)
{
public void setRolePermission(SysUser user) {
List<SysRole> roles = user.getRoles();
if (!roles.isEmpty() && roles.size() > 1)
{
if (!roles.isEmpty() && roles.size() > 1) {
// 多角色设置permissions属性以便数据权限匹配权限
for (SysRole role : roles)
{
for (SysRole role : roles) {
Set<String> rolePerms = menuService.selectPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
}
@ -180,8 +174,7 @@ public class SysLoginService
*
* @param userId ID
*/
public void recordLoginInfo(Long userId)
{
public void recordLoginInfo(Long userId) {
SysUser user = new SysUser();
user.setUserId(userId);
user.setLoginIp(ShiroUtils.getIp());

@ -39,7 +39,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="examine" column="examine" />
<result property="examinedate" column="examinedate" />
<result property="examineuser" column="examineuser" />
<result property="ukeyId" column="ukey_id" />
<result property="havePassport" column="have_passport" />
<result property="confAgreement" column="conf_agreement" />
<result property="entryexitFiling" column="entryexit_filing" />

Loading…
Cancel
Save