diff --git a/RELEASE.md b/RELEASE.md index 27682b6e..34e973d7 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,3 +1,11 @@ +# Release 1.11.0 + +#### Major Features and Improvements +**Major Features** + +* Binning component display optimization +* Password configuration item allows encryption + # Release 1.10.0 #### Major Features and Improvements diff --git a/pom.xml b/pom.xml index d56a8a38..019baccb 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 fateboard fateboard - 1.10.0 + 1.11.0 org.springframework.boot @@ -21,7 +21,7 @@ 1.1.10 - 1.1.10 + 1.18.24 2.0.0 diff --git a/resources-front-end/src/transform/fn/binningDataHandler.js b/resources-front-end/src/transform/fn/binningDataHandler.js index 6fc82f4c..672751d4 100644 --- a/resources-front-end/src/transform/fn/binningDataHandler.js +++ b/resources-front-end/src/transform/fn/binningDataHandler.js @@ -215,7 +215,7 @@ export default function(data, header, type, partyId, role, Currentrole, skipStat monotonicity: skipStatic ? '-' : (Currentrole === 'host' ? '-' : (data[key].isWoeMonotonic ? data[key].isWoeMonotonic.toString() : 'false')), partyid: partyId, role, - binning_count: data[key].splitPoints.length || data[key].ivArray.length + binning_count: parseFloat(data[key].binNums) }) options.push({ value: key, diff --git a/resources-front-end/src/views/job-dashboard/log/Logs.vue b/resources-front-end/src/views/job-dashboard/log/Logs.vue index eae027c6..e80fb9eb 100644 --- a/resources-front-end/src/views/job-dashboard/log/Logs.vue +++ b/resources-front-end/src/views/job-dashboard/log/Logs.vue @@ -254,7 +254,7 @@ export default { getInstanceIdFromFlow() { return getInstanceId().then((res) => { const result = [] - for (const [instance] of Object.entries(res.data)) { + for (const instance of Object.values(res.data)) { if (instance.host === window.location.host) { this.currentInstanceId = instance.instance_id } diff --git a/src/main/java/com/webank/ai/fate/board/services/UserService.java b/src/main/java/com/webank/ai/fate/board/services/UserService.java index dc8eb73a..60e60233 100644 --- a/src/main/java/com/webank/ai/fate/board/services/UserService.java +++ b/src/main/java/com/webank/ai/fate/board/services/UserService.java @@ -16,6 +16,8 @@ package com.webank.ai.fate.board.services; import com.webank.ai.fate.board.pojo.UserDTO; +import com.webank.ai.fate.board.utils.StandardRSAUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -63,6 +65,16 @@ public boolean checkUser(String username, String password) { updateConfig(); String usernameValue = getValue("server.board.login.username"); String passwordValue = getValue("server.board.login.password"); + String privateKey = getValue("server.board.encrypt.private_key"); + String encrypted = getValue("server.board.encrypt.enable"); + if (StringUtils.isNotBlank(privateKey) && "true".equalsIgnoreCase(encrypted)) { + try { + passwordValue = StandardRSAUtils.decryptByPrivateKey(passwordValue, privateKey); + } catch (Exception e) { + logger.error("decrypt password error"); + return false; + } + } if (!username.equals(usernameValue)) { return false; diff --git a/src/main/java/com/webank/ai/fate/board/utils/StandardRSAUtils.java b/src/main/java/com/webank/ai/fate/board/utils/StandardRSAUtils.java index e590e0c9..96bff4e1 100644 --- a/src/main/java/com/webank/ai/fate/board/utils/StandardRSAUtils.java +++ b/src/main/java/com/webank/ai/fate/board/utils/StandardRSAUtils.java @@ -1,12 +1,13 @@ package com.webank.ai.fate.board.utils; -import org.apache.commons.codec.binary.Base64; - import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; import java.security.*; +import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -15,69 +16,296 @@ */ public class StandardRSAUtils { - private static Map keyMap = new HashMap<>(); + /** + * 加密算法RSA + */ + public static final String KEY_ALGORITHM = "RSA"; + + /** + * 签名算法 + */ + public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; + + /** + * 获取公钥的key + */ + private static final String PUBLIC_KEY = "RSAPublicKey"; + + /** + * 获取私钥的key + */ + private static final String PRIVATE_KEY = "RSAPrivateKey"; + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 245; /** - * 随机生成密钥对 + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 256; + + /** + *

+ * 生成密钥对(公钥和私钥) + *

* - * @throws NoSuchAlgorithmException + * @return + * @throws Exception */ - public static void getKeyPair() throws Exception { - //KeyPairGenerator类用于生成公钥和密钥对,基于RSA算法生成对象 - KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); - keyPairGen.initialize(2048, new SecureRandom()); - //生成一个密钥对,保存在keyPair中 + public static Map genKeyPair() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + keyPairGen.initialize(2048); KeyPair keyPair = keyPairGen.generateKeyPair(); - PrivateKey privateKey = keyPair.getPrivate();//得到私钥 - PublicKey publicKey = keyPair.getPublic();//得到公钥 - //得到公钥字符串 - String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); - //得到私钥字符串 - String privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded())); - //将公钥和私钥保存到Map - keyMap.put(0, publicKeyString);//0表示公钥 - keyMap.put(1, privateKeyString);//1表示私钥 + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + Map keyMap = new HashMap(2); + keyMap.put(PUBLIC_KEY, publicKey); + keyMap.put(PRIVATE_KEY, privateKey); + return keyMap; } /** - * RSA公钥加密 + *

+ * 用私钥对信息生成数字签名 + *

* - * @param str 加密字符串 - * @param publicKey 公钥 - * @return 密文 - * @throws Exception 加密过程中的异常信息 + * @param msg 已加密数据 + * @param privateKey 私钥(BASE64编码) + * @return + * @throws Exception */ - public static String encrypt(String str, String publicKey) throws Exception { - //base64编码的公钥 - byte[] decoded = Base64.decodeBase64(publicKey); - RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA") - .generatePublic(new X509EncodedKeySpec(decoded)); - //RAS加密 - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, pubKey); - String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8"))); - return outStr; + public static String sign(String msg, String privateKey) throws Exception { + byte[] data = msg.getBytes(); + byte[] keyBytes = Base64.getDecoder().decode(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(privateK); + signature.update(data); + return Base64.getEncoder().encodeToString(signature.sign()); } /** - * RSA私钥解密 + *

+ * 校验数字签名 + *

* - * @param str 加密字符串 - * @param privateKey 私钥 - * @return 铭文 - * @throws Exception 解密过程中的异常信息 + * @param msg 已加密数据 + * @param publicKey 公钥(BASE64编码) + * @param sign 数字签名 + * @return + * @throws Exception */ - public static String decrypt(String str, String privateKey) throws Exception { - //Base64解码加密后的字符串 - byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8")); - //Base64编码的私钥 - byte[] decoded = Base64.decodeBase64(privateKey); - PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); - //RSA解密 - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, priKey); - String outStr = new String(cipher.doFinal(inputByte)); - return outStr; + public static boolean verify(String msg, String publicKey, String sign) + throws Exception { + byte[] data = msg.getBytes(); + byte[] keyBytes = Base64.getDecoder().decode(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey publicK = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(publicK); + signature.update(data); + return signature.verify(Base64.getDecoder().decode(sign)); + } + /** + *

+ * 私钥解密 + *

+ * + * @param encryptedDataStr 已加密数据 + * @param privateKey 私钥(BASE64编码) + * @return + * @throws Exception + */ + public static String decryptByPrivateKey(String encryptedDataStr, String privateKey) + throws Exception { + byte[] encryptedData = Base64.getDecoder().decode(encryptedDataStr); + byte[] keyBytes = Base64.getDecoder().decode(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateK); + int inputLen = encryptedData.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_DECRYPT_BLOCK; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + return new String(decryptedData); } + + /** + *

+ * 公钥解密 + *

+ * + * @param encryptedDataStr 已加密数据 + * @param publicKey 公钥(BASE64编码) + * @return + * @throws Exception + */ + public static String decryptByPublicKey(String encryptedDataStr, String publicKey) + throws Exception { + byte[] encryptedData = Base64.getDecoder().decode(encryptedDataStr); + byte[] keyBytes = Base64.getDecoder().decode(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicK = keyFactory.generatePublic(x509KeySpec); + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, publicK); + int inputLen = encryptedData.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_DECRYPT_BLOCK; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + return new String(decryptedData); + } + + /** + *

+ * 公钥加密 + *

+ * + * @param msg 源数据 + * @param publicKey 公钥(BASE64编码) + * @return + * @throws Exception + */ + public static String encryptByPublicKey(String msg, String publicKey) + throws Exception { + byte[] data = msg.getBytes(); + byte[] keyBytes = Base64.getDecoder().decode(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicK = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, publicK); + int inputLen = data.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(data, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + + String encryptedDataStr = Base64.getEncoder().encodeToString(encryptedData); + return encryptedDataStr; + } + + /** + *

+ * 私钥加密 + *

+ * + * @param msg 源数据 + * @param privateKey 私钥(BASE64编码) + * @return + * @throws Exception + */ + public static String encryptByPrivateKey(String msg, String privateKey) + throws Exception { + byte[] data = msg.getBytes(); + byte[] keyBytes = Base64.getDecoder().decode(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateK = keyFactory.generatePrivate(pkcs8KeySpec); + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, privateK); + int inputLen = data.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(data, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + + String encryptedDataStr = Base64.getEncoder().encodeToString(encryptedData); + return encryptedDataStr; + } + + /** + *

+ * 获取私钥 + *

+ * + * @param keyMap 密钥对 + * @return + * @throws Exception + */ + public static String getPrivateKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + return Base64.getEncoder().encodeToString(key.getEncoded()); + } + + /** + *

+ * 获取公钥 + *

+ * + * @param keyMap 密钥对 + * @return + * @throws Exception + */ + public static String getPublicKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PUBLIC_KEY); + return Base64.getEncoder().encodeToString(key.getEncoded()); + } + + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index fe7e208a..0b0e8a6c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,6 +18,8 @@ server.compression.enabled=true server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain server.board.login.username=admin server.board.login.password=admin +server.board.encrypt.private_key= +server.board.encrypt.enable=false #only [h,m,s] is available server.servlet.session.timeout=4h server.servlet.session.cookie.max-age=4h