feat: adjust api send token
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user