Newspaper 1 năm trước cách đây
mục cha
commit
cc255ee30c

+ 6 - 0
pom.xml

@@ -67,6 +67,12 @@
         </dependency>
 
         <dependency>
+            <groupId>io.github.mingyang66</groupId>
+            <artifactId>oceansky-jwt</artifactId>
+            <version>4.3.2</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <optional>true</optional>

+ 21 - 0
src/main/java/com/huimv/wine/config/KeyUrlConfig.java

@@ -0,0 +1,21 @@
+package com.huimv.wine.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@Data
+public class KeyUrlConfig {
+    @Value("${config.server_private}")
+    private String pair1PrivateUrl;
+
+    @Value("${config.server_public}")
+    private String pair1PublicUrl;
+
+    @Value("${config.client_private}")
+    private String pair2PrivateUrl;
+
+    @Value("${config.client_public}")
+    private String pair2PublicUrl;
+}

+ 166 - 0
src/main/java/com/huimv/wine/utils/KeyUtil.java

@@ -0,0 +1,166 @@
+package com.huimv.wine.utils;
+
+import com.huimv.wine.config.KeyUrlConfig;
+import org.apache.ibatis.annotations.Case;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+@Component
+public class KeyUtil {
+    private static String serverPrivate;
+
+    private static String serverPublic;
+
+    private static String clientPrivate;
+
+    private static String clientPublic;
+
+    public static void setServerPrivate() {
+//        KeyUrlConfig keyUrlConfig = SpringContextUtil.getBean(KeyUrlConfig.class);
+//        String serverPrivateUrl = keyUrlConfig.getPair1PrivateUrl();
+        InputStream inputStream = KeyUtil.class.getClassLoader().getResourceAsStream("certs/server-private.pem");
+        if (inputStream != null) {
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+                StringBuilder builder = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    builder.append(line).append(System.lineSeparator());
+                }
+                serverPrivate = extractPrivateKeyFromString(builder.toString());
+            } catch (IOException e) {
+                e.printStackTrace();
+                serverPrivate = null;
+            }
+        } else {
+            serverPrivate = null;
+        }
+    }
+
+    public static String extractPrivateKeyFromString(String publicKeyString){
+        String startTag = "-----BEGIN RSA PRIVATE KEY-----";
+        String endTag = "-----END RSA PRIVATE KEY-----";
+
+        int start = publicKeyString.indexOf(startTag) + startTag.length();
+        int end = publicKeyString.indexOf(endTag);
+
+        if (start != -1 && end != -1) {
+            return publicKeyString.substring(start, end).replaceAll("\\s", ""); // 去除空格和换行符
+        } else {
+            throw new IllegalArgumentException("Invalid public key string format.");
+        }
+    }
+
+    public static String extractPublicKeyFromString(String publicKeyString) {
+        String startTag = "-----BEGIN PUBLIC KEY-----";
+        String endTag = "-----END PUBLIC KEY-----";
+
+        int start = publicKeyString.indexOf(startTag) + startTag.length();
+        int end = publicKeyString.indexOf(endTag);
+
+        if (start != -1 && end != -1) {
+            return publicKeyString.substring(start, end).replaceAll("\\s", ""); // 去除空格和换行符
+        } else {
+            throw new IllegalArgumentException("Invalid public key string format.");
+        }
+
+    }
+
+    public static String getServerPrivate() {
+        if (serverPrivate == null) {
+            setServerPrivate();
+        }
+        return serverPrivate;
+    }
+
+    public static void setServerPublic() {
+        KeyUrlConfig keyUrlConfig = SpringContextUtil.getBean(KeyUrlConfig.class);
+        String serverPublicUrl = keyUrlConfig.getPair2PublicUrl();
+        InputStream inputStream = KeyUtil.class.getClassLoader().getResourceAsStream(serverPublicUrl);
+        if (inputStream != null) {
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+                StringBuilder builder = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    builder.append(line).append(System.lineSeparator());
+                }
+                serverPublic = extractPublicKeyFromString(builder.toString());
+            } catch (IOException e) {
+                e.printStackTrace();
+                serverPublic = null;
+            }
+        } else {
+            serverPublic = null;
+        }
+    }
+
+    public static String getServerPublic() {
+        if (serverPublic == null) {
+            setServerPublic();
+        }
+        return serverPublic;
+    }
+
+
+    public static void setClientPrivate() {
+        KeyUrlConfig keyUrlConfig = SpringContextUtil.getBean(KeyUrlConfig.class);
+        String clientPrivateUrl = keyUrlConfig.getPair2PrivateUrl();
+        InputStream inputStream = KeyUtil.class.getClassLoader().getResourceAsStream(clientPrivateUrl);
+        if (inputStream != null) {
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+                StringBuilder builder = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    builder.append(line).append(System.lineSeparator());
+                }
+                clientPrivate = builder.toString();
+            } catch (IOException e) {
+                e.printStackTrace();
+                clientPrivate = null;
+            }
+        } else {
+            clientPrivate = null;
+        }
+    }
+
+    public static String getClientPrivate() {
+        if (clientPrivate == null) {
+            setClientPrivate();
+        }
+        return clientPrivate;
+    }
+
+
+    public static void setClientPublic() {
+//        KeyUrlConfig keyUrlConfig = SpringContextUtil.getBean(KeyUrlConfig.class);
+//        String clientPublicUrl = keyUrlConfig.getPair1PublicUrl();
+        InputStream inputStream = KeyUtil.class.getClassLoader().getResourceAsStream("certs/client-public.pem");
+        if (inputStream != null) {
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+                StringBuilder builder = new StringBuilder();
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    builder.append(line).append(System.lineSeparator());
+                }
+                clientPublic = builder.toString();
+            } catch (IOException e) {
+                e.printStackTrace();
+                clientPublic = null;
+            }
+        } else {
+            clientPublic = null;
+        }
+    }
+
+    public static String getClientPublic() {
+        if (clientPublic == null) {
+            setClientPublic();
+        }
+        return clientPublic;
+    }
+}

+ 178 - 0
src/main/java/com/huimv/wine/utils/RSAUtil.java

@@ -0,0 +1,178 @@
+package com.huimv.wine.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+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.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public class RSAUtil {
+
+    public static final String KEY_ALGORITHM = "RSA";
+
+//    private static final String PUBLIC_KEY = "RSAPublicKey";
+//
+//    private static final String PRIVATE_KEY = "RSAPrivateKey";
+//
+//    // 1024 bits 的 RSA 密钥对,最大加密明文大小
+//    private static final int MAX_ENCRYPT_BLOCK = 117;
+//
+//    // 1024 bits 的 RSA 密钥对,最大解密密文大小
+//    private static final int MAX_DECRYPT_BLOCK = 128;
+//
+//    // 生成密钥对
+//    public static Map<String, Object> initKey(int keysize) throws Exception {
+//        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
+//        // 设置密钥对的 bit 数,越大越安全
+//        keyPairGen.initialize(keysize);
+//        KeyPair keyPair = keyPairGen.generateKeyPair();
+//
+//        // 获取公钥
+//        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+//        // 获取私钥
+//        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+//        Map<String, Object> keyMap = new HashMap<>(2);
+//        keyMap.put(PUBLIC_KEY, publicKey);
+//        keyMap.put(PRIVATE_KEY, privateKey);
+//        return keyMap;
+//    }
+//
+//    // 获取公钥字符串
+//    public static String getPublicKeyStr(Map<String, Object> keyMap) {
+//        // 获得 map 中的公钥对象,转为 key 对象
+//        Key key = (Key) keyMap.get(PUBLIC_KEY);
+//        // 编码返回字符串
+//        return encryptBASE64(key.getEncoded());
+//    }
+//
+//    // 获取私钥字符串
+//    public static String getPrivateKeyStr(Map<String, Object> keyMap) {
+//        // 获得 map 中的私钥对象,转为 key 对象
+//        Key key = (Key) keyMap.get(PRIVATE_KEY);
+//        // 编码返回字符串
+//        return encryptBASE64(key.getEncoded());
+//    }
+
+    // 获取公钥
+    public static PublicKey getPublicKey(String publicKeyString) throws NoSuchAlgorithmException, InvalidKeySpecException {
+        byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString);
+        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte);
+        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+        return keyFactory.generatePublic(keySpec);
+    }
+
+    // 获取私钥
+    public static PrivateKey getPrivateKey(String privateKeyString) throws Exception {
+        byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString);
+        Security.addProvider(new BouncyCastleProvider());
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte);
+        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
+        return keyFactory.generatePrivate(keySpec);
+    }
+
+    /**
+     * BASE64 编码返回加密字符串
+     *
+     * @param key 需要编码的字节数组
+     * @return 编码后的字符串
+     */
+    public static String encryptBASE64(byte[] key) {
+        return new String(Base64.getEncoder().encode(key));
+    }
+
+    /**
+     * BASE64 解码,返回字节数组
+     *
+     * @param key 待解码的字符串
+     * @return 解码后的字节数组
+     */
+    public static byte[] decryptBASE64(String key) {
+        return Base64.getDecoder().decode(key);
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param text         待加密的明文字符串
+     * @param publicKeyStr 公钥
+     * @return 加密后的密文
+     */
+    public static String encrypt1(String text, String publicKeyStr) {
+        try {
+            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
+            cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr));
+            byte[] tempBytes = cipher.doFinal(text.getBytes("UTF-8"));
+            return Base64.getEncoder().encodeToString(tempBytes);
+        } catch (Exception e) {
+            throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e);
+        }
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param secretText    待解密的密文字符串
+     * @param privateKeyStr 私钥
+     * @return 解密后的明文
+     */
+    public static String decrypt1(String secretText, String privateKeyStr) {
+        try {
+            // 生成私钥
+            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
+            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr));
+            // 密文解码
+            byte[] secretTextDecoded = Base64.getDecoder().decode(secretText.getBytes("UTF-8"));
+            byte[] tempBytes = cipher.doFinal(secretTextDecoded);
+            return new String(tempBytes);
+        } catch (Exception e) {
+            throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e);
+        }
+    }
+
+
+    public static String extractPublicKeyFromString(String publicKeyString) {
+        String startTag = "-----BEGIN PUBLIC KEY-----";
+        String endTag = "-----END PUBLIC KEY-----";
+
+        int start = publicKeyString.indexOf(startTag) + startTag.length();
+        int end = publicKeyString.indexOf(endTag);
+
+        if (start != -1 && end != -1) {
+            return publicKeyString.substring(start, end).replaceAll("\\s", ""); // 去除空格和换行符
+        } else {
+            throw new IllegalArgumentException("Invalid public key string format.");
+        }
+
+    }
+
+    public static void main(String[] args) throws Exception {
+//        String cipherText;
+//        // 原始明文
+//        String content = "春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。";
+//
+//        String clientPublic = KeyUtil.getClientPublic();
+//        String privateKey = KeyUtil.getServerPrivate();
+//        // 加密
+//
+//        clientPublic = extractPublicKeyFromString(clientPublic);
+//        privateKey = extractPrivateKeyFromString(privateKey);
+//
+//        cipherText = encrypt1(content, clientPublic);
+//        log.info("加密后的密文:[{}],长度:[{}]", cipherText, cipherText.length());
+        // 解密
+//        String cipherText = "0DPL7N0vYlFhADLFT5WyMsof4LbgmCJySA6Izgkv05QgWCLJQ4vp83MKxxACR5YjrObxsDNILkKC004FlawbGEDP2BS6TrQ0ODVhyZ8DMSP2CuVrQVHVLVs+eWQzQJnu8tL0HbI9grjQnspXFNtgXtzOR6QGVFe5aCmMymVWNTbE3SHYdc7EAHOTdtDErpHZjnsdA2AYVtAbzNZk3pByx/OGydFQk1D85tCLimx3DzHB30afMcJVfJmxORmGhKUy1HC+6Y3KzUTRPDuJ/27Msa5E1bVryC+NYKOiIoXYfcx5dzPGF1IeZZD70iagZG4Hgm8Wl/EF+MA5eWfw3DpIjA==";
+//        String plainText = decrypt1(cipherText , privateKey);
+//        log.info("解密后明文:[{}]", plainText);
+    }
+}
+

+ 12 - 82
src/main/java/com/huimv/wine/utils/Result.java

@@ -1,97 +1,27 @@
 package com.huimv.wine.utils;
 
+import lombok.Data;
+
 import java.io.Serializable;
 
+@Data
 public class Result implements Serializable {
 
-    private boolean success;
-    private Integer code;
-    private String message;
-
-    public boolean isSuccess() {
-        return success;
-    }
-
-    public void setSuccess(boolean success) {
-        this.success = success;
-    }
-
-    public Integer getCode() {
-        return code;
-    }
-
-    public void setCode(Integer code) {
-        this.code = code;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-
-    public void setMessage(String message) {
-        this.message = message;
-    }
-
-    public Object getData() {
-        return data;
-    }
-
-    public void setData(Object data) {
-        this.data = data;
-    }
-
+    private boolean status;
     private Object data;
+    private String msg;
 
-    public Object getData2() {
-        return data2;
-    }
-
-    public void setData2(Object data2) {
-        this.data2 = data2;
-    }
-
-    private Object data2;
-
-    //不需要返回数据时使用
-    public Result(ResultCode code) {
-        this.success = code.success;
-        this.code = code.code;
-        this.message = code.message;
-    }
-
-    public Result(ResultCode code, Object data) {
-        this.success = code.success;
-        this.code = code.code;
-        this.message = code.message;
-        this.data = data;
-    }
-    public Result(ResultCode code, Object data, Object data2) {
-        this.success = code.success;
-        this.code = code.code;
-        this.message = code.message;
+    public Result(boolean status, Object data, String msg) {
+        this.status = status;
         this.data = data;
-        this.data2 = data2;
-
-    }
-
-    public Result(Integer code, String message, boolean success) {
-        this.code = code;
-        this.message = message;
-        this.success = success;
-    }
-
-    /*
-     * 调用ResultCode类封装常用的返回数据
-     */
-    public static Result SUCCESS(){
-        return new Result(ResultCode.SUCCESS);
+        this.msg = msg;
     }
 
-    public static Result ERROR(){
-        return new Result(ResultCode.SERVER_ERROR);
+    public Result success(Object data){
+        return new Result(true,data,"success");
     }
 
-    public static Result FAIL(){
-        return new Result(ResultCode.FAIL);
+    public Result fail(String msg){
+        return new Result(false,null,msg);
     }
 }

+ 98 - 11
src/main/java/com/huimv/wine/ws/WorkerController.java

@@ -1,31 +1,53 @@
 package com.huimv.wine.ws;
 
-import com.huimv.wine.utils.WebsocketWorkerUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.huimv.wine.entity.Worker;
+import com.huimv.wine.entity.vo.WsEvent;
+import com.huimv.wine.mapper.WorkerMapper;
+import com.huimv.wine.utils.*;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
+import org.springframework.web.socket.WebSocketMessage;
 import org.springframework.web.socket.handler.TextWebSocketHandler;
+import sun.nio.cs.ext.MacArabic;
 
 import javax.websocket.*;
 import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
 import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * websocket接口处理类
  */
 @Component
-@ServerEndpoint(value = "/worker/{seq}")
+@ServerEndpoint(value = "/worker/{token}")
 public class WorkerController extends TextWebSocketHandler {
 
     /**
      * 连接事件,加入注解
      */
-    @OnOpen
-    public void onOpen(@PathParam(value = "seq") String seq, Session session) {
-        String message = "[" + seq + "]加入聊天室!!";
-        System.out.println(message);
 
+
+
+    @OnOpen
+    public void onOpen(@PathParam(value = "token") String token, Session session) {
         // 添加到session的映射关系中
-        WebsocketWorkerUtil.addSession(seq, session);
+        WebsocketWorkerUtil.addSession(token, session);
+        getSecrets(session);
+    }
+
+
+
+    private void getSecrets(Session session) {
+        Map map = new HashMap();
+        map.put("public",KeyUtil.getClientPublic());
+        map.put("private",KeyUtil.getClientPrivate());
+        WsEvent wsEvent = new WsEvent("secrets",map);
+        WebsocketWorkerUtil.sendMessage(session,wsEvent);
     }
 
     /**
@@ -33,18 +55,83 @@ public class WorkerController extends TextWebSocketHandler {
      * 用户断开链接
      */
     @OnClose
-    public void onClose(@PathParam(value = "seq") String seq, Session session) {
+    public void onClose(@PathParam(value = "token") String token, Session session) {
         // 删除映射关系
-        WebsocketWorkerUtil.removeSession(seq);
+        WebsocketWorkerUtil.removeSession(token);
     }
 
     /**
      * 当接收到用户上传的消息
      */
     @OnMessage
-    public void onMessage(@PathParam(value = "seq") String seq, Session session, String message) {
-        String msg = "[" + seq + "]:" + message;
+    public void onMessage(@PathParam(value = "token") String token, Session session, String message) {
+        String msg = "[" + token + "]:" + message;
         System.out.println("接收到信息:" + msg);
+
+        JSONObject jsonObject = JSON.parseObject(message);
+        String event = jsonObject.getString("event");
+        JSONObject data = jsonObject.getJSONObject("data");
+
+        if ("login".equals(event)){
+            login(session,token,data);
+        }
+
+        RedisTemplate redisTemplate = SpringContextUtil.getBean(RedisTemplate.class);
+        if (!redisTemplate.hasKey(token)){
+            WsEvent wsEvent = new WsEvent("tokenExpired",null);
+            WebsocketWorkerUtil.sendMessage(session,wsEvent);
+        }else {
+//            switch (event){
+//                case ""
+//            }
+        }
+
+
+
+
+
+//        switch (event) {
+//            case "pin":
+//                keepAlive(session);
+//                break;
+//            case "query":
+//                query(session, data);
+//                break;
+//            case "detail":
+//                detail(data);
+//                break;
+//            case "ready":
+//                ready(session, data);
+//                break;
+//            case "finish":
+//                finish(session, data);
+//                break;
+//            default:
+//                sendError(session, "unrecognized event");
+//                break;
+//        }
+    }
+
+    private void login(Session session, String token, JSONObject data) {
+        WorkerMapper WorkerMapper = SpringContextUtil.getBean(WorkerMapper.class);
+
+        String account = data.getString("account");
+        String password = data.getString("password");
+
+        String serverPrivate = KeyUtil.getServerPrivate();
+        String realPassword = RSAUtil.decrypt1(password, serverPrivate);
+//        Worker worker = WorkerMapper.login(account,realPassword);
+//        if (ObjectUtil.isEmpty(worker)){
+//            WsEvent wsEvent = new WsEvent("loginResult",new Result());
+//            WebsocketWorkerUtil.sendMessage(session,wsEvent);
+//        }
+
+
+
+    }
+
+    private void loginSocket(String message, Session session) {
+
     }
 
     /**

+ 4 - 4
src/main/resources/application-dev.yml

@@ -20,10 +20,10 @@ mybatis-plus:
     # log-impl:   org.apache.ibatis.logging.nologging.NoLoggingImpl
 config:
   prefix: "http://192.168.1.10:3080"
-  server_private: "./certs/server-private.pem"
-  server_public: "./certs/server-public.pem"
-  client_private: "./certs/client-private.pem"
-  client_public: "./certs/client-public.pem"
+  server_private: "certs/server-private.pem"
+  server_public: "certs/server-public.pem"
+  client_private: "certs/client-private.pem"
+  client_public: "certs/client-public.pem"
 
 wx:
   pay_title: "贵州醴泉古酿酒业"

+ 6 - 1
src/main/resources/application.properties

@@ -1,2 +1,7 @@
 spring.application.name=wine-server
-spring.profiles.active=dev
+spring.profiles.active=dev
+vip.scan_api:http://192.168.1.10:3080/seller/vip-test
+config.server_private:certs/server-private.pem
+config.server_public:certs/server-public.pem
+config.client_private:certs/client-private.pem
+config.client_public:certs/client-public.pem