Merge remote-tracking branch 'origin/V1.00' into V1.00
This commit is contained in:
commit
8e6933f4be
98
src/main/java/com/cisd/tms/common/util/IpWhitelistUtil.java
Normal file
98
src/main/java/com/cisd/tms/common/util/IpWhitelistUtil.java
Normal file
@ -0,0 +1,98 @@
|
||||
package com.cisd.tms.common.util;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class IpWhitelistUtil {
|
||||
|
||||
private IpWhitelistUtil() {
|
||||
}
|
||||
|
||||
public static String getClientIp(HttpServletRequest request) {
|
||||
String ipAddress = request.getHeader("X-Forwarded-For");
|
||||
if (isBlankOrUnknown(ipAddress)) {
|
||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (isBlankOrUnknown(ipAddress)) {
|
||||
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (isBlankOrUnknown(ipAddress)) {
|
||||
ipAddress = request.getRemoteAddr();
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public static List<String> readWhitelist(String path) {
|
||||
List<String> whitelist = new ArrayList<>();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (!line.isBlank()) {
|
||||
whitelist.add(line.trim());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore when whitelist file missing
|
||||
}
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
public static boolean isIpInWhitelist(String ipAddress, List<String> whitelist) {
|
||||
for (String entry : whitelist) {
|
||||
if (entry.contains("/")) {
|
||||
if (isIpInCidr(ipAddress, entry)) {
|
||||
return true;
|
||||
}
|
||||
String cidrAddress = entry.split("/")[0];
|
||||
if (cidrAddress.equals(ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
} else if (entry.equals(ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isIpInCidr(String ipAddress, String cidr) {
|
||||
try {
|
||||
String[] parts = cidr.split("/");
|
||||
if (parts.length != 2) {
|
||||
return false;
|
||||
}
|
||||
InetAddress inetAddr = InetAddress.getByName(parts[0]);
|
||||
int prefixLength = Integer.parseInt(parts[1]);
|
||||
byte[] network = inetAddr.getAddress();
|
||||
byte[] ip = InetAddress.getByName(ipAddress).getAddress();
|
||||
if (network.length != ip.length) {
|
||||
return false;
|
||||
}
|
||||
int fullBytes = prefixLength / 8;
|
||||
int remainingBits = prefixLength % 8;
|
||||
for (int i = 0; i < fullBytes; i++) {
|
||||
if (network[i] != ip[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (remainingBits > 0) {
|
||||
int mask = (-1) << (8 - remainingBits);
|
||||
if ((network[fullBytes] & mask) != (ip[fullBytes] & mask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBlankOrUnknown(String value) {
|
||||
return value == null || value.isEmpty() || "unknown".equalsIgnoreCase(value);
|
||||
}
|
||||
}
|
||||
72
src/main/java/com/cisd/tms/common/util/Sm2SignatureUtil.java
Normal file
72
src/main/java/com/cisd/tms/common/util/Sm2SignatureUtil.java
Normal file
@ -0,0 +1,72 @@
|
||||
package com.cisd.tms.common.util;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
public final class Sm2SignatureUtil {
|
||||
|
||||
private static final String PROVIDER = "BC";
|
||||
private static final String SIGNATURE_ALGO = "SM3withSM2";
|
||||
|
||||
static {
|
||||
if (Security.getProvider(PROVIDER) == null) {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
}
|
||||
|
||||
private Sm2SignatureUtil() {
|
||||
}
|
||||
|
||||
public static boolean verifyBase64Signature(String publicKey, String data, String signatureBase64) {
|
||||
if (publicKey == null || publicKey.isBlank()) {
|
||||
throw new IllegalArgumentException("public key is required");
|
||||
}
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("data is required");
|
||||
}
|
||||
if (signatureBase64 == null || signatureBase64.isBlank()) {
|
||||
throw new IllegalArgumentException("signature is required");
|
||||
}
|
||||
try {
|
||||
PublicKey key = parsePublicKey(publicKey);
|
||||
Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
|
||||
signature.initVerify(key);
|
||||
signature.update(data.getBytes(StandardCharsets.UTF_8));
|
||||
byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64.trim());
|
||||
return signature.verify(signatureBytes);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("sm2 verify failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static PublicKey parsePublicKey(String publicKey) throws Exception {
|
||||
String normalized = stripPem(publicKey);
|
||||
byte[] keyBytes = Base64.getDecoder().decode(normalized.getBytes(StandardCharsets.UTF_8));
|
||||
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("EC", PROVIDER);
|
||||
return keyFactory.generatePublic(spec);
|
||||
}
|
||||
|
||||
private static String stripPem(String key) {
|
||||
String trimmed = key.trim();
|
||||
if (!trimmed.contains("BEGIN")) {
|
||||
return trimmed;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String[] lines = trimmed.split("\\R");
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("-----")) {
|
||||
continue;
|
||||
}
|
||||
builder.append(line.trim());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.cisd.tms.modules.mk.config;
|
||||
|
||||
import com.cisd.tms.modules.mk.dto.UserInfo;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ConfigurationProperties(prefix = "tms.ukey")
|
||||
public class UKeyLoginProperties {
|
||||
|
||||
/**
|
||||
* 认证信息白名单。
|
||||
*/
|
||||
private List<UserInfo> authenticationInfoList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 角色密码映射。
|
||||
*/
|
||||
private Map<String, String> rolePasswords = new HashMap<>();
|
||||
|
||||
/**
|
||||
* IP 白名单文件路径。
|
||||
*/
|
||||
private String whitelistPath;
|
||||
|
||||
public List<UserInfo> getAuthenticationInfoList() {
|
||||
return authenticationInfoList;
|
||||
}
|
||||
|
||||
public void setAuthenticationInfoList(List<UserInfo> authenticationInfoList) {
|
||||
this.authenticationInfoList = authenticationInfoList;
|
||||
}
|
||||
|
||||
public Map<String, String> getRolePasswords() {
|
||||
return rolePasswords;
|
||||
}
|
||||
|
||||
public void setRolePasswords(Map<String, String> rolePasswords) {
|
||||
this.rolePasswords = rolePasswords;
|
||||
}
|
||||
|
||||
public String getWhitelistPath() {
|
||||
return whitelistPath;
|
||||
}
|
||||
|
||||
public void setWhitelistPath(String whitelistPath) {
|
||||
this.whitelistPath = whitelistPath;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.cisd.tms.modules.mk.controller;
|
||||
|
||||
import com.cisd.tms.common.api.ApiResponse;
|
||||
import com.cisd.tms.modules.mk.dto.AuthInfo;
|
||||
import com.cisd.tms.modules.mk.dto.LoginDTO;
|
||||
import com.cisd.tms.modules.mk.service.UKeyLoginService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* UKey 登录接口迁移自 manager.service。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1")
|
||||
@Tag(name = "UKey 登录", description = "UKey 登录校验与认证信息生成")
|
||||
public class LoginController {
|
||||
|
||||
@Resource
|
||||
private UKeyLoginService uKeyLoginService;
|
||||
|
||||
@PostMapping("/UKeyLogin2")
|
||||
@Operation(summary = "UKey 登录", description = "校验 UKey 签名、随机数与角色密码,并返回认证信息。")
|
||||
public ApiResponse<AuthInfo> uKeyLogin(@RequestBody LoginDTO loginDTO, HttpServletRequest request) {
|
||||
return ApiResponse.success(uKeyLoginService.uKeyLogin(loginDTO, request));
|
||||
}
|
||||
}
|
||||
24
src/main/java/com/cisd/tms/modules/mk/dto/AuthInfo.java
Normal file
24
src/main/java/com/cisd/tms/modules/mk/dto/AuthInfo.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
@Data
|
||||
public class AuthInfo extends SecretKey {
|
||||
private String token;
|
||||
|
||||
public static AuthInfo getInstance(String token) {
|
||||
AuthInfo authInfo = new AuthInfo();
|
||||
authInfo.token = token;
|
||||
authInfo.setKey(generateKey());
|
||||
authInfo.setIv(generateKey());
|
||||
return authInfo;
|
||||
}
|
||||
|
||||
private static byte[] generateKey() {
|
||||
byte[] key = new byte[16];
|
||||
new SecureRandom().nextBytes(key);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
142
src/main/java/com/cisd/tms/modules/mk/dto/LoginDTO.java
Normal file
142
src/main/java/com/cisd/tms/modules/mk/dto/LoginDTO.java
Normal file
@ -0,0 +1,142 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 登录时传输的实体
|
||||
*/
|
||||
@Data
|
||||
public class LoginDTO {
|
||||
/**
|
||||
* 选择的角色
|
||||
*/
|
||||
private String roleSelected;
|
||||
/**
|
||||
* 登录的角色
|
||||
*/
|
||||
private String role;
|
||||
/**
|
||||
* 认证信息
|
||||
*/
|
||||
private List<LoginAuthInfo> authInfo;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
public Set<String> getRids() {
|
||||
Set<String> roles = new HashSet<>();
|
||||
if (authInfo == null) {
|
||||
return roles;
|
||||
}
|
||||
for (LoginAuthInfo auth : authInfo) {
|
||||
roles.add(auth.rid);
|
||||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
public List<String> getRandom() {
|
||||
List<String> randomList = new ArrayList<>();
|
||||
if (authInfo == null) {
|
||||
return randomList;
|
||||
}
|
||||
for (LoginAuthInfo loginAuthInfo : authInfo) {
|
||||
randomList.add(loginAuthInfo.getRb());
|
||||
}
|
||||
return randomList;
|
||||
}
|
||||
|
||||
public UserInfo getUserInfo() {
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setRole(this.role);
|
||||
List<UserInfo.User> userList = new ArrayList<>();
|
||||
if (authInfo != null) {
|
||||
for (LoginAuthInfo loginAuthInfo : authInfo) {
|
||||
UserInfo.User user = new UserInfo.User();
|
||||
user.setUid(loginAuthInfo.getUid());
|
||||
user.setRid(loginAuthInfo.getRid());
|
||||
userList.add(user);
|
||||
}
|
||||
}
|
||||
userInfo.setUser(userList);
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public List<UKeySignVerifyDTO> getUKeySignVerifyDTOList() {
|
||||
List<UKeySignVerifyDTO> uKeySignVerifyDTOList = new ArrayList<>();
|
||||
if (authInfo == null) {
|
||||
return uKeySignVerifyDTOList;
|
||||
}
|
||||
for (LoginAuthInfo loginAuthInfo : authInfo) {
|
||||
UKeySignVerifyDTO uKeySignVerifyDTO = new UKeySignVerifyDTO();
|
||||
UKeySignDTO uKeySignDTO = new UKeySignDTO();
|
||||
uKeySignDTO.setPubKey(loginAuthInfo.pubKey);
|
||||
uKeySignDTO.setRole(this.role);
|
||||
uKeySignDTO.setUid(loginAuthInfo.uid);
|
||||
uKeySignDTO.setRid(loginAuthInfo.rid);
|
||||
|
||||
uKeySignVerifyDTO.setuKeySignDTO(uKeySignDTO);
|
||||
uKeySignVerifyDTO.setSign(loginAuthInfo.issueSign);
|
||||
|
||||
uKeySignVerifyDTOList.add(uKeySignVerifyDTO);
|
||||
}
|
||||
return uKeySignVerifyDTOList;
|
||||
}
|
||||
|
||||
public List<LoginSignDTO> getLoginSignDTOList() {
|
||||
List<LoginSignDTO> loginSignDTOList = new ArrayList<>();
|
||||
if (authInfo == null) {
|
||||
return loginSignDTOList;
|
||||
}
|
||||
for (LoginAuthInfo loginAuthInfo : authInfo) {
|
||||
LoginSignDTO loginSignDTO = new LoginSignDTO();
|
||||
loginSignDTO.setPubKey(loginAuthInfo.pubKey);
|
||||
loginSignDTO.setSignValue(loginAuthInfo.loginSign);
|
||||
loginSignDTO.setLoginSignData(loginAuthInfo.loginSignData);
|
||||
|
||||
loginSignDTOList.add(loginSignDTO);
|
||||
}
|
||||
return loginSignDTOList;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LoginAuthInfo {
|
||||
/**
|
||||
* U盾公钥
|
||||
*/
|
||||
private String pubKey;
|
||||
/**
|
||||
* U盾 uid
|
||||
*/
|
||||
private String uid;
|
||||
/**
|
||||
* U盾 rid
|
||||
*/
|
||||
private String rid;
|
||||
/**
|
||||
* 前端生成随机数
|
||||
*/
|
||||
private String ra;
|
||||
/**
|
||||
* 后端生成随机数
|
||||
*/
|
||||
private String rb;
|
||||
/**
|
||||
* 发行签名
|
||||
*/
|
||||
private String issueSign;
|
||||
/**
|
||||
* 登录签名的原始值
|
||||
*/
|
||||
private String loginSignData;
|
||||
/**
|
||||
* 登录签名
|
||||
*/
|
||||
private String loginSign;
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/cisd/tms/modules/mk/dto/LoginSignDTO.java
Normal file
40
src/main/java/com/cisd/tms/modules/mk/dto/LoginSignDTO.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
public class LoginSignDTO {
|
||||
/**
|
||||
* U盾公钥
|
||||
*/
|
||||
private String pubKey;
|
||||
/**
|
||||
* 签名原始数据
|
||||
*/
|
||||
private String loginSignData;
|
||||
/**
|
||||
* 签名值
|
||||
*/
|
||||
private String signValue;
|
||||
|
||||
public String getPubKey() {
|
||||
return pubKey;
|
||||
}
|
||||
|
||||
public void setPubKey(String pubKey) {
|
||||
this.pubKey = pubKey;
|
||||
}
|
||||
|
||||
public String getLoginSignData() {
|
||||
return loginSignData;
|
||||
}
|
||||
|
||||
public void setLoginSignData(String loginSignData) {
|
||||
this.loginSignData = loginSignData;
|
||||
}
|
||||
|
||||
public String getSignValue() {
|
||||
return signValue;
|
||||
}
|
||||
|
||||
public void setSignValue(String signValue) {
|
||||
this.signValue = signValue;
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/cisd/tms/modules/mk/dto/SecretKey.java
Normal file
18
src/main/java/com/cisd/tms/modules/mk/dto/SecretKey.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 加密密钥及偏移量
|
||||
*/
|
||||
@Data
|
||||
public class SecretKey {
|
||||
/**
|
||||
* 密钥
|
||||
*/
|
||||
private byte[] key;
|
||||
/**
|
||||
* 偏移量
|
||||
*/
|
||||
private byte[] iv;
|
||||
}
|
||||
70
src/main/java/com/cisd/tms/modules/mk/dto/UserInfo.java
Normal file
70
src/main/java/com/cisd/tms/modules/mk/dto/UserInfo.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.cisd.tms.modules.mk.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色信息
|
||||
*/
|
||||
@Data
|
||||
public class UserInfo {
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
private String role;
|
||||
|
||||
private Integer defpassflag;
|
||||
|
||||
private List<User> user;
|
||||
|
||||
private List<String> permissions;
|
||||
|
||||
@Data
|
||||
public static class User {
|
||||
/**
|
||||
* uid
|
||||
*/
|
||||
private String uid;
|
||||
/**
|
||||
* rid
|
||||
*/
|
||||
private String rid;
|
||||
|
||||
public boolean isSameUser(User user) {
|
||||
return this.uid.equals(user.uid) && this.rid.equals(user.rid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证信息校验,校验配置文件中的认证信息和传入的认证信息是否相同
|
||||
*/
|
||||
public boolean roleUserAuth(List<UserInfo> authenticationInfoList) {
|
||||
List<User> userConfig = new ArrayList<>(getUserFromAuthList(authenticationInfoList));
|
||||
for (User current : this.user) {
|
||||
Iterator<User> iterable = userConfig.iterator();
|
||||
while (iterable.hasNext()) {
|
||||
if (iterable.next().isSameUser(current)) {
|
||||
iterable.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return userConfig.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置信息当中相应角色应包含的认证信息
|
||||
*/
|
||||
public List<User> getUserFromAuthList(List<UserInfo> authenticationInfoList) {
|
||||
for (UserInfo authenticationInfo : authenticationInfoList) {
|
||||
if (authenticationInfo.getRole().equals(this.role)) {
|
||||
this.permissions = authenticationInfo.getPermissions();
|
||||
return authenticationInfo.getUser();
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("role info not found: " + role);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,195 @@
|
||||
package com.cisd.tms.modules.mk.service;
|
||||
|
||||
import com.cisd.tms.common.util.IpWhitelistUtil;
|
||||
import com.cisd.tms.common.util.Sm2SignatureUtil;
|
||||
import com.cisd.tms.modules.mk.config.UKeyLoginProperties;
|
||||
import com.cisd.tms.modules.mk.dto.AuthInfo;
|
||||
import com.cisd.tms.modules.mk.dto.LoginDTO;
|
||||
import com.cisd.tms.modules.mk.dto.LoginSignDTO;
|
||||
import com.cisd.tms.modules.mk.dto.UKeySignEntity;
|
||||
import com.cisd.tms.modules.mk.dto.UKeySignVerifyDTO;
|
||||
import com.cisd.tms.modules.mk.dto.UserInfo;
|
||||
import com.cisd.tms.modules.mk.enums.MasterKeyStatus;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class UKeyLoginService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UKeyLoginService.class);
|
||||
|
||||
private final LmkService lmkService;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final UKeyLoginProperties loginProperties;
|
||||
|
||||
private volatile List<String> randomList;
|
||||
|
||||
public UKeyLoginService(LmkService lmkService, ObjectMapper objectMapper, UKeyLoginProperties loginProperties) {
|
||||
this.lmkService = lmkService;
|
||||
this.objectMapper = objectMapper;
|
||||
this.loginProperties = loginProperties;
|
||||
}
|
||||
|
||||
public AuthInfo uKeyLogin(LoginDTO loginDTO, HttpServletRequest request) {
|
||||
try {
|
||||
log.info("UKey login payload: {}", objectMapper.writeValueAsString(loginDTO));
|
||||
|
||||
currentRoleCheck(loginDTO.getRole(), loginDTO.getRids());
|
||||
uKeySignCheck(loginDTO.getUKeySignVerifyDTOList());
|
||||
|
||||
UserInfo userInfo = loginDTO.getUserInfo();
|
||||
userInfoCheck(userInfo);
|
||||
throwIfLmkNotGenerated();
|
||||
|
||||
randomCheck(loginDTO.getRandom());
|
||||
loginSignCheck(loginDTO.getLoginSignDTOList());
|
||||
whitelistCheck(request);
|
||||
checkRolePassword(loginDTO.getRole(), loginDTO.getPassword());
|
||||
|
||||
AuthInfo authInfo = AuthInfo.getInstance(UUID.randomUUID().toString());
|
||||
log.info("UKey login success, role={}", loginDTO.getRole());
|
||||
return authInfo;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException("serialize login payload failed", e);
|
||||
} catch (RuntimeException e) {
|
||||
log.error("UKey login failed: {}", e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void currentRoleCheck(String role, java.util.Set<String> rids) {
|
||||
boolean matched;
|
||||
switch (role) {
|
||||
case "superadmin" -> matched = rids.size() == 3
|
||||
&& rids.contains("1")
|
||||
&& rids.contains("2")
|
||||
&& rids.contains("3");
|
||||
case "keyadmin" -> matched = rids.size() == 2
|
||||
&& rids.contains("4")
|
||||
&& rids.contains("5");
|
||||
case "auditadmin" -> matched = rids.size() == 1
|
||||
&& rids.contains("6");
|
||||
case "configadmin" -> matched = rids.size() == 1
|
||||
&& rids.contains("7");
|
||||
case "systemadmin" -> matched = rids.size() == 1
|
||||
&& rids.contains("8");
|
||||
default -> matched = false;
|
||||
}
|
||||
if (!matched) {
|
||||
throw new IllegalArgumentException("selected role not matched with UKey rids");
|
||||
}
|
||||
}
|
||||
|
||||
private void uKeySignCheck(List<UKeySignVerifyDTO> uKeySignVerifyDTOList) {
|
||||
String iPubKey = lmkService.exportIkPublicKeyHex();
|
||||
boolean result = true;
|
||||
for (UKeySignVerifyDTO uKeySignVerifyDTO : uKeySignVerifyDTOList) {
|
||||
String payload;
|
||||
try {
|
||||
payload = objectMapper.writeValueAsString(UKeySignEntity.getInstance(
|
||||
uKeySignVerifyDTO.getuKeySignDTO(), iPubKey));
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException("serialize ukey sign payload failed", e);
|
||||
}
|
||||
boolean verified = Sm2SignatureUtil.verifyBase64Signature(
|
||||
uKeySignVerifyDTO.getuKeySignDTO().getPubKey(),
|
||||
payload,
|
||||
uKeySignVerifyDTO.getSign());
|
||||
result = result && verified;
|
||||
}
|
||||
if (!result) {
|
||||
throw new IllegalArgumentException("ukey issue sign verify failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void userInfoCheck(UserInfo userInfo) {
|
||||
List<UserInfo> config = loginProperties.getAuthenticationInfoList();
|
||||
if (config == null || config.isEmpty()) {
|
||||
throw new IllegalStateException("ukey authentication config missing");
|
||||
}
|
||||
boolean result = userInfo.roleUserAuth(config);
|
||||
if (!result) {
|
||||
throw new IllegalArgumentException("auth info incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
private void throwIfLmkNotGenerated() {
|
||||
int masterKeyCode = lmkService.getMasterKeyStatus().getCode();
|
||||
if (MasterKeyStatus.ABNORMAL.getCode() == masterKeyCode) {
|
||||
throw new IllegalStateException("master key not generated");
|
||||
}
|
||||
}
|
||||
|
||||
private void randomCheck(List<String> incomingRandoms) {
|
||||
if (incomingRandoms == null || incomingRandoms.isEmpty()) {
|
||||
throw new IllegalArgumentException("random number input error");
|
||||
}
|
||||
List<String> currentRandomList = this.randomList;
|
||||
if (currentRandomList == null || currentRandomList.isEmpty()) {
|
||||
throw new IllegalStateException("random list not initialized");
|
||||
}
|
||||
for (String r : incomingRandoms) {
|
||||
if (!currentRandomList.contains(r)) {
|
||||
throw new IllegalArgumentException("random number input error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loginSignCheck(List<LoginSignDTO> loginSignDTOList) {
|
||||
boolean result = true;
|
||||
for (LoginSignDTO loginSignDTO : loginSignDTOList) {
|
||||
boolean verified = Sm2SignatureUtil.verifyBase64Signature(
|
||||
loginSignDTO.getPubKey(),
|
||||
loginSignDTO.getLoginSignData(),
|
||||
loginSignDTO.getSignValue());
|
||||
result = result && verified;
|
||||
}
|
||||
if (!result) {
|
||||
throw new IllegalArgumentException("login sign verify failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void whitelistCheck(HttpServletRequest request) {
|
||||
String clientIp = IpWhitelistUtil.getClientIp(request);
|
||||
List<String> whitelist = IpWhitelistUtil.readWhitelist(resolveWhitelistPath());
|
||||
if (!whitelist.isEmpty()) {
|
||||
if (!IpWhitelistUtil.isIpInWhitelist(clientIp, whitelist)) {
|
||||
throw new IllegalArgumentException("access denied");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveWhitelistPath() {
|
||||
String configured = loginProperties.getWhitelistPath();
|
||||
if (configured != null && !configured.isBlank()) {
|
||||
return configured;
|
||||
}
|
||||
return System.getProperty("user.dir") + "/config/whitelist.txt";
|
||||
}
|
||||
|
||||
private void checkRolePassword(String role, String password) {
|
||||
Map<String, String> rolePasswords = loginProperties.getRolePasswords();
|
||||
if (rolePasswords == null || rolePasswords.isEmpty()) {
|
||||
throw new IllegalStateException("role password config missing");
|
||||
}
|
||||
String expected = rolePasswords.get(role);
|
||||
if (expected == null) {
|
||||
throw new IllegalArgumentException("role not found: " + role);
|
||||
}
|
||||
if (!expected.equals(password)) {
|
||||
throw new IllegalArgumentException("password check failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void setRandomList(List<String> randomList) {
|
||||
this.randomList = randomList;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user