Go 开放平台 AppKey 和 AppSecret 设计指南:生成、存储与验证全流程
前言
当你的项目发展到一定阶段,总会面临一个绕不开的需求——对外开放 API 接口。 无论是为了对接合作伙伴,还是构建自己的开发者生态,你都需要一套安全、可靠的认证机制来管理第三方的访问权限。 在众多认证方案中,AppKey + AppSecret + 签名验证 是业界最经典也最广泛使用的模式。
微信开放平台、支付宝开放平台、阿里云 API 等大厂产品,几乎都采用了这套方案的变体。
我最近刚好在公司的 Go 项目中完成了开放平台的搭建工作,踩了不少坑,也积累了一些实践经验。
这篇文章会从 设计思路、生成算法、存储方案、验证流程 等维度,把整个链路掰开揉碎地讲清楚,希望能给有同样需求的朋友提供一些参考。
什么是 AppKey 和 AppSecret
在正式动手之前,先把概念对齐。
AppKey(也叫 App ID、Client ID)是分配给第三方应用的 唯一公开标识。
你可以把它类比为一个"用户名",它的作用是告诉服务端"我是谁"。
AppKey 会在每次 API 请求中明文传输,所以它本身不承担安全职责。
AppSecret(也叫 App Secret、Client Secret)是与 AppKey 配对的 私密密钥。
它类似于"密码",绝不能在网络中明文传输。
AppSecret 的核心用途是参与签名计算,用来证明"请求确实是我发的,而且没有被篡改"。
两者的关系和定位如下表所示:
| 属性 | AppKey | AppSecret |
|---|---|---|
| 可见性 | 公开,可在请求中明文传输 | 私密,仅存储在服务端和客户端本地 |
| 作用 | 标识调用方身份 | 参与签名计算,验证请求合法性 |
| 类比 | 银行卡号 | 银行卡密码 |
| 泄露风险 | 低,泄露后无法单独伪造请求 | 高,泄露后可伪造合法请求 |
打个通俗的比方:AppKey 就像你家的门牌号,所有人都能看到;AppSecret 就像你家的钥匙,只有你自己持有。
别人知道你住哪里(AppKey)不可怕,但如果钥匙(AppSecret)丢了,那问题就大了。
整体架构设计
在开始编码之前,我们先从全局视角看一下整个开放平台认证体系的架构:
┌──────────────────────────────────────────────────────────────┐
│ 第三方开发者 │
│ │
│ 1. 在开放平台注册应用 → 获取 AppKey + AppSecret │
│ 2. 使用 AppSecret 对请求参数进行签名 │
│ 3. 将 AppKey + 签名 + 时间戳 + 随机数 放入请求头或参数 │
│ │
└──────────────────┬───────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ API 网关层 │
│ │
│ 1. 提取 AppKey,查询对应的 AppSecret │
│ 2. 校验时间戳是否过期(防止重放攻击) │
│ 3. 校验随机数是否重复(防止重放攻击) │
│ 4. 用相同算法重新计算签名,对比是否一致 │
│ 5. 检查应用状态、权限、频率限制等 │
│ │
└──────────────────┬───────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 业务服务层 │
│ │
│ 认证通过后,将请求转发到具体的业务接口处理 │
│ │
└──────────────────────────────────────────────────────────────┘这个架构的核心思想是 关注点分离:认证逻辑集中在网关层处理,业务服务不需要关心调用方是谁、签名对不对,只管处理业务逻辑就好。
AppKey 和 AppSecret 的生成策略
密钥的生成看起来简单,但细节里全是魔鬼。
一个好的生成策略需要同时满足 唯一性、随机性、不可预测性 三个要求。
AppKey 的生成
AppKey 是公开标识,生成策略可以相对灵活。常见的做法有:
方案一:前缀 + UUID 截取
给 AppKey 加一个有意义的前缀,既方便人眼识别,也便于日志排查。比如:
ak_前缀表示这是一个 AppKey- 后面跟一串随机字符
最终效果类似:ak_5f3a8b2c1d4e6f7a8b9c
方案二:前缀 + 时间戳 + 随机数
把时间信息编码进去,可以粗略判断密钥的创建时间,在运维排查时会很方便。
我个人推荐方案一,理由是简单、直观、碰撞概率极低。
时间戳方案虽然多了一些信息,但也暴露了密钥的创建时间,在安全性上有轻微的信息泄露风险。
AppSecret 的生成
AppSecret 承担安全职责,生成策略必须严格得多:
- 必须使用密码学安全的随机数生成器(Go 中是
crypto/rand,千万别用math/rand) - 长度建议 32 字节以上(编码为十六进制就是 64 个字符)
- 不要包含任何可推测的信息(时间戳、用户 ID 等都不行)
为什么强调要用 crypto/rand?因为 math/rand 是伪随机数生成器,它的输出序列是可预测的。
如果攻击者知道了种子值(或者能猜到),就可以推算出所有后续生成的 AppSecret,这在安全领域是致命的。
Go 代码实现
package openapi
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
)
const (
appKeyPrefix = "ak_"
appKeyRandBytes = 16 // 生成 32 个十六进制字符
appSecretBytes = 32 // 生成 64 个十六进制字符
)
// GenerateAppKey 生成 AppKey
// 格式:ak_ + 32 位随机十六进制字符串
func GenerateAppKey() (string, error) {
bytes := make([]byte, appKeyRandBytes)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("生成 AppKey 失败: %w", err)
}
return appKeyPrefix + hex.EncodeToString(bytes), nil
}
// GenerateAppSecret 生成 AppSecret
// 64 位随机十六进制字符串,使用密码学安全随机数
func GenerateAppSecret() (string, error) {
bytes := make([]byte, appSecretBytes)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("生成 AppSecret 失败: %w", err)
}
return hex.EncodeToString(bytes), nil
}
// GenerateKeyPair 一次性生成 AppKey 和 AppSecret
func GenerateKeyPair() (appKey, appSecret string, err error) {
appKey, err = GenerateAppKey()
if err != nil {
return "", "", err
}
appSecret, err = GenerateAppSecret()
if err != nil {
return "", "", err
}
return appKey, appSecret, nil
}这段代码有几个值得注意的地方:
crypto/rand.Read会从操作系统的加密安全随机源读取数据(Linux 下是/dev/urandom),保证了随机性- 错误处理不能忽略,虽然
rand.Read在正常情况下几乎不会失败,但在极端场景下(比如系统熵池耗尽)可能出错 - AppKey 带前缀,便于在日志和数据库中快速识别
数据库表结构设计
密钥生成之后,需要持久化存储。
下面是一个经过实践验证的表结构设计:
CREATE TABLE open_app (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '主键 ID',
user_id BIGINT UNSIGNED NOT NULL COMMENT '所属用户 ID',
app_name VARCHAR(100) NOT NULL COMMENT '应用名称',
app_key VARCHAR(64) NOT NULL COMMENT 'AppKey,公开标识',
app_secret VARCHAR(255) NOT NULL COMMENT 'AppSecret,加密存储',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-正常 2-禁用 3-已删除',
permissions JSON DEFAULT NULL COMMENT '权限列表,控制可访问的 API 范围',
rate_limit INT NOT NULL DEFAULT 1000 COMMENT '每小时请求上限',
ip_whitelist TEXT DEFAULT NULL COMMENT 'IP 白名单,为空表示不限制',
expire_at DATETIME DEFAULT NULL COMMENT '过期时间,为空表示永不过期',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY uk_app_key (app_key),
INDEX idx_user_id (user_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开放平台应用表';几个设计要点说明:
app_key加唯一索引:既保证唯一性,也保证查询效率。每次 API 请求进来,第一步就是通过 AppKey 查表,这个查询必须快app_secret存加密值:明文存储 AppSecret 是不可接受的,后面会详细讲存储方案permissions用 JSON 类型:不同应用可能有不同的 API 访问权限,用 JSON 存储比较灵活rate_limit频率限制:防止某个应用把服务打爆ip_whitelistIP 白名单:生产环境建议强制配置,大幅降低密钥泄露后的风险expire_at过期时间:密钥定期轮换是安全最佳实践
密钥的安全存储方案
AppSecret 的存储是整个设计中 最容易出问题的环节。
我见过不少项目直接把 AppSecret 明文存在数据库里,这跟把家门钥匙挂在门把手上没什么区别。
为什么不能明文存储?
一旦数据库被拖库(这种事并不罕见),所有 AppSecret 就全部泄露了。
攻击者拿到 AppSecret 后可以伪造任何合法请求,而你毫不知情。
存储方案的选择
这里有两种思路,各有适用场景:
方案一:哈希存储(推荐)
用 bcrypt 或 PBKDF2 等慢哈希算法对 AppSecret 做不可逆的哈希处理后存储。这种方案的好处是即使数据库泄露,攻击者也无法还原出原始密钥。
但这意味着 AppSecret 只在创建时展示一次,后续无法再查看。如果用户忘记了,只能重新生成。微信、支付宝等大厂的开放平台都是这么做的。
方案二:加密存储
使用 AES-256 等对称加密算法加密后存储,服务端持有解密密钥。这种方案允许在需要时解密出原始 AppSecret,灵活性更高,但安全性稍逊于哈希方案——因为加密密钥本身也需要妥善保管。
我推荐方案一,理由如下:
- 更安全,密钥泄露后攻击面更小
- “只展示一次"的体验已经被市场广泛接受,用户不会觉得奇怪
- 实现更简单,不需要管理额外的加密密钥
Go 中使用 bcrypt 存储
package openapi
import (
"golang.org/x/crypto/bcrypt"
)
// HashAppSecret 对 AppSecret 做 bcrypt 哈希
func HashAppSecret(secret string) (string, error) {
// cost 参数建议 12 以上,越大越安全但越慢
hashedBytes, err := bcrypt.GenerateFromPassword([]byte(secret), 12)
if err != nil {
return "", err
}
return string(hashedBytes), nil
}
// VerifyAppSecret 校验 AppSecret 是否匹配
func VerifyAppSecret(hashedSecret, inputSecret string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedSecret), []byte(inputSecret))
return err == nil
}注意:bcrypt 的 cost 参数直接影响哈希计算的耗时。建议根据服务器性能做压测,在"安全性"和"响应速度"之间找到平衡点。一般来说,cost 设为 12 是比较合理的起点。
签名验证流程设计
有了 AppKey 和 AppSecret,接下来就是最关键的环节——签名验证。签名的本质是用 AppSecret 作为密钥,对请求内容做一次 HMAC 运算,服务端用同样的方式重新计算一遍,对比结果是否一致。
签名流程(客户端)
第三方调用 API 时,需要按照以下步骤生成签名:
第 1 步:准备签名参数
将所有参与签名的参数收集起来,通常包括:
app_key:应用标识timestamp:当前时间戳(秒级或毫秒级)nonce:一次性随机字符串(防重放)- 其他业务参数
第 2 步:参数排序与拼接
将所有参数 按照参数名的字典序(ASCII 码升序)排列,然后用 & 拼接成字符串。例如:
app_key=ak_5f3a8b2c1d4e6f7a&nonce=a1b2c3d4e5×tamp=1700000000&user_id=12345为什么要排序?因为不同编程语言、不同框架对参数的迭代顺序可能不一样。统一排序后,不管客户端用什么语言,拼出来的字符串都是一致的。
第 3 步:HMAC-SHA256 计算签名
用 AppSecret 作为密钥,对拼接好的字符串做 HMAC-SHA256 运算,得到签名值。
第 4 步:发送请求
将 app_key、timestamp、nonce、sign 放入请求头(推荐)或查询参数中,发送请求。
验证流程(服务端)
服务端收到请求后,按照以下步骤验证:
收到请求
│
▼
提取 AppKey ──→ 数据库查询应用信息 ──→ 应用不存在或已禁用?──→ 拒绝
│ │
│ 否
▼ ▼
检查时间戳 ──→ 与服务器时间差值 > 5 分钟?──→ 拒绝(防重放)
│ │
│ 否
▼ ▼
检查 nonce ──→ Redis 中已存在?──→ 拒绝(防重放)
│ │
│ 否(存入 Redis,设置过期时间)
▼ ▼
重新计算签名 ──→ 与请求中的 sign 不一致?──→ 拒绝
│ │
│ 否
▼ ▼
检查权限和频率限制 ──→ 通过 ──→ 转发到业务层这里有个值得思考的问题:既然用了 bcrypt 存储 AppSecret,服务端怎么拿到原始的 AppSecret 来计算签名?
好问题。答案是:签名验证场景下,不能用 bcrypt 方案。因为 HMAC 计算需要原始密钥,而 bcrypt 是不可逆的。
所以实际工程中,通常会 两者结合:
- 用 AES-256 加密存储 AppSecret(可解密,用于签名验证)
- 加密密钥通过环境变量或密钥管理服务(如 HashiCorp Vault)注入,不落盘
或者采用更简洁的方案:
- 直接用 HMAC 签名验证 作为唯一的认证方式
- AppSecret 加密存储 在数据库中
- 服务端每次解密后用于签名计算
Go 代码实现
下面给出核心的签名与验证实现,你可以根据自己的项目结构做调整。
签名工具
package openapi
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"sort"
"strings"
"time"
)
// SignParams 对参数进行签名
// params: 需要参与签名的所有参数(不包含 sign 本身)
// secret: AppSecret
func SignParams(params map[string]string, secret string) string {
// 1. 按 key 字典序排序
keys := make([]string, 0, len(params))
for k := range params {
if k == "sign" {
continue // sign 本身不参与签名
}
keys = append(keys, k)
}
sort.Strings(keys)
// 2. 拼接成 key=value&key=value 格式
var builder strings.Builder
for i, k := range keys {
if i > 0 {
builder.WriteString("&")
}
builder.WriteString(fmt.Sprintf("%s=%s", k, params[k]))
}
// 3. HMAC-SHA256 计算签名
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(builder.String()))
return hex.EncodeToString(mac.Sum(nil))
}
// VerifySign 验证签名是否合法
func VerifySign(params map[string]string, secret, sign string) bool {
expectedSign := SignParams(params, secret)
// 使用 hmac.Equal 防止时序攻击
return hmac.Equal([]byte(expectedSign), []byte(sign))
}认证中间件
package middleware
import (
"net/http"
"strconv"
"time"
"your-project/openapi"
"your-project/service"
"github.com/gin-gonic/gin"
)
const (
maxTimestampDiff = 5 * time.Minute // 时间戳最大允许偏差
)
// OpenAPIAuth 开放接口认证中间件
func OpenAPIAuth(appService service.AppService) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 提取认证参数
appKey := c.GetHeader("X-App-Key")
timestamp := c.GetHeader("X-Timestamp")
nonce := c.GetHeader("X-Nonce")
sign := c.GetHeader("X-Sign")
if appKey == "" || timestamp == "" || nonce == "" || sign == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "缺少认证参数",
})
c.Abort()
return
}
// 2. 校验时间戳
ts, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 400,
"message": "时间戳格式错误",
})
c.Abort()
return
}
requestTime := time.Unix(ts, 0)
if time.Since(requestTime).Abs() > maxTimestampDiff {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "请求已过期",
})
c.Abort()
return
}
// 3. 检查 nonce 是否重复(需要 Redis 支持)
if appService.IsNonceUsed(c, nonce) {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "重复请求",
})
c.Abort()
return
}
// 4. 查询应用信息
app, err := appService.GetByAppKey(c, appKey)
if err != nil || app.Status != 1 {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "应用不存在或已被禁用",
})
c.Abort()
return
}
// 5. 验证签名
params := map[string]string{
"app_key": appKey,
"timestamp": timestamp,
"nonce": nonce,
}
// 将查询参数也加入签名计算
for k, v := range c.Request.URL.Query() {
if len(v) > 0 {
params[k] = v[0]
}
}
appSecret := appService.DecryptSecret(app.AppSecret)
if !openapi.VerifySign(params, appSecret, sign) {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "签名验证失败",
})
c.Abort()
return
}
// 6. 记录 nonce,防止重放
appService.MarkNonceUsed(c, nonce, maxTimestampDiff)
// 7. 将应用信息存入上下文,方便后续使用
c.Set("open_app", app)
c.Next()
}
}密钥加解密工具
package openapi
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"os"
)
// getEncryptionKey 从环境变量获取加密密钥
// 生产环境建议使用密钥管理服务(如 Vault)
func getEncryptionKey() []byte {
key := os.Getenv("APP_SECRET_ENCRYPT_KEY")
if len(key) != 32 {
panic("APP_SECRET_ENCRYPT_KEY 必须为 32 字节")
}
return []byte(key)
}
// EncryptSecret 使用 AES-256-GCM 加密 AppSecret
func EncryptSecret(plaintext string) (string, error) {
block, err := aes.NewCipher(getEncryptionKey())
if err != nil {
return "", fmt.Errorf("创建 AES cipher 失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("创建 GCM 失败: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", fmt.Errorf("生成 nonce 失败: %w", err)
}
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// DecryptSecret 解密 AppSecret
func DecryptSecret(encrypted string) (string, error) {
data, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil {
return "", fmt.Errorf("base64 解码失败: %w", err)
}
block, err := aes.NewCipher(getEncryptionKey())
if err != nil {
return "", fmt.Errorf("创建 AES cipher 失败: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return "", fmt.Errorf("创建 GCM 失败: %w", err)
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return "", fmt.Errorf("密文数据异常")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return "", fmt.Errorf("解密失败: %w", err)
}
return string(plaintext), nil
}安全防护策略
光有签名验证还不够,一个生产级的开放平台需要多层防护。
以下是我在实际项目中用到的几个关键策略:
1. 时间戳校验——防重放攻击
在请求中加入当前时间戳,服务端校验时间戳与当前时间的差值是否在可接受范围内(通常 5 分钟)。超过范围的请求直接拒绝。
这能有效阻止攻击者截获一个合法请求后反复发送。即使请求被截获,5 分钟后就失效了。
2. Nonce 随机数——防精确重放
仅靠时间戳还不够——5 分钟内的请求依然可以被重放。所以还需要一个 nonce(一次性随机字符串)。服务端用 Redis 记录已使用的 nonce,发现重复就拒绝。
nonce 的过期时间要 大于等于时间戳的允许偏差(比如也设 5 分钟),这样才能完全杜绝重放。
3. IP 白名单
生产环境 强烈建议 开启 IP 白名单。即使 AppSecret 泄露了,攻击者如果不在白名单内,也无法发起请求。这是最简单也最有效的纵深防御措施。
4. 频率限制
为每个应用设置请求频率上限,防止:
- 恶意调用方发起 DDoS 攻击
- 某个应用的 Bug 导致疯狂重试
- 资源被少数调用方独占
常见做法是使用 Redis 的滑动窗口或令牌桶算法实现。
5. 密钥定期轮换
AppSecret 不应该一用就是一辈子。建议:
- 支持生成新的 AppSecret(旧的在过渡期内同时有效)
- 设置最长有效期,到期后强制轮换
- 提供 AppSecret 重置功能
6. 敏感操作二次确认
对于重置 AppSecret、删除应用这类高危操作,建议加入短信验证码或邮件确认环节,防止账号被盗后密钥遭到恶意篡改。
常见问题
Q1:AppKey 和 AppSecret 有什么区别?为什么需要两个?
AppKey 是公开的身份标识,用于告诉服务端"我是哪个应用”。AppSecret 是私密密钥,用于签名计算以证明"这个请求确实来自我"。之所以需要两个,是因为 AppKey 需要在网络中传输(用来查找对应的密钥),如果只用一个密钥,它就不得不在网络中明文暴露,任何人截获后都能伪造请求。
Q2:为什么不直接在请求中传递 AppSecret?
如果 AppSecret 在请求中明文传输,一旦网络被嗅探或日志被泄露,攻击者就能直接拿到 AppSecret。使用签名机制后,即使请求被截获,攻击者也拿不到 AppSecret,无法伪造新的请求。
Q3:HMAC-SHA256 和 MD5 签名哪个更好?
强烈推荐 HMAC-SHA256。MD5 已经被证明存在碰撞漏洞,在安全敏感场景下不应再使用。HMAC-SHA256 在性能和安全性之间取得了很好的平衡,也是目前业界的主流选择。
Q4:时间戳允许偏差设多大合适?
一般设置 5 分钟 是比较合理的。太短的话,客户端和服务端的时钟偏差可能导致正常请求被拒绝;太长的话,重放攻击的窗口期就会变大。5 分钟是一个经过大量实践验证的折中值。
Q5:AppSecret 泄露了怎么办?
发现泄露后应立即:
- 在开放平台管理后台 重置 AppSecret,原密钥即时失效
- 排查泄露原因(代码仓库、日志、聊天记录等)
- 审计泄露期间的 API 调用日志,确认是否有异常操作
- 通知受影响的用户
这也是为什么我们建议支持"一键重置"功能的原因。
Q6:这套方案和 OAuth 2.0 有什么区别?适用场景是什么?
AppKey + AppSecret + 签名验证 更适合 服务端对服务端 的调用场景(也叫 S2S 或 Server-to-Server)。
OAuth 2.0 更适合涉及用户授权的场景(比如"用微信登录")。
如果你的开放平台只是提供数据接口给合作方的服务器调用,AppKey + AppSecret 方案就完全够用,而且比 OAuth 2.0 实现起来简单得多。
总结
回顾一下这篇文章的核心内容:
- AppKey 是公开标识,AppSecret 是私密密钥,两者配合完成身份认证和请求防篡改
- 密钥生成必须使用密码学安全随机数(Go 中用
crypto/rand),不要用math/rand - AppSecret 务必加密存储,推荐 AES-256-GCM 加密,加密密钥通过环境变量或密钥管理服务注入
- 签名验证采用 HMAC-SHA256,参数按字典序排列后拼接再签名
- 多层防护缺一不可:时间戳校验、nonce 防重放、IP 白名单、频率限制
- 支持密钥轮换和一键重置,为安全事件做好预案
最终极简总结(你一定要记住)
-
数据库存哈希→ 防平台被拖库,密钥不泄露
-
不能把 AppSecret 放前端→ 防你自己把密钥泄露
-
HTTPS 传输→ 防网络中间被抓包
这三件事各防各的,互不替代。
开放平台的安全不是一蹴而就的事,上面这套方案能覆盖绝大多数场景。
但随着业务的发展,你可能还需要考虑更细粒度的权限控制、审计日志、SDK 封装等工作。
建议在项目初期就把架构搭好,后续扩展会轻松很多。
如果大家对 AppKey 和 AppSecret 的设计还有哪些疑问,或者在实际项目中遇到了什么特殊场景不知道怎么处理,欢迎大家在评论区交流讨论~也可以分享一下你们项目中是怎么做开放接口认证的,互相学习 🍻
版权声明
未经授权,禁止转载本文章。
如需转载请保留原文链接并注明出处。即视为默认获得授权。
未保留原文链接未注明出处或删除链接将视为侵权,必追究法律责任!
本文原文链接: https://fiveyoboy.com/articles/go-open-platform-appkey-appsecret-design-guide/
备用原文链接: https://blog.fiveyoboy.com/articles/go-open-platform-appkey-appsecret-design-guide/