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