| "encoding/xml" | "encoding/xml" | ||||
| ) | ) | ||||
| //EncryptedMsg encrypted message from Wechat | |||||
| type EncryptedMsg struct { | |||||
| ToUserName string | |||||
| Encrypt string | |||||
| } | |||||
| //all xml message has these headers | //all xml message has these headers | ||||
| type CommonHeader struct { | type CommonHeader struct { | ||||
| ToUserName string | ToUserName string | ||||
| xml.Unmarshal([]byte(s), &r) | xml.Unmarshal([]byte(s), &r) | ||||
| return r | return r | ||||
| } | } | ||||
| //ReadEncryptedMsg get Encrypted Msg | |||||
| func ReadEncryptedMsg(s string) EncryptedMsg { | |||||
| var r = EncryptedMsg{} | |||||
| xml.Unmarshal([]byte(s), &r) | |||||
| return r | |||||
| } |
| AssertEqual(t, m.Url, "url", "Url failed") | AssertEqual(t, m.Url, "url", "Url failed") | ||||
| AssertEqual(t, m.MsgId, int64(1234567890123456), "MsgId not match") | AssertEqual(t, m.MsgId, int64(1234567890123456), "MsgId not match") | ||||
| } | } | ||||
| func TestReadEncryptMsg(t *testing.T) { | |||||
| msg := `<xml> | |||||
| <ToUserName><![CDATA[gh_f09231355c68]]></ToUserName> | |||||
| <Encrypt><![CDATA[Dv3epMMhmmGU1o6lg71IfbpRrOYX1S8oZX3nwW0uBAHHMKx62T4KniS4efuf8fNHWf6gsF/YGaDraF6HhGOdKp8vbzluiIEsCnIveKN1pO+IUDOBBxzPAzQSFSYJ3OwVXWmBdBcC1S5guQrOxLysH+6UIWSor9cEef+94UAKTNw/MLB0zPfqK5TVoN1A0yobmP9OU8wtFJP0L1aKySPFGGbqBMfJkStRTrYLjIQfZ7pAIisB/g3c87w26r7LUz9hVh4ey3/T6cjQ8vKvgNKL3j8y4IwUdmnmTPrrdOsyA1pz69977xKHFtIptZYHKGD9dTW6PyPcKKTP6iOod6Agb8TI+is80auqHkjvUyvT/xPG8fxak/wI9BKzKndAnwxlcDG/8WElkHVl0TwxpsCb48ZxLEf4GFKaYaliC9xBVweKLNnqdbBmzwfe7GBNWC61h7KQYqwtZqMkZs3BBsStcQ==]]></Encrypt> | |||||
| </xml>` | |||||
| m := ReadEncryptedMsg(msg) | |||||
| AssertEqual(t, m.ToUserName, "gh_f09231355c68", "ToUserName failed") | |||||
| AssertEqual(t, m.Encrypt, "Dv3epMMhmmGU1o6lg71IfbpRrOYX1S8oZX3nwW0uBAHHMKx62T4KniS4efuf8fNHWf6gsF/YGaDraF6HhGOdKp8vbzluiIEsCnIveKN1pO+IUDOBBxzPAzQSFSYJ3OwVXWmBdBcC1S5guQrOxLysH+6UIWSor9cEef+94UAKTNw/MLB0zPfqK5TVoN1A0yobmP9OU8wtFJP0L1aKySPFGGbqBMfJkStRTrYLjIQfZ7pAIisB/g3c87w26r7LUz9hVh4ey3/T6cjQ8vKvgNKL3j8y4IwUdmnmTPrrdOsyA1pz69977xKHFtIptZYHKGD9dTW6PyPcKKTP6iOod6Agb8TI+is80auqHkjvUyvT/xPG8fxak/wI9BKzKndAnwxlcDG/8WElkHVl0TwxpsCb48ZxLEf4GFKaYaliC9xBVweKLNnqdbBmzwfe7GBNWC61h7KQYqwtZqMkZs3BBsStcQ==", "ToUserName failed") | |||||
| } |
| import ( | import ( | ||||
| "crypto/sha1" | "crypto/sha1" | ||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | |||||
| "log" | "log" | ||||
| "net/http" | "net/http" | ||||
| "net/http/httputil" | "net/http/httputil" | ||||
| case "GET": | case "GET": | ||||
| answerInitialAuth(w, r) | answerInitialAuth(w, r) | ||||
| default: | default: | ||||
| log.Fatalln("Unhandled HTTP %s", r.Method) | |||||
| log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method)) | |||||
| fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") | fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") | ||||
| } | } | ||||
| } | } | ||||
| //answerWechatPost distribute PostRequest according to xml body info | //answerWechatPost distribute PostRequest according to xml body info | ||||
| // | // | ||||
| func answerWechatPost(w http.ResponseWriter, r *http.Request) { | func answerWechatPost(w http.ResponseWriter, r *http.Request) { | ||||
| body, _ := ioutil.ReadAll(r.Body) | |||||
| fmt.Printf("get body: %s", string(body)) | |||||
| s := ReadEncryptedMsg(string(body)) | |||||
| //fmt.Printf("to decrypt %s", s.Encrypt) | |||||
| d := Decode(s.Encrypt) | |||||
| fmt.Printf("decrypt as: %s", d) | |||||
| h := ReadCommonHeader(d) | |||||
| reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) | |||||
| fmt.Fprint(w, reply) | |||||
| return | return | ||||
| } | } | ||||
| package main | |||||
| import ( | |||||
| "crypto/sha1" | |||||
| "fmt" | |||||
| "math/rand" | |||||
| "sort" | |||||
| "strings" | |||||
| "time" | |||||
| ) | |||||
| //BuildTextMsg Given a text message send it to wechat client | |||||
| func BuildTextMsg(txt string, ToUserName string) (string, error) { | |||||
| template := `<xml> | |||||
| <ToUserName><![CDATA[%s]]></ToUserName> | |||||
| <FromUserName><![CDATA[%s]]></FromUserName> | |||||
| <CreateTime>%d</CreateTime> | |||||
| <MsgType><![CDATA[text]]></MsgType> | |||||
| <Content><![CDATA[%s]]></Content> | |||||
| </xml>` | |||||
| msg := fmt.Sprintf(template, ToUserName, "gh_f09231355c68", int32(time.Now().Unix()), txt) | |||||
| fmt.Println(msg) | |||||
| e := Encode(msg) | |||||
| str, _, _, _ := signMsg(e) | |||||
| fmt.Println(str) | |||||
| return str, nil | |||||
| } | |||||
| func signMsg(content string) (xml string, timestamp int32, nonce int32, signature string) { | |||||
| timestamp = int32(time.Now().Unix()) | |||||
| nonce = rand.Int31() | |||||
| strTimestamp := fmt.Sprintf("%d", timestamp) | |||||
| strNonce := fmt.Sprintf("%d", nonce) | |||||
| signature = getSignature(APIConfig.Token, strTimestamp, strNonce, content) | |||||
| xml = "<xml>" + | |||||
| "<Encrypt>" + content + "</Encrypt>" + | |||||
| "<MsgSignature>" + signature + "</MsgSignature>" + | |||||
| "<TimeStamp>" + strTimestamp + "</TimeStamp>" + | |||||
| "<Nonce>" + strNonce + "</Nonce>" + | |||||
| "</xml>" | |||||
| return | |||||
| } | |||||
| func getSignature(token string, timestamp string, nonce string, content string) (signature string) { | |||||
| strs := []string{token, timestamp, nonce, content} | |||||
| sort.Strings(strs) | |||||
| s := strings.Join(strs, "") | |||||
| h := sha1.New() | |||||
| h.Write([]byte(s)) | |||||
| signature = fmt.Sprintf("%x", h.Sum(nil)) | |||||
| return | |||||
| } |
| package main | |||||
| import "testing" | |||||
| func TestBuildTxtMsg(t *testing.T) { | |||||
| BuildTextMsg("你好", "oUN420bxqFqlx0ZQHciUOesZO3PE") | |||||
| } | |||||
| func TextCalculateSignature (t *testing.T ){ | |||||
| } |