Forráskód Böngészése

白马三方接口相关

liuyc 1 éve
szülő
commit
f5ca116c08

+ 1 - 0
blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java

@@ -32,6 +32,7 @@ public class AuthProvider {
     private static final List<String> DEFAULT_SKIP_URL = new ArrayList<>();
 
     static {
+        DEFAULT_SKIP_URL.add("/bm/**"); //白马三方接口放行
         DEFAULT_SKIP_URL.add("/example");
         DEFAULT_SKIP_URL.add("/oauth/token/**");
         DEFAULT_SKIP_URL.add("/loginByToken");

+ 13 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/bean/GetTokenDTO.java

@@ -0,0 +1,13 @@
+package org.springblade.system.user.bean;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class GetTokenDTO implements Serializable {
+
+    private String uuid;
+    private String idcard;
+
+}

+ 19 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/bean/TripartiteSecretKeyBean.java

@@ -0,0 +1,19 @@
+package org.springblade.system.user.bean;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("a_tripartite_secret_key")
+public class TripartiteSecretKeyBean implements Serializable {
+
+    private Long uuid;
+    private String publicKey;
+    private String privateKey;
+    private Date createTime;
+    private String appKey;
+
+}

+ 230 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/thirdparty/ThirdPartySystemApi.java

@@ -0,0 +1,230 @@
+package org.springblade.system.user.thirdparty;
+
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.Consts;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.message.BasicNameValuePair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springblade.system.entity.AuthClient;
+import org.springblade.system.user.bean.GetTokenDTO;
+import org.springblade.system.user.bean.TripartiteSecretKeyBean;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.util.RsaUtils;
+import org.springblade.system.user.util.TokenBucket;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+
+/**
+ * 白马三方接口相关
+ *
+ * @author liuyc
+ * @date 2023年11月14日15:15:42
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bm")
+public class ThirdPartySystemApi {
+
+    private final JdbcTemplate jdbcTemplate;
+    private static final Logger logger = LoggerFactory.getLogger(ThirdPartySystemApi.class);
+    private final TokenBucket tokenBucket = new TokenBucket(5, 5, 60000); /*令牌桶=60秒5次*/
+
+    /**
+     * 获取公钥信息
+     */
+    @GetMapping("/getPublicKeyAndUUIDByAppKey")
+    public Map<String, Object> getPublicKeyAndUUID(@RequestParam String appKey) {
+        if (!tokenBucket.tryConsume()) {
+            throw new ServiceException("请求频率超过限制,请稍后再试");
+        }
+        if (StringUtils.isEmpty(appKey)) {
+            throw new ServiceException("appKey不能为空");
+        }
+        if (!isValidAppKey(appKey)) {
+            throw new ServiceException("appKey验证失败");
+        }
+
+        /*创建秘钥对*/
+        Map<String, String> keyPair = RsaUtils.createKeyPair();
+        String pk = keyPair.get("pk");
+        String sk = keyPair.get("sk");
+        Long uuid = SnowFlakeUtil.getId();
+
+        Map<String, Object> resultMap = new HashMap<>();
+        try {
+            insertKeyPairIntoDatabase(uuid, pk, sk, appKey);
+        } catch (Exception e) {
+            resultMap.put("uuid", null);
+            resultMap.put("pk", null);
+            resultMap.put("code", 400);
+            resultMap.put("msg", e.getMessage());
+            return resultMap;
+        }
+        resultMap.put("uuid", uuid);
+        resultMap.put("pk", pk);
+        resultMap.put("code", 200);
+        resultMap.put("msg", "操作成功");
+        return resultMap;
+    }
+
+    private boolean isValidAppKey(String appKey) {
+        List<AuthClient> bladeClients = jdbcTemplate.query(
+                "SELECT (1) FROM blade_client WHERE client_id = ?",
+                new Object[]{appKey},
+                new BeanPropertyRowMapper<>(AuthClient.class)
+        );
+        return !bladeClients.isEmpty();
+    }
+
+    private void insertKeyPairIntoDatabase(Long uuid, String pk, String sk, String appKey) {
+        jdbcTemplate.update(
+                "INSERT INTO a_tripartite_secret_key (uuid, public_key, private_key, create_time, app_key) VALUES (?, ?, ?, ?, ?)",
+                uuid, pk, sk, new Date(), appKey
+        );
+    }
+
+    /**
+     * 获取token
+     */
+    @PostMapping("/getTokenByCondition")
+    public R<Object> getToken(@RequestBody GetTokenDTO dto) {
+        if (dto.getUuid().isEmpty() || dto.getIdcard().isEmpty()) {
+            throw new ServiceException("入参不能为空");
+        }
+
+        /*根据uuid获取私钥*/
+        TripartiteSecretKeyBean tripartiteSecretKey = jdbcTemplate.query(
+                "SELECT private_key FROM a_tripartite_secret_key WHERE uuid = ?",
+                new Object[]{dto.getUuid()},
+                new BeanPropertyRowMapper<>(TripartiteSecretKeyBean.class))
+                .stream().findAny().orElse(null);
+
+        if (tripartiteSecretKey != null && StringUtils.isNotEmpty(tripartiteSecretKey.getPrivateKey())) {
+            /*解密密钥对获取身份证信息*/
+            String privateKey = tripartiteSecretKey.getPrivateKey();
+            String decryptedIdCard = RsaUtils.decryptWithSk(dto.getIdcard(), privateKey);
+
+            /*根据身份证获取用户token*/
+            if (StringUtils.isNotEmpty(decryptedIdCard)) {
+                List<User> users = jdbcTemplate.query(
+                        "SELECT account, plaintext_password FROM blade_user WHERE is_deleted = 0 AND status = 1 AND id_number = ?",
+                        new Object[]{decryptedIdCard},
+                        new BeanPropertyRowMapper<>(User.class)
+                );
+
+                if (users.size() > 0 && users.size() != 1) {
+                    throw new ServiceException("获取用户信息异常");
+                } else if (users.size() == 1) {
+                    User user = users.get(0);
+                    HttpClient httpClient = HttpClientBuilder.create().build();
+                    HttpPost httpPost = new HttpPost("http://127.0.0.1:8090/blade-auth/oauth/token");
+                    httpPost.setHeader("Authorization", "Basic Y2xpZW50OmNsaWVudF9zZWNyZXQ=");
+                    httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
+                    httpPost.setHeader("Tenant-Id", "000000");
+                    List<NameValuePair> params = new ArrayList<NameValuePair>();
+                    params.add(new BasicNameValuePair("grant_type", "password"));
+                    params.add(new BasicNameValuePair("username", user.getAccount()));
+
+                    /*密码加密*/
+                    String plaintextPassword = user.getPlaintextPassword();
+                    String hex = DigestUtil.md5Hex(plaintextPassword);
+
+                    params.add(new BasicNameValuePair("password", hex));
+                    params.add(new BasicNameValuePair("scope", "all"));
+                    params.add(new BasicNameValuePair("tenantId", "000000"));
+                    httpPost.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
+
+                    try {
+                        HttpResponse httpResponse = httpClient.execute(httpPost);
+                        InputStream inputStream = httpResponse.getEntity().getContent();
+                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+                        byte[] buffer = new byte[1024];
+                        int index;
+                        while ((index = inputStream.read(buffer)) != -1) {
+                            outputStream.write(buffer, 0, index);
+                        }
+
+                        JSONObject jsonObject = JSON.parseObject(outputStream.toString());
+                        Object accessToken = jsonObject.get("access_token");
+                        if (accessToken != null) {
+                            Object expiresIn = jsonObject.get("expires_in");
+                            int minutes = 0;
+                            if (expiresIn instanceof Integer) {
+                                int seconds = (Integer) expiresIn;
+                                minutes = seconds / 60;
+                            }
+                            Map<String, Object> resultMap = new HashMap<>();
+                            resultMap.put("access_token", accessToken);
+                            resultMap.put("expires_in", minutes);
+                            return R.data(200, resultMap, "操作成功");
+
+                        } else {
+                            return R.data(400, null, "解析异常,token为空");
+                        }
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                        return R.data(500, null, e.getMessage());
+                    }
+                } else {
+                    return R.data(400, null, "未获取到对应的用户信息");
+                }
+            }
+        }
+        return R.data(451, null, "未获取到对应的私钥信息");
+    }
+
+    /**
+     * 定时删除昨天及之前的密钥对信息
+     */
+    @Scheduled(cron = "0 0 1 * * ?") //每天凌晨1点执行
+    public void delTripartiteSecretKeyInfos() {
+        //获取昨天及之前的日期
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_MONTH, -1);
+        Date yesterday = calendar.getTime();
+
+        //执行删除操作
+        String sql = "DELETE FROM a_tripartite_secret_key WHERE create_time <= ?";
+        int deletedRows = jdbcTemplate.update(sql, yesterday);
+
+        logger.info("执行方法【delTripartiteSecretKeyInfos】,删除 " + deletedRows + " 条密钥对信息成功...");
+    }
+
+    /*public static void main(String[] args) {
+        //公钥
+        String pk = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKHbUex3gTpQsaGgMtfWWDwE6LrCETLr8c588diQYHSdVEYbU6A1RnKioGIxGer6G6Cb/81wn6s9Zx833uNf6VkCAwEAAQ==";
+
+        //字符串
+        String idcard = "100136199809011400";
+
+        //使用公钥加密
+        String encryptedIdCard = RsaUtils.encryptWithPk(idcard, pk);
+
+        System.out.println("Encrypted Id Card: " + encryptedIdCard);
+    }*/
+
+}
+

+ 209 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/util/RsaUtils.java

@@ -0,0 +1,209 @@
+package org.springblade.system.user.util;
+
+import javax.crypto.Cipher;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+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;
+
+public final class RsaUtils {
+    /**
+     * 密钥长度 于原文长度对应 以及越长速度越慢
+     */
+    private final static int KEY_SIZE = 512;
+
+    /**
+     * 随机生成密钥对
+     */
+    public static Map<String, String> createKeyPair() {
+        try {
+            Map<String, String> keyMap = new HashMap<>(2);
+            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
+            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
+            // 初始化密钥对生成器
+            keyPairGen.initialize(KEY_SIZE, new SecureRandom());
+            // 生成一个密钥对,保存在keyPair中
+            KeyPair keyPair = keyPairGen.generateKeyPair();
+            // 得到私钥
+            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+            // 得到公钥
+            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+            String publicKeyString = encryptBASE64(publicKey.getEncoded());
+            // 得到私钥字符串
+            String privateKeyString = encryptBASE64(privateKey.getEncoded());
+            // 将公钥和私钥保存到Map
+            keyMap.put("pk", publicKeyString);
+            keyMap.put("sk", privateKeyString);
+            return keyMap;
+        } catch (Exception ex) {
+            throw new RuntimeException("密钥对生成失败");
+        }
+    }
+
+    /**
+     * RSA公钥加密
+     *
+     * @param data      加密字符串
+     * @param publicKey 公钥
+     * @return 密文
+     */
+    public static String encryptWithPk(String data, String publicKey) {
+        try {
+            //base64编码的公钥
+            byte[] decoded = decryptBASE64(publicKey);
+            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
+            //RSA加密
+            Cipher cipher = Cipher.getInstance("RSA");
+            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
+            return encryptBASE64(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
+        } catch (Exception ex) {
+            throw new RuntimeException("数据加密处理失败(encrypt with pk)");
+        }
+    }
+
+    /**
+     * RSA公钥解密
+     *
+     * @param data      加密字符串
+     * @param publicKey 公钥
+     * @return 明文
+     */
+    public static String decryptWithPk(String data, String publicKey) {
+        try {
+            //64位解码加密后的字符串
+            byte[] inputByte = decryptBASE64(data);
+            //base64编码的私钥
+            byte[] decoded = decryptBASE64(publicKey);
+            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
+            //RSA解密
+            Cipher cipher = Cipher.getInstance("RSA");
+            cipher.init(Cipher.DECRYPT_MODE, pubKey);
+            return new String(cipher.doFinal(inputByte));
+        } catch (Exception ex) {
+            throw new RuntimeException("数据解析处理失败(decrypt with pk)");
+        }
+    }
+
+    /**
+     * RSA私钥加密
+     *
+     * @param data       加密字符串
+     * @param privateKey 私钥
+     * @return 密文
+     */
+    public static String encryptWithSk(String data, String privateKey) {
+        try {
+            //base64编码的公钥
+            byte[] decoded = decryptBASE64(privateKey);
+            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
+            //RSA加密
+            Cipher cipher = Cipher.getInstance("RSA");
+            cipher.init(Cipher.ENCRYPT_MODE, priKey);
+            return encryptBASE64(cipher.doFinal(data.getBytes(StandardCharsets.UTF_8)));
+        } catch (Exception ex) {
+            throw new RuntimeException("数据加密处理失败(encrypt with sk)");
+        }
+    }
+
+    /**
+     * RSA私钥解密
+     *
+     * @param data       加密字符串
+     * @param privateKey 私钥
+     * @return 明文
+     */
+    public static String decryptWithSk(String data, String privateKey) {
+        try {
+            //64位解码加密后的字符串
+            byte[] inputByte = decryptBASE64(data);
+            //base64编码的私钥
+            byte[] decoded = decryptBASE64(privateKey);
+            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
+            //RSA解密
+            Cipher cipher = Cipher.getInstance("RSA");
+            cipher.init(Cipher.DECRYPT_MODE, priKey);
+            return new String(cipher.doFinal(inputByte));
+        } catch (Exception ex) {
+            throw new RuntimeException("数据解析处理失败(decrypt with sk)");
+        }
+    }
+
+    /**
+     * 编码返回字符串
+     */
+    private static String encryptBASE64(byte[] key) {
+        try {
+            return Base64.getEncoder().encodeToString(key);
+        } catch (Exception ex) {
+            throw new RuntimeException("编码返回字符串失败");
+        }
+    }
+
+    /**
+     * 解码返回byte
+     */
+    private static byte[] decryptBASE64(String key) {
+        try {
+
+            return Base64.getDecoder().decode(key);
+        } catch (Exception ex) {
+            throw new RuntimeException("解码返回byte失败");
+        }
+    }
+
+    /*private static void testCreateKeyPair() {
+        //生成公钥和私钥
+        Map<String, String> keyMap = createKeyPair();
+        //加密字符串
+        long temp = System.currentTimeMillis();
+        System.out.println("公钥:" + keyMap.get("pk"));
+        System.out.println("私钥:" + keyMap.get("sk"));
+        System.out.println("生成密钥消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+
+        String message = "RSA测试aaa";
+        System.out.println("原文:" + message);
+        System.out.println("--------------------------------------");
+        temp = System.currentTimeMillis();
+        String messagePEn = encryptWithPk(message, keyMap.get("pk"));
+        System.out.println("密文:" + messagePEn);
+        System.out.println("加密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+
+        temp = System.currentTimeMillis();
+        String messageSDe = decryptWithSk(messagePEn, keyMap.get("sk"));
+        System.out.println("解密:" + messageSDe);
+        System.out.println("解密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+        System.out.println("--------------------------------------");
+
+        temp = System.currentTimeMillis();
+        String messageSEn = encryptWithSk(message, keyMap.get("sk"));
+        System.out.println("密文:" + messageSEn);
+        System.out.println("加密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+
+        temp = System.currentTimeMillis();
+        String messagePDe = decryptWithPk(messageSEn, keyMap.get("pk"));
+        System.out.println("解密:" + messagePDe);
+        System.out.println("解密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "秒");
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCreateKeyPair();
+        String pk = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALZL9pOjNpDs1wu15o8xmoeNg4YGCUO5zoLrKiGlmFl7\\r\\nCKXKwJ83pDmu/bE4Szf2RAyzwr33bViqoNH45/EaCrECAwEAAQ==\\r\\n";
+        System.out.println(encryptWithPk("admin", pk));
+        System.out.println(encryptWithPk("admin123", pk));
+
+        String str1 = Base64.getEncoder().encodeToString(pk.getBytes());
+        String str2 = (new BASE64Encoder()).encodeBuffer(pk.getBytes());
+        System.out.println("# " + str1);
+        System.out.println("# " + str2);
+        System.out.println(str1.equals(str2));
+    }*/
+
+}

+ 41 - 0
blade-service/blade-user/src/main/java/org/springblade/system/user/util/TokenBucket.java

@@ -0,0 +1,41 @@
+package org.springblade.system.user.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TokenBucket {
+
+    private final int capacity;
+    private final AtomicInteger tokens;
+    private long lastRefillTime;
+    private final long refillInterval;
+
+    public TokenBucket(int capacity, int tokens, long refillInterval) {
+        this.capacity = capacity;
+        this.tokens = new AtomicInteger(Math.min(tokens, capacity));
+        this.lastRefillTime = System.currentTimeMillis();
+        this.refillInterval = refillInterval;
+    }
+
+    public synchronized boolean tryConsume() {
+        refill();
+        if (tokens.get() > 0) {
+            tokens.decrementAndGet();
+            return true;
+        }
+        return false;
+    }
+
+    private void refill() {
+        long currentTime = System.currentTimeMillis();
+        long timeSinceLastRefill = currentTime - lastRefillTime;
+
+        //计算经过的时间内需要补充的令牌数
+        int newTokens = (int) (timeSinceLastRefill / refillInterval);
+
+        //设置桶中的令牌数,不超过桶的容量
+        tokens.set(Math.min(capacity, tokens.get() + newTokens));
+
+        lastRefillTime = currentTime;
+    }
+
+}