基于Go语言实现安全的分享链接功能(AES 加密+SHA256签名+过期防重放)

 更新时间:2026年04月23日 08:31:53   作者:stark张宇  
文章描述了在多端(PC、H5、小程序、App)测评系统中,为了解决多端通信的兼容性和安全性问题,进行报告重构的过程,设计了加密串和签名机制,确保URL加密、防篡改、防重放等安全要求,需要的朋友可以参考下

问题描述

业务线有PC端、H5、小程序、App四种类型,有五种类型的测评,因为时间的关系,App和小程序要使用iframe方式嵌入的都是H5,现在要重构之前的分享报告,主要解决的问题是多端通信的兼容性和安全性。

画流程图理清思路,思路对一个程序员解决问题尤为重要,一个问题从理解,理清思路,在分层次的表达出来这个问题已经解决了一大半了,所以针对架构问题先画流程图,流程图如下:

理清思路后,下面是给AI的提示词:

我是一名Go语言后端开发工程师,现在要实现PC端 、H5 、小程序 、App 有分享测评报告的功能 ,要求是对分享的url链接进行加密  要求不能篡改 分享在加密串在答题提交后失效 生成新的加密串查看报告详情 并且兼容之前的用户校验逻辑 被分享人使用分享人的token  
需要注意的点:
1、各端 Http Header头信息 是不同的 
2、App中使用iframe方式嵌入的都是H5
3、报告信息分属于2个不同的服务项
帮我出一套程序架构的设计思路

AI巴拉巴拉给我出了一堆方案,我把符合我实际业务和需要的思路整理提取出来,进行编码和程序设计。

核心需求梳理

  1. 多端差异:PC/H5 / 小程序 / App Header 不同,App 内嵌 H5 共享一套逻辑
  2. 安全要求:URL 加密 + 防篡改,禁止篡改报告 ID、用户 ID 等核心参数
  3. 业务规则:分享签名使用一次,答题后查看报告生成新加密串
  4. 兼容要求:不破坏原有用户校验逻辑,复用分享人 Token
  5. 业务拆分:整合之前多服务痛点,需统一分享、答题、查看报告入口

设计原则

  1. 无侵入兼容:原有鉴权、业务逻辑 0 修改,仅新增分享模块
  2. 统一中间层:屏蔽多端差异,所有端共用一套分享加密 / 解密逻辑
  3. 安全闭环:加密 + 签名 + 过期 + 防重放 + 防篡改,全链路防护
  4. 服务解耦:双服务报告通过统一标识区分,不耦合业务
  5. Go 语言适配:基于 Go 原生库实现,无 heavy 依赖,高性能

核心模块详细设计

我复述一下主要的业务逻辑:

机构端PC对学生档案的学生进行分享,生成一次性加密和签名,依赖加密串和签名用户鉴权身份,进行获取答题题目和选项,提交答题,答题后回收旧签名,(如果一直没有答题,15天自动过期)生成新签名(之前测评中心在两个服务,业务代码上进行了整合、优化,统一处理),核心在于加密串和签名串的传递,加密和解密结构体的一致性,解析完传递的结构体,分析完这些思路已经很清晰了,剩下的细节和前端的同学进行联调和测试。

加密串签名逻辑:分享人生成签名 → 被分享人打开链接 → 获取题目 → 提交答题 → 后端使旧签名失效 → 生成新签名用于查看报告

1、全局配置

配置文件中要添加 AES 加密密钥、签名密钥、分享链接默认过期时间配置,这个地方非常重要,每个服务要保持一致,才能反解出加密结构体。

# 测评报告
ShareReportAuth:
  AesSecretKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx" # AES 加密密钥
  SignSecretKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 签名密钥
  ShareExpireHours: 3 # 分享链接默认过期时间(3小时)

2、设计规则

  • URL加密: 核心参数 全部进行AES 加密 ,SHA256 签名,任何修改都会验签失败
  • 携带分享人 StrId,被分享人打开直接用分享人权限和信息
  • 过期时间、随机串防重放,防止被利用
  • 参数还原,转发到具体的业务逻辑

3、生成加密串、签名

加密步骤:①拼接好业务需要的参数结构体 ②序列化成 JSON ③ 进行AES 加密 ⑤ Base64 转成可放在 URL 中的字符串(统一使用 StdEncoding),⑥生成防篡改签名,最终生成加密串(encrypted_string)和签名(sign),全局都是这一个名字,方便参数传递。

type ShareReportRequest struct {
  //业务参数略...
  ExpireAt     int64  `json:"exp,optional"`   // 过期时间戳(防永久有效)
  Nonce        string `json:"nc,optional"`    // 随机串(防重放攻击)
}

// AES 加密 + SHA256 签名
func (l *EvaluationService) SetEncryptSign(payload *ShareReportRequest) (encryptStr string, signStr string, err error) {
  expireHours := l.svcCtx.Config.ShareReportAuth.ShareExpireHours
  AesSecretKey := l.svcCtx.Config.ShareReportAuth.AesSecretKey
  SignSecretKey := l.svcCtx.Config.ShareReportAuth.SignSecretKey
  mergedPayload := &ShareReportRequest{
    //业务参数略...
    ExpireAt:     time.Now().Add(time.Duration(expireHours) * time.Hour).Unix(),
    Nonce:        utils.GenerateUniqueRandomString(16),
  }

  //2. 序列化成 JSON
  jsonBytes, err := json.Marshal(mergedPayload)
  if err != nil {
    return "", "", errors.New("JSON序列化失败")
  }
  // 3. AES 加密
  encryptBytes, err := aesEncrypt(jsonBytes, []byte(AesSecretKey))
  if err != nil {
    return "", "", errors.New("AES加密失败")
  }
  // 4. Base64 转成可放在 URL 中的字符串(统一使用 StdEncoding)
  encryptStr = base64.StdEncoding.EncodeToString(encryptBytes)
  //5. 生成防篡改签名
  signStr = generateSign(encryptStr, SignSecretKey)

  return
}

4、解密步骤

解密业务需要的结构体、需要注意的是参数保持一致,清理空字节,不然解密会失败。 解密步骤:① 对加密串和签名进行验签 ②Base64 解码(统一使用 StdEncoding)③ AES 解密 ④清理空字节 ⑤检查是否为有效JSON ⑥ 校验过期时间、校验必要字段

func (l *EvaluationService) GetReportSign(encryptStr, signStr string) (sign *ShareReportRequest, err error) {
    AesSecretKey := l.svcCtx.Config.ShareReportAuth.AesSecretKey
    SignSecretKey := l.svcCtx.Config.ShareReportAuth.SignSecretKey
    var payload ShareReportRequest

    // 先验签
    expectedSign := utils.GenerateSign(encryptStr, SignSecretKey)
    if !verifySign(encryptStr, signStr, SignSecretKey) {
       return nil, errors.New("分享链接已被篡改,验签失败")
    }

    // Base64 解码(统一使用 StdEncoding)
    var encryptBytes []byte
    encryptBytes, err = base64.StdEncoding.DecodeString(encryptStr)
    if err != nil {
       return nil, fmt.Errorf("base64解码失败: %v", err)
    }

    // 4. AES 解密
    jsonBytes, err := utils.AesDecrypt(encryptBytes, []byte(AesSecretKey))
    if err != nil {
       return nil, fmt.Errorf("解密失败: %v", err)
    }

    // 5. 清理空字节
    jsonBytes = removeNullBytes(jsonBytes)

    // 6. 检查是否为有效JSON
    if len(jsonBytes) == 0 {
       return nil, errors.New("解密后数据为空,请检查加密密钥是否正确")
    }

    if err := json.Unmarshal(jsonBytes, &payload); err != nil {
       return nil, fmt.Errorf("json解析失败: %v, 原始数据: %s", err, string(jsonBytes))
    }

    // 7. 校验过期
    if payload.ExpireAt > 0 && payload.ExpireAt < time.Now().Unix() {
       return nil, errors.New("分享链接已过期")
    }

    // 8. 校验必要字段
    if payload.StudentId == 0 || payload.CorpId == 0 {
       return nil, errors.New("分享信息不完整")
    }
    return &payload, nil
}

5、完成

最后生成的URL如下,所有的信息和参数都在参数中传递和解码完成服务间的通信。

# https://你的域名/share/{加密字符串}.{签名}

以上就是基于Go语言实现安全的分享链接功能(AES 加密+SHA256签名+过期防重放)的详细内容,更多关于Go安全的分享链接功能的资料请关注脚本之家其它相关文章!

相关文章

  • go开发中引用静态库.a文件的方法

    go开发中引用静态库.a文件的方法

    这篇文章主要介绍了go开发中引用静态库.a文件的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • 为什么不建议在go项目中使用init()

    为什么不建议在go项目中使用init()

    这篇文章主要介绍了为什么不建议在go项目中使用init(),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Go语言与gRPC的完美结合实战演练

    Go语言与gRPC的完美结合实战演练

    这篇文章主要介绍了Go语言与gRPC的完美结合实战演练,gRPC(Remote Procedure Call)是一种远程过程调用技术,通过压缩和序列化数据来优化网络通信,可以显著提高服务调用的性能和效率
    2024-01-01
  • Go中阻塞以及非阻塞操作实现(Goroutine和main Goroutine)

    Go中阻塞以及非阻塞操作实现(Goroutine和main Goroutine)

    本文主要介绍了Go中阻塞以及非阻塞操作实现(Goroutine和main Goroutine),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • golang多次读取http request body的问题分析

    golang多次读取http request body的问题分析

    这篇文章主要给大家分析了golang多次读取http request body的问题,文中通过代码示例和图文介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-01-01
  • Go语言函数学习教程

    Go语言函数学习教程

    这篇文章主要介绍了Go语言函数基本用法,结合实例形式分析了Go语言函数的格式、定义、使用方法与相关注意事项,需要的朋友可以参考下
    2016-07-07
  • Golang的strings.Split()踩坑记录

    Golang的strings.Split()踩坑记录

    工作中,当我们需要对字符串按照某个字符串切分成字符串数组数时,常用到strings.Split(),本文主要介绍了Golang的strings.Split()踩坑记录,感兴趣的可以了解一下
    2022-05-05
  • Go代码格式化gofmt的使用方法实例

    Go代码格式化gofmt的使用方法实例

    Golang制定了统一的官方代码风格,并推出gofmt工具(go fmt)来帮助开发人员格式化代码到统一的风格,下面这篇文章主要给大家介绍了关于Go代码格式化gofmt的使用方法,需要的朋友可以参考下
    2023-04-04
  • Golang连接并操作PostgreSQL数据库基本操作

    Golang连接并操作PostgreSQL数据库基本操作

    PostgreSQL是常见的免费的大型关系型数据库,具有丰富的数据类型,也是软件项目常用的数据库之一,下面这篇文章主要给大家介绍了关于Golang连接并操作PostgreSQL数据库基本操作的相关资料,需要的朋友可以参考下
    2022-09-09
  • Go语言实现单例模式的多种方法

    Go语言实现单例模式的多种方法

    单例模式是一种创建型设计模式,Go中常见实现包括使用sync.Once、双重检查锁定和原子操作法,每种方法都有其独特的优点和适用场景,下面就来具体介绍一下
    2025-07-07

最新评论