和风天气
pom.xml
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.16</version> </dependency> <!-- 加密算法 --> <dependency> <groupId>net.i2p.crypto</groupId> <artifactId>eddsa</artifactId> <version>0.3.0</version> </dependency>
import cn.hutool.core.map.MapUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.jwt.JWT; import cn.hutool.jwt.signers.JWTSigner; import cn.hutool.jwt.signers.JWTSignerUtil; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Base64; import java.util.Map; public class WeatherEdDSA { static { // 注册 Bouncy Castle 提供者 Security.addProvider(new BouncyCastleProvider()); } public static void main(String[] args) { // 1.生成 公钥和私钥 Map<String, String> keyMap = genPublicPrivateKey(); // 2.授权获取jwt格式的token String jwtToken = authToken(keyMap.get("privateKey")); // 3.通过公钥验证 jwt token boolean b = validateQWeatherToken(jwtToken, keyMap.get("publicKey")); System.out.println(b); } public static String authToken(String privateKeyString) { byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString.trim()); try { // Base64url header + payload String data = buildHeaderPayload(); // Private key PKCS8EncodedKeySpec encoded = new PKCS8EncodedKeySpec(privateKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("Ed25519", "BC"); PrivateKey privateKey = keyFactory.generatePrivate(encoded); // Sign final Signature s = Signature.getInstance("EdDSA"); s.initSign(privateKey); s.update(data.getBytes(StandardCharsets.UTF_8)); byte[] signature = s.sign(); String signatureString = Base64.getEncoder().encodeToString(signature); // Print Token String jwt = data + "." + signatureString; return jwt; } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } /** * 验证和风天气JWT Token * @param jwtToken 待验证的Token字符串 * @param base64PublicKey Base64编码的ED25519公钥(需去除PEM头尾) * @return 验证通过返回true,失败抛出异常 */ public static boolean validateQWeatherToken(String jwtToken, String base64PublicKey) { try{ // 1. 解码Base64公钥 byte[] publicKeyBytes = Base64.getDecoder().decode(base64PublicKey); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); // 2. 使用ED25519算法初始化公钥 KeyFactory keyFactory = KeyFactory.getInstance(EdDSANamedCurveTable.ED_25519, "BC"); PublicKey publicKey = keyFactory.generatePublic(keySpec); // 3. 创建Ed25519验证器 JWTSigner signer = JWTSignerUtil.createSigner(EdDSANamedCurveTable.ED_25519, publicKey); // 4. 验证Token return JWT.of(jwtToken).setSigner(signer).verify(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } private static String buildHeaderPayload() { // Header String headerJson = "{\"alg\": \"EdDSA\", \"kid\": \"CA5CM8YUVG\"}"; // Payload long iat = ZonedDateTime.now(ZoneOffset.UTC).toEpochSecond() - 30; long exp = iat + 1*24*60*60; String payloadJson = "{\"sub\": \"4A89NRJ6WW\", \"iat\": " + iat + ", \"exp\": " + exp + "}"; // Base64url header+payload String headerEncoded = Base64.getEncoder().encodeToString(headerJson.getBytes(StandardCharsets.UTF_8)); String payloadEncoded = Base64.getEncoder().encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8)); return headerEncoded + "." + payloadEncoded; } public static Map<String, String> genPublicPrivateKey() { try { // 1. 创建 KeyPairGenerator 实例 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("Ed25519", "BC"); // 2. 生成密钥对 KeyPair keyPair = keyGen.generateKeyPair(); // 3. 获取公钥和私钥的字节数组 byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded(); // 4. 转换为 Base64 字符串 String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey); String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey); return MapUtil.builder("publicKey", publicKeyBase64) .put("privateKey", privateKeyBase64) .build(); } catch (Exception e) { throw new RuntimeException(e); } } @Test public void qweatherApi() { String baseUrl = "https://xxxxxx.re.qweatherapi.com/"; // String token = authToken(); String token = "eyJhbGciOiAiRWREU0EiLCAia2lkIjogIkNBNUNNOFlVVkcifQ==.eyJzdWIiOiAiNEE4OU5SSjZXVyIsICJpYXQiOiAxNzQ3NjM1NDc5LCAiZXhwIjogMTc0NzcyMTg3OX0=.8G2GkYh1jm0_whE80Db00IyN1M6TaKKRdx8GkVbal1YRO0EeC11cFOrQGZV7dYy5DKjdCYn7B23NvZGbk3xmCg=="; // 实时天气 String weatherNow = baseUrl + "v7/weather/now"; HttpResponse execute = HttpRequest.get(weatherNow + "?location=101270101").bearerAuth(token).execute(); if(200 == execute.getStatus()) { String body = execute.body(); String s = execute.contentEncoding(); System.out.println("内容编码: "+s); System.out.println("实时天气: "+body); } // // // 逐小时天气预报(24h, 72h, 168h) // String weatherHours = baseUrl + "v7/weather/24h"; // execute = HttpRequest.get(weatherHours + "?location=101270101").bearerAuth(token).execute(); // if(200 == execute.getStatus()) { // String body = execute.body(); // System.out.println("逐小时天气预报: "+body); // } // // // 天气灾害预警 // String warningNow = baseUrl + "/v7/warning/now"; // execute = HttpRequest.get(warningNow+"?location=101270101").bearerAuth(token).execute(); // if(200 == execute.getStatus()) { // String body = execute.body(); // System.out.println("天气灾害预警: "+body); // } // Map<String, String> header = new HashMap<>(); // header.put("Authorization", "Bearer "+token); // String s = HttpUtils.sendGZipGet(weatherNow, "location=101270101", header); // System.out.println(s); } }

浙公网安备 33010602011771号