feat(okx): 添加OKX WebSocket认证和签名工具
- 新增ExchangeInfoEnum枚举类,用于管理OKX账户的API密钥信息
- 实现SignUtils工具类,支持REST和WebSocket的HmacSHA256签名
- 更新LoginWs类引用路径,使用新的枚举和工具类
- 修改OkxQuantWebSocketClient,使用ExchangeInfoEnum判断账户类型切换URL
- 移除旧的INTERNET布尔变量配置
- 调整包结构路径引用,统一到okxWs模块下
2 files modified
2 files added
| | |
| | | import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxWs.*; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums; |
| | | import com.xcong.excoin.modules.okxNewPrice.utils.SSLConfig; |
| | | import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeService; |
| | |
| | | |
| | | private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/private"; |
| | | private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/private"; |
| | | private static final boolean INTERNET = false; |
| | | |
| | | /** |
| | | * 订阅频道指令 |
| | |
| | | SSLConfig.configureSSL(); |
| | | System.setProperty("https.protocols", "TLSv1.2,TLSv1.3"); |
| | | String WS_URL = WS_URL_MONIPAN; |
| | | if (INTERNET){ |
| | | if (ExchangeInfoEnum.OKX_UAT.isAccountType()){ |
| | | WS_URL = WS_URL_SHIPAN; |
| | | } |
| | | URI uri = new URI(WS_URL); |
| | |
| | | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxpi.config.ExchangeInfoEnum; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxpi.config.utils.SignUtils; |
| | | import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum; |
| | | import com.xcong.excoin.modules.okxNewPrice.utils.SignUtils; |
| | | import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.java_websocket.client.WebSocketClient; |
| New file |
| | |
| | | package com.xcong.excoin.modules.okxNewPrice.okxWs.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * 交易信息枚举类 |
| | | * 用于存储不同交易账户的密钥信息,包括实盘账户和模拟账户 |
| | | */ |
| | | @Getter |
| | | public enum ExchangeInfoEnum { |
| | | |
| | | /** |
| | | * 模拟盘账户信息 |
| | | * 存储了模拟盘交易所需的API密钥、秘钥和通过码 |
| | | */ |
| | | OKX_UAT("7a023eb2-06c0-4255-9969-b86ea1cef0d7", |
| | | "D0106A4D63BD22BEAB9CBA8F41219661", |
| | | "Aa12345678@", |
| | | false); |
| | | |
| | | // /** |
| | | // * 模拟盘账户信息 |
| | | // * 存储了模拟盘交易所需的API密钥、秘钥和通过码 |
| | | // */ |
| | | // OKX_UAT("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f", |
| | | // "7AF4A574BC44907CE76BBFF91F53852D", |
| | | // "Aa123456@", |
| | | // false); |
| | | |
| | | // API公钥,用于识别用户身份 |
| | | private String apiKey; |
| | | |
| | | // API秘钥,用于签名和验证请求 |
| | | private String secretKey; |
| | | |
| | | // API通过码,用于额外的身份验证 |
| | | private String passphrase; |
| | | |
| | | // 账户类型,true表示实盘账户,false表示模拟账户 |
| | | private boolean accountType; |
| | | |
| | | /** |
| | | * 构造方法 |
| | | * |
| | | * @param apiKey API公钥,用于识别用户身份 |
| | | * @param secretKey API秘钥,用于签名和验证请求 |
| | | * @param passphrase API通过码,用于额外的身份验证 |
| | | * @param accountType 账户类型,true表示实盘账户,false表示模拟账户 |
| | | */ |
| | | ExchangeInfoEnum(String apiKey, String secretKey, String passphrase, boolean accountType) { |
| | | this.apiKey = apiKey; |
| | | this.secretKey = secretKey; |
| | | this.passphrase = passphrase; |
| | | this.accountType = accountType; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xcong.excoin.modules.okxNewPrice.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import javax.crypto.Mac; |
| | | import javax.crypto.spec.SecretKeySpec; |
| | | import java.util.Base64; |
| | | |
| | | |
| | | @Slf4j |
| | | public class SignUtils { |
| | | |
| | | public static String signRest(String secretKey, String timestamp, String method, String path, String body) { |
| | | String str = String.format("%s%s%s%s", |
| | | timestamp, // timestamp |
| | | method, // method GET/POST |
| | | path, // requestPath |
| | | body // body |
| | | ); |
| | | try { |
| | | return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes())); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * HmacSHA256算法,返回的结果始终是32位 |
| | | * |
| | | * @param key 加密的键,可以是任何数据 |
| | | * @param content 待加密的内容 |
| | | * @return 加密后的内容 |
| | | * @throws Exception ex |
| | | */ |
| | | public static byte[] hmacSHA256(byte[] key, byte[] content) throws Exception { |
| | | Mac hmacSha256 = Mac.getInstance("HmacSHA256"); |
| | | hmacSha256.init(new SecretKeySpec(key, 0, key.length, "HmacSHA256")); |
| | | return hmacSha256.doFinal(content); |
| | | } |
| | | |
| | | public static String signWebsocket(String timestamp, String secretKey) { |
| | | String str = String.format("%s%s%s", |
| | | timestamp, |
| | | "GET", |
| | | "/users/self/verify"); |
| | | try { |
| | | return Base64.getEncoder().encodeToString(hmacSHA256(secretKey.getBytes(), str.getBytes())); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | } |