This commit is contained in:
zhouhefeng
2026-07-15 11:47:47 +08:00
parent 9b4f4d57be
commit 3425888c91
14 changed files with 691 additions and 589 deletions
@@ -41,15 +41,11 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@IocBean
@@ -58,6 +54,11 @@ import java.util.UUID;
@Api(tags = "登录")
public class SysLoginController {
private static final Log log = Logs.get();
private static final LoginRsaKeyRing LOGIN_RSA_KEY_RING = new LoginRsaKeyRing();
/**
* TODO: 临时屏蔽 localhost 平台登录验证码,恢复时改为 false。
*/
private static final boolean DISABLE_LOGIN_CAPTCHA = true;
@Inject
private SysUserService sysUserService;
@Inject
@@ -123,14 +124,20 @@ public class SysLoginController {
}
try {
// 验证码校验
try {
validateService.checkCode(captchaKey, captchaCode);
} catch (BaseException e) {
return Result.error(e.getMessage());
if (!isLoginCaptchaDisabled(req)) {
try {
validateService.checkCode(captchaKey, captchaCode);
} catch (BaseException e) {
return Result.error(e.getMessage());
}
}
// 解密密码
String decryptPwd = validateService.decryptPwd(keyId, password);
LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.getPrivateKey(keyId);
if (rsaKey == null) {
throw new BaseException("Login key expired, please refresh the login page");
}
String decryptPwd = RsaUtils.decrypt(password, rsaKey.privateKey);
if (decryptPwd == null) {
throw new BaseException("用户登录失败");
}
@@ -153,6 +160,17 @@ public class SysLoginController {
}
}
private boolean isLoginCaptchaDisabled(HttpServletRequest req) {
if (!DISABLE_LOGIN_CAPTCHA || req == null) {
return false;
}
String serverName = req.getServerName();
return "localhost".equalsIgnoreCase(serverName)
|| "127.0.0.1".equals(serverName)
|| "0:0:0:0:0:0:0:1".equals(serverName)
|| "::1".equals(serverName);
}
@At(value = "/platform/sso/login", top = true)
@Ok("re")
@ApiOperation("用户cas登录统一入口")
@@ -267,25 +285,89 @@ public class SysLoginController {
@ApiOperation("获取公钥")
public Object publicKey() {
try {
// 生成密钥对
KeyPair keyPair = RsaUtils.generateKeyPair();
String publicKeyStr = RsaUtils.getPublicKeyBase64(keyPair.getPublic());
String privateKeyStr = RsaUtils.getPrivateKeyBase64(keyPair.getPrivate());
// 生成 UUID 作为 keyId
String keyId = UUID.randomUUID().toString().replace("-", "");
// 私钥存入 Redis,5 分钟过期,也防止恶意刷密钥
redisService.setex(RedisConstant.RSA_KEY_PREFIX + keyId, 5 * 60, privateKeyStr);
// 返回给前端
LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.currentKey();
return Result.success(Map.of(
"publicKey", publicKeyStr,
"keyId", keyId
"publicKey", rsaKey.publicKey,
"keyId", rsaKey.keyId,
"expireAt", rsaKey.encryptExpireAt
));
} catch (Exception e) {
log.error("生成 RSA 密钥失败", e);
return Result.error("系统异常");
}
}
private static class LoginRsaKeyRing {
private static final long ACTIVE_MILLIS = 30 * 60 * 1000L;
private static final long DECRYPT_GRACE_MILLIS = 10 * 60 * 1000L;
private final ConcurrentHashMap<String, LoginRsaKey> keys = new ConcurrentHashMap<>();
private volatile LoginRsaKey current;
LoginRsaKey currentKey() throws Exception {
long now = System.currentTimeMillis();
LoginRsaKey key = current;
if (key != null && now < key.encryptExpireAt) {
return key;
}
synchronized (this) {
key = current;
now = System.currentTimeMillis();
if (key == null || now >= key.encryptExpireAt) {
key = generateKey(now);
current = key;
keys.put(key.keyId, key);
cleanup(now);
}
return key;
}
}
LoginRsaKey getPrivateKey(String keyId) {
if (StrUtil.isBlank(keyId)) {
return null;
}
long now = System.currentTimeMillis();
LoginRsaKey key = keys.get(keyId);
if (key == null) {
return null;
}
if (now >= key.decryptExpireAt) {
keys.remove(keyId);
return null;
}
return key;
}
private LoginRsaKey generateKey(long now) throws Exception {
KeyPair keyPair = RsaUtils.generateKeyPair();
String keyId = UUID.randomUUID().toString().replace("-", "");
return new LoginRsaKey(
keyId,
RsaUtils.getPublicKeyBase64(keyPair.getPublic()),
keyPair.getPrivate(),
now + ACTIVE_MILLIS,
now + ACTIVE_MILLIS + DECRYPT_GRACE_MILLIS
);
}
private void cleanup(long now) {
keys.entrySet().removeIf(entry -> now >= entry.getValue().decryptExpireAt);
}
}
private static class LoginRsaKey {
private final String keyId;
private final String publicKey;
private final PrivateKey privateKey;
private final long encryptExpireAt;
private final long decryptExpireAt;
private LoginRsaKey(String keyId, String publicKey, PrivateKey privateKey, long encryptExpireAt, long decryptExpireAt) {
this.keyId = keyId;
this.publicKey = publicKey;
this.privateKey = privateKey;
this.encryptExpireAt = encryptExpireAt;
this.decryptExpireAt = decryptExpireAt;
}
}
}
@@ -25,7 +25,6 @@ import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -69,15 +68,11 @@ public class TeacherCongressInstitutionController {
Teacher_congress_institution rootInstitution = new Teacher_congress_institution();
rootInstitution.setId("0");
rootInstitution.setParentId("");
rootInstitution.setName("两代会组织机构");
rootInstitution.setName("教代会机构");
rootInstitution.setLocation(0);
institutionList.add(rootInstitution);
List<TreeNode<String>> treeNodes = institutionList.stream().map(institution -> {
TreeNode<String> treeNode = new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation());
treeNode.setExtra(Map.of("code", Strings.sNull(institution.getCode())));
return treeNode;
}).toList();
List<TreeNode<String>> treeNodes = institutionList.stream().map(institution -> new TreeNode<>(institution.getId(), institution.getParentId(), institution.getName(), institution.getLocation())).toList();
List<Tree<String>> treeList = TreeUtil.build(treeNodes, "");
return Result.success(treeList);
}
@@ -102,16 +97,6 @@ public class TeacherCongressInstitutionController {
return Result.success(Map.of("treeFlat", children, "treeList", treeList));
}
@At
@SaCheckPermission("tc.institution")
public Result specialCommitteeRoleOptions() {
Sys_dict parent = sysDictService.fetch(Cnd.where(Sys_dict::getCode, "=", "SPECIAL_COMMITTEE_ROLES"));
if (ObjectUtil.isEmpty(parent)) {
return Result.success(List.of());
}
List<Sys_dict> children = sysDictService.query(Cnd.where(Sys_dict::getPath, "like", parent.getPath() + "%").and(Sys_dict::getId, "!=", parent.getId()).asc(Sys_dict::getLocation));
return Result.success(children);
}
/**
* 机构分页查询
@@ -128,7 +113,6 @@ public class TeacherCongressInstitutionController {
cnd.and("parentId", "=", parentId);
cnd.and("sessionId", "=", sessionId);
cnd.asc("location");
cnd.asc("code");
Pagination pagination = baseService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), "teacher_congress_institution", cnd);
return Result.success(pagination);
}
@@ -145,7 +129,7 @@ public class TeacherCongressInstitutionController {
if (dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getCode, "=", institution.getCode()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) > 0) {
return Result.error("机构已存在");
}
if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0") && dao.count(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId())) == 0) {
if (StrUtil.isNotBlank(institution.getParentId()) && !institution.getParentId().equals("0")) {
Sys_dict sysDict = sysDictService.fetch(Cnd.where(Sys_dict::getId, "=", institution.getParentId()));
Teacher_congress_institution parentInstitution = dao.fetch(Teacher_congress_institution.class,
Cnd.where(Teacher_congress_institution::getCode, "=", sysDict.getCode())
@@ -167,34 +151,10 @@ public class TeacherCongressInstitutionController {
* @param id
* @return
*/
@At
@SaCheckPermission("tc.institution")
public Result update(Teacher_congress_institution institution) {
Teacher_congress_institution dbInstitution = dao.fetch(Teacher_congress_institution.class, institution.getId());
if (ObjectUtil.isEmpty(dbInstitution)) {
return Result.error("机构不存在");
}
dbInstitution.setName(institution.getName());
dbInstitution.setIntroduce(institution.getIntroduce());
dbInstitution.setLocation(institution.getLocation());
dao.update(dbInstitution);
return Result.success();
}
@At
@SaCheckPermission("tc.institution")
@Aop(TransAop.READ_COMMITTED)
public Result delete(@Valid String id) {
Teacher_congress_institution institution = dao.fetch(Teacher_congress_institution.class, id);
if (ObjectUtil.isEmpty(institution)) {
return Result.error("机构不存在");
}
List<Teacher_congress_institution> siblingList = dao.query(Teacher_congress_institution.class, Cnd.where(Teacher_congress_institution::getParentId, "=", institution.getParentId()).and(Teacher_congress_institution::getSessionId, "=", institution.getSessionId()).asc(Teacher_congress_institution::getLocation).asc(Teacher_congress_institution::getCode));
for (int i = 0; i < siblingList.size() && i < 6; i++) {
if (id.equals(siblingList.get(i).getId())) {
return Result.error("你选择的组织机构是两代会基本机构,不允许删除。");
}
}
dao.delete(Teacher_congress_institution.class, id);
dao.clear(Teacher_congress_institution_user.class, Cnd.where("institutionId", "=", id));
return Result.success();
@@ -254,8 +214,8 @@ public class TeacherCongressInstitutionController {
@SaCheckPermission("tc.institution")
@SLog(tag = "教代会-机构设置", msg = "添加人员")
@Aop(TransAop.READ_COMMITTED)
public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity, String roleCode) {
teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity, roleCode);
public Result userInsert(@Valid String userId, @Valid String institutionId, @Valid String sessionId, @Valid String identity) {
teacherCongressInstitutionUserService.userInsert(userId, institutionId, sessionId, identity);
return Result.success();
}
@@ -14,7 +14,7 @@ public interface TeacherCongressInstitutionUserService extends BaseService<Teach
* @param sessionId 届次id
* @param identity 身份 主任|副主任|成员
*/
void userInsert(String userId, String institutionId, String sessionId, String identity, String roleCode);
void userInsert(String userId, String institutionId, String sessionId, String identity);
/**
* 删除机构成员
@@ -1,6 +1,5 @@
package com.budwk.app.zhgh.democratic.teachercongress.institution.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
@@ -31,7 +30,7 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
@Override
@Aop(TransAop.READ_COMMITTED)
public void userInsert(String userId, String institutionId, String sessionId, String identity, String roleCode) {
public void userInsert(String userId, String institutionId, String sessionId, String identity) {
int count = dao().count(Teacher_congress_institution_user.class,
Cnd.where(Teacher_congress_institution_user::getInstitutionId, "=", institutionId)
.and(Teacher_congress_institution_user::getUserId, "=", userId)
@@ -50,28 +49,6 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
dao().insert(institutionUser);
Teacher_congress_institution institution = dao().fetch(Teacher_congress_institution.class, institutionId);
if ("ZWH001".equals(institution.getCode()) || "ZXWYH".equals(institution.getCode()) || "DBZGSCXZ".equals(institution.getCode())) {
if (StrUtil.isBlank(roleCode)) {
throw new BaseException("请选择角色");
}
Sys_role sysRole = sysRoleService.fetch(Cnd.where(Sys_role::getCode, "=", roleCode));
if (sysRole == null) {
throw new BaseException("无法找到" + roleCode + "对应编码的角色");
}
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)
.and(Sys_user_role::getTcSessionId, "=", sessionId)
.and(Sys_user_role::getRoleId, "=", sysRole.getId())
);
if (existsRole == 0) {
Sys_user_role insertSysUserRole = new Sys_user_role();
insertSysUserRole.setRoleId(sysRole.getId());
insertSysUserRole.setUserId(userId);
insertSysUserRole.setTcSessionId(sessionId);
dao().insert(insertSysUserRole);
sysRoleService.clearCache();
sysUserService.clearCache();
}
}
if (institution.getCode().contains("TEACHER_CONGRESS_INSTITUTION_PROPOSAL_COMMITTEE") && identity.equals("主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)