feat: adjust api send token

This commit is contained in:
engigu
2025-10-08 13:16:27 +08:00
parent ac91292d21
commit 651a596a02
3 changed files with 108 additions and 11 deletions
+54
View File
@@ -0,0 +1,54 @@
package util
import (
"encoding/hex"
"errors"
)
//// getDeterministicSalt 根据字符串内容生成确定性 salt(范围 0~255)
//func getDeterministicSalt(text string) byte {
// var sum int
// for i := 0; i < len(text); i++ {
// sum = (sum + int(text[i])*(i+1)) & 0xFF
// }
// return byte(sum)
//}
//// EncryptTokenHex 按照确定性 salt + 异或规则生成十六进制 token
//// 规则:首字节为 salt;其余字节为 char ^ key ^ ((salt + idx) & 0xFF)
//func EncryptTokenHex(text string, key byte) string {
// salt := getDeterministicSalt(text)
// // 预分配:2个hex字符的salt + 每个字符2个hex
// out := make([]byte, 0, 2+len(text)*2)
// // 写入salt
// out = append(out, []byte(hex.EncodeToString([]byte{salt}))...)
// // 写入加密数据
// for i := 0; i < len(text); i++ {
// b := text[i] ^ key ^ byte((int(salt)+i)&0xFF)
// enc := make([]byte, 2)
// hex.Encode(enc, []byte{b})
// out = append(out, enc...)
// }
// return string(out)
//}
// DecryptTokenHex 解析十六进制 token 为原始字符串
// 规则:首字节为salt;后续字节与 key ^ ((salt + idx) & 0xFF) 异或
func DecryptTokenHex(enc string, key byte) (string, error) {
if enc == "" {
return "", errors.New("empty token")
}
bs, err := hex.DecodeString(enc)
if err != nil {
return "", err
}
if len(bs) < 1 {
return "", errors.New("invalid token length")
}
salt := bs[0]
out := make([]byte, len(bs)-1)
for i := 1; i < len(bs); i++ {
out[i-1] = bs[i] ^ key ^ byte((int(salt)+i-1)&0xFF)
}
return string(out), nil
}
+32 -10
View File
@@ -2,16 +2,21 @@ package v1
import ( import (
"fmt" "fmt"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"message-nest/pkg/app" "message-nest/pkg/app"
"message-nest/pkg/e" "message-nest/pkg/e"
utilpkg "message-nest/pkg/util"
"message-nest/service/send_message_service" "message-nest/service/send_message_service"
"net/http" "net/http"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
) )
type SendMessageReq struct { type SendMessageReq struct {
TaskID string `json:"task_id" validate:"required,len=12" label:"任务id"` // 兼容旧参数:task_id;不再必填
TaskID string `json:"task_id" validate:"omitempty,len=12" label:"任务id"`
// 新参数:token(与task_id互斥,优先使用task_id
Token string `json:"token" label:"任务token"`
Text string `json:"text" validate:"required" label:"文本内容"` Text string `json:"text" validate:"required" label:"文本内容"`
Title string `json:"title" label:"消息标题"` Title string `json:"title" label:"消息标题"`
HTML string `json:"html" label:"html内容"` HTML string `json:"html" label:"html内容"`
@@ -33,14 +38,31 @@ func DoSendMassage(c *gin.Context) {
return return
} }
// 解析token为task_id(如提供)
taskID := req.TaskID
// 如果使用了token就使用token解析出task_id
if req.Token != "" {
dec, err := utilpkg.DecryptTokenHex(req.Token, 71) // 71 为简单对称密钥
if err != nil {
appG.CResponse(http.StatusBadRequest, fmt.Sprintf("token解析失败:%v", err), nil)
return
}
taskID = dec
}
if taskID == "" {
appG.CResponse(http.StatusBadRequest, "参数缺失:token 或 task_id 必须提供其一", nil)
return
}
msgService := send_message_service.SendMessageService{ msgService := send_message_service.SendMessageService{
TaskID: req.TaskID, TaskID: taskID,
Title: req.Title, Title: req.Title,
Text: req.Text, Text: req.Text,
HTML: req.HTML, HTML: req.HTML,
URL: req.URL, URL: req.URL,
MarkDown: req.MarkDown, MarkDown: req.MarkDown,
CallerIp: c.ClientIP(), CallerIp: c.ClientIP(),
DefaultLogger: logrus.WithFields(logrus.Fields{ DefaultLogger: logrus.WithFields(logrus.Fields{
//"prefix": "[Message Instance]", //"prefix": "[Message Instance]",
}), }),
+22 -1
View File
@@ -6,8 +6,29 @@ const gethttpOrigin = () => {
class ApiStrGenerate { class ApiStrGenerate {
// 根据字符串内容生成确定性 salt(范围 0~255)
static getDeterministicSalt(text) {
let sum = 0;
for (let i = 0; i < text.length; i++) {
sum = (sum + text.charCodeAt(i) * (i + 1)) & 0xFF;
}
return sum;
}
// 加密:首字节为salt,后续为按位异或后的数据
static encryptHex(text, key) {
const salt = ApiStrGenerate.getDeterministicSalt(text);
let result = salt.toString(16).padStart(2, '0');
for (let i = 0; i < text.length; i++) {
const code = text.charCodeAt(i) ^ (key & 0xFF) ^ ((salt + i) & 0xFF);
result += code.toString(16).padStart(2, '0');
}
return result;
}
static getDataString(task_id, options) { static getDataString(task_id, options) {
let data = { task_id: task_id }; // 新版仅展示 token;兼容旧版 task_id(后端依然支持)
let data = { token: ApiStrGenerate.encryptHex(task_id, 71) };
data.title = 'message title'; data.title = 'message title';
data.text = 'Hello World!'; data.text = 'Hello World!';
if (options.html) { if (options.html) {