news 2026/4/23 15:44:39

从零到一:手把手教你用Ed25519密钥对构建和风天气JWT认证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零到一:手把手教你用Ed25519密钥对构建和风天气JWT认证

从零构建Ed25519密钥对实现和风天气JWT认证全流程指南

1. 为什么选择JWT认证替代传统API Key

在当今的API安全领域,JSON Web Token(JWT)正逐渐成为身份认证的主流方案。与传统的API Key相比,JWT提供了更高级别的安全性保障,特别适合需要精细控制访问权限的场景。

传统API Key的主要问题在于:

  • 长期有效:一旦泄露,攻击者可以无限期使用
  • 权限单一:无法区分不同操作或数据范围的权限
  • 缺乏时效性:无法自动失效,必须手动撤销

JWT认证的核心优势体现在:

  • 时效控制:通过exp字段设置精确的过期时间
  • 数字签名:使用Ed25519算法确保令牌无法伪造
  • 最小权限:可针对不同场景生成具有特定权限的令牌
# 传统API Key调用示例(不推荐) curl -H "X-QW-Api-Key: ABCD1234EFGH" \ 'https://api.qweather.com/v7/weather/now?location=101010100' # JWT认证调用示例(推荐) curl -H "Authorization: Bearer eyJhbGciOiJFZERTQS..." \ 'https://api.qweather.com/v7/weather/now?location=101010100'

2. Ed25519密钥对的生成与管理

Ed25519是目前最先进的椭圆曲线数字签名算法之一,相比传统RSA具有以下优势:

特性Ed25519RSA-2048
签名速度快3-4倍基准值
验证速度快5-6倍基准值
密钥长度256位2048位
安全性128位112位

生成密钥对的具体步骤

  1. 确保系统已安装OpenSSL 3.0+版本

    # 检查OpenSSL版本 openssl version
  2. 生成Ed25519私钥

    openssl genpkey -algorithm ED25519 -out private.pem
  3. 从私钥导出公钥

    openssl pkey -pubout -in private.pem -out public.pem

重要提示:私钥文件(private.pem)必须严格保密,建议设置400权限并存储在安全位置。公钥文件(public.pem)需要上传到和风天气控制台。

常见问题排查

  • 如果遇到"Algorithm ED25519 not found"错误,说明OpenSSL版本过低
  • Windows用户可通过winget安装最新版:winget install OpenSSL.OpenSSL

3. 和风天气控制台配置

完成密钥生成后,需要将公钥配置到和风天气控制台:

  1. 登录控制台进入"项目管理"
  2. 选择目标项目点击"添加凭据"
  3. 选择"JSON Web Token"认证方式
  4. 复制public.pem的全部内容到公钥文本框
  5. 设置有意义的凭据名称(如"生产环境API")

配置完成后,系统会生成一个凭据ID(kid),这个ID需要记录并在后续生成JWT时使用。

验证公钥是否匹配的方法

# 计算本地公钥SHA256 openssl dgst -sha256 public.pem # 与控制台显示的SHA256值比对

4. JWT的组成与生成原理

一个标准的JWT由三部分组成,通过点号(.)连接:header.payload.signature

4.1 Header部分

必须包含算法类型和凭据ID:

{ "alg": "EdDSA", "kid": "T8GYP76CD2" }

4.2 Payload部分

核心字段包括:

  • sub: 项目ID(控制台获取)
  • iat: 签发时间(建议设置为当前时间-30秒)
  • exp: 过期时间(最长不超过24小时)

示例Payload:

{ "sub": "29KVA6G27T", "iat": 1703912400, "exp": 1703916000 }

4.3 Signature生成

签名是JWT安全性的核心,生成步骤:

  1. 对Header和Payload分别进行Base64URL编码
  2. 用点号连接两个编码结果
  3. 使用Ed25519私钥对连接后的字符串签名
  4. 对签名结果进行Base64URL编码

Base64URL与标准Base64的区别:替换+/-_并去掉末尾的=

5. 多语言实现示例

Python实现

import jwt import time private_key = """-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIMt6oO4GC+QnzZFEp/Q245fpquD+j5wKApSJaY2MHFuJ -----END PRIVATE KEY-----""" headers = {"alg": "EdDSA", "kid": "T8GYP76CD2"} payload = { "sub": "29KVA6G27T", "iat": int(time.time()) - 30, "exp": int(time.time()) + 3600 } token = jwt.encode(payload, private_key, algorithm="EdDSA", headers=headers) print(f"JWT: {token}")

Node.js实现

const { SignJWT, importPKCS8 } = require('jose'); async function generateJWT() { const privateKey = await importPKCS8( `-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIMt6oO4GC+QnzZFEp/Q245fpquD+j5wKApSJaY2MHFuJ -----END PRIVATE KEY-----`, 'EdDSA' ); const jwt = await new SignJWT({ sub: '29KVA6G27T' }) .setProtectedHeader({ alg: 'EdDSA', kid: 'T8GYP76CD2' }) .setIssuedAt(Math.floor(Date.now() / 1000) - 30) .setExpirationTime('1h') .sign(privateKey); console.log(`JWT: ${jwt}`); }

Java实现

import java.security.*; import java.util.*; import io.jsonwebtoken.*; public class JwtGenerator { public static void main(String[] args) { String privateKey = "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIMt6oO4GC+QnzZFEp/Q245fpquD+j5wKApSJaY2MHFuJ\n" + "-----END PRIVATE KEY-----"; long iat = System.currentTimeMillis() / 1000 - 30; long exp = iat + 3600; String jwt = Jwts.builder() .setHeaderParam("alg", "EdDSA") .setHeaderParam("kid", "T8GYP76CD2") .claim("sub", "29KVA6G27T") .claim("iat", iat) .claim("exp", exp) .signWith(Keys.hmacShaKeyFor(privateKey.getBytes())) .compact(); System.out.println("JWT: " + jwt); } }

6. 实战:调用天气API完整流程

6.1 获取城市Location ID

import requests api_host = "https://api.qweather.com" endpoint = "/geo/v2/city/lookup" params = {"location": "北京"} response = requests.get(f"{api_host}{endpoint}", headers={"Authorization": f"Bearer {token}"}, params=params) location_id = response.json()["location"][0]["id"]

6.2 查询实时天气

endpoint = "/v7/weather/now" params = { "location": location_id, "lang": "zh", "unit": "m" } response = requests.get(f"{api_host}{endpoint}", headers={"Authorization": f"Bearer {token}"}, params=params) weather_data = response.json() print(f"当前温度: {weather_data['now']['temp']}°C")

6.3 处理API响应

典型成功响应:

{ "code": "200", "updateTime": "2025-07-17T17:18+08:00", "now": { "temp": "35", "feelsLike": "36", "text": "晴", "windDir": "东北风", "windScale": "2" } }

常见错误代码:

  • 401: JWT认证失败(检查过期时间/kid/sub)
  • 404: 接口路径错误(检查API Host和endpoint)
  • 429: 请求频率超限(检查调用频率)

7. 高级技巧与最佳实践

JWT缓存策略

  • 服务端应用:缓存时间设为exp-iat的80%
  • 客户端应用:每次请求生成新JWT

密钥轮换方案

  1. 生成新密钥对(new_private.pem, new_public.pem)
  2. 上传新公钥到控制台获得新kid
  3. 逐步将应用迁移到新kid
  4. 确认无旧JWT使用后删除旧凭据

性能优化建议

# 使用会话保持连接池 session = requests.Session() session.headers.update({"Authorization": f"Bearer {token}"}) # 启用gzip压缩 session.headers.update({"Accept-Encoding": "gzip"})

安全防护措施

  • 限制JWT有效期(前端建议15分钟,后端可设1小时)
  • 监控异常JWT使用模式
  • 定期轮换Ed25519密钥对(建议每3-6个月)

在实际项目中,我曾遇到时区问题导致JWT立即过期的情况。解决方案是在生成iat和exp时明确指定时区,确保服务器和客户端使用相同的时区标准。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:19:12

Flowise案例解析:非技术人员创建AI应用全过程

Flowise案例解析:非技术人员创建AI应用全过程 1. 为什么Flowise是普通人踏入AI世界的“第一块踏板” 你有没有过这样的时刻:看到别人用AI自动整理会议纪要、把公司文档变成随时可问的智能助手、甚至让Excel表格自己写分析报告——心里痒痒,…

作者头像 李华
网站建设 2026/4/23 10:45:30

Qwen3-ASR-0.6B语音识别:5分钟快速搭建多语言转写工具

Qwen3-ASR-0.6B语音识别:5分钟快速搭建多语言转写工具 1. 为什么你需要一个“开箱即用”的语音转写工具? 你有没有遇到过这些场景: 开完一场两小时的线上会议,却要花一整个下午手动整理会议纪要?收到客户发来的方言…

作者头像 李华
网站建设 2026/4/23 12:14:07

革新性Mac软件管理:Applite重新定义高效工具体验

革新性Mac软件管理:Applite重新定义高效工具体验 【免费下载链接】Applite User-friendly GUI macOS application for Homebrew Casks 项目地址: https://gitcode.com/gh_mirrors/ap/Applite 在数字化工作流中,Mac软件管理往往成为效率瓶颈——繁…

作者头像 李华