83 lines
2.0 KiB
Go
83 lines
2.0 KiB
Go
package security
|
||
|
||
import (
|
||
"crypto/hmac"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"fmt"
|
||
"io"
|
||
"math"
|
||
"net/url"
|
||
"strconv"
|
||
"time"
|
||
)
|
||
|
||
// https://oapi.dingtalk.com/robot/send?access_token=xxx
|
||
const dingTalkOAPI = "oapi.dingtalk.com"
|
||
|
||
var dingTalkURL url.URL = url.URL{
|
||
Scheme: "https",
|
||
Host: dingTalkOAPI,
|
||
Path: "robot/send",
|
||
}
|
||
|
||
// URL get DingTalk URL with accessToken & secret
|
||
// If no signature is set, the secret is set to ""
|
||
// 如果没有加签,secret 设置为 "" 即可
|
||
func URL(accessToken string, secret string) (string, error) {
|
||
timestamp := strconv.FormatInt(time.Now().Unix()*1000, 10)
|
||
return URLWithTimestamp(timestamp, accessToken, secret)
|
||
}
|
||
|
||
// URLWithTimestamp get DingTalk URL with timestamp & accessToken & secret
|
||
func URLWithTimestamp(timestamp string, accessToken string, secret string) (string, error) {
|
||
dtu := dingTalkURL
|
||
value := url.Values{}
|
||
value.Set("access_token", accessToken)
|
||
|
||
if secret == "" {
|
||
dtu.RawQuery = value.Encode()
|
||
return dtu.String(), nil
|
||
}
|
||
|
||
sign, err := sign(timestamp, secret)
|
||
if err != nil {
|
||
dtu.RawQuery = value.Encode()
|
||
return dtu.String(), err
|
||
}
|
||
|
||
value.Set("timestamp", timestamp)
|
||
value.Set("sign", sign)
|
||
dtu.RawQuery = value.Encode()
|
||
return dtu.String(), nil
|
||
}
|
||
|
||
// Validate validate
|
||
// https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p
|
||
func Validate(signStr, timestamp, secret string) (bool, error) {
|
||
t, err := strconv.ParseInt(timestamp, 10, 64)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
timeGap := time.Since(time.Unix(t, 0))
|
||
if math.Abs(timeGap.Hours()) > 1 {
|
||
return false, fmt.Errorf("specified timestamp is expired")
|
||
}
|
||
|
||
ourSign, err := sign(timestamp, secret)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return ourSign == signStr, nil
|
||
}
|
||
|
||
func sign(timestamp string, secret string) (string, error) {
|
||
stringToSign := fmt.Sprintf("%s\n%s", timestamp, secret)
|
||
h := hmac.New(sha256.New, []byte(secret))
|
||
if _, err := io.WriteString(h, stringToSign); err != nil {
|
||
return "", err
|
||
}
|
||
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
||
}
|