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
|
|||
|
}
|