// AESResponse 回复aes加密的消息给微信服务器. // msg: 经过 encoding/xml.Marshal 得到的结果符合微信消息格式的任何数据结构 // timestamp: 时间戳, 如果为 0 则默认使用 Context.Timestamp // nonce: 随机数, 如果为 "" 则默认使用 Context.Nonce // random: 16字节的随机字符串, 如果为 nil 则默认使用 Context.Random func (ctx *Context) AESResponse(msg interface{}, timestamp int64, nonce string, random []byte) (err error) { if timestamp == 0 { timestamp = ctx.Timestamp } if nonce == "" { nonce = ctx.Nonce } if len(random) == 0 { random = ctx.Random } msgPlaintext, err := callback.XmlMarshalResponseMessage(msg) if err != nil { return } encryptedMsg := util.AESEncryptMsg(random, msgPlaintext, ctx.AppId, ctx.AESKey) base64EncryptedMsg := base64.StdEncoding.EncodeToString(encryptedMsg) timestampString := strconv.FormatInt(timestamp, 10) msgSignature := util.MsgSign(ctx.Token, timestampString, nonce, base64EncryptedMsg) if sw, ok := ctx.ResponseWriter.(stringWriter); ok { if _, err = sw.WriteString("<xml><Encrypt>"); err != nil { return } if _, err = sw.WriteString(base64EncryptedMsg); err != nil { return } if _, err = sw.WriteString("</Encrypt><MsgSignature>"); err != nil { return } if _, err = sw.WriteString(msgSignature); err != nil { return } if _, err = sw.WriteString("</MsgSignature><TimeStamp>"); err != nil { return } if _, err = sw.WriteString(timestampString); err != nil { return } if _, err = sw.WriteString("</TimeStamp><Nonce>"); err != nil { return } if err = xml.EscapeText(ctx.ResponseWriter, []byte(nonce)); err != nil { return } _, err = sw.WriteString("</Nonce></xml>") return } else { bufw := bufio.NewWriterSize(ctx.ResponseWriter, 256) if _, err = bufw.WriteString("<xml><Encrypt>"); err != nil { return } if _, err = bufw.WriteString(base64EncryptedMsg); err != nil { return } if _, err = bufw.WriteString("</Encrypt><MsgSignature>"); err != nil { return } if _, err = bufw.WriteString(msgSignature); err != nil { return } if _, err = bufw.WriteString("</MsgSignature><TimeStamp>"); err != nil { return } if _, err = bufw.WriteString(timestampString); err != nil { return } if _, err = bufw.WriteString("</TimeStamp><Nonce>"); err != nil { return } if err = xml.EscapeText(bufw, []byte(nonce)); err != nil { return } if _, err = bufw.WriteString("</Nonce></xml>"); err != nil { return } return bufw.Flush() } }
// ServeHTTP 处理微信服务器的回调请求, queryParams 参数可以为 nil. func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request, queryParams url.Values) { callback.DebugPrintRequest(r) if queryParams == nil { queryParams = r.URL.Query() } errorHandler := srv.errorHandler switch r.Method { case "POST": // 推送消息(事件) switch encryptType := queryParams.Get("encrypt_type"); encryptType { case "aes": haveSignature := queryParams.Get("signature") if haveSignature == "" { errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) return } haveMsgSignature := queryParams.Get("msg_signature") if haveMsgSignature == "" { errorHandler.ServeError(w, r, errors.New("not found msg_signature query parameter")) return } timestampString := queryParams.Get("timestamp") if timestampString == "" { errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) return } timestamp, err := strconv.ParseInt(timestampString, 10, 64) if err != nil { err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString) errorHandler.ServeError(w, r, err) return } nonce := queryParams.Get("nonce") if nonce == "" { errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) return } var token string currentToken, lastToken := srv.getToken() if currentToken == "" { err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method") errorHandler.ServeError(w, r, err) return } token = currentToken wantSignature := util.Sign(token, timestampString, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { if lastToken == "" { err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } token = lastToken wantSignature = util.Sign(token, timestampString, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } } else { if lastToken != "" { srv.removeLastToken(lastToken) } } requestBodyBuf := textBufferPool.Get().(*bytes.Buffer) requestBodyBuf.Reset() defer textBufferPool.Put(requestBodyBuf) if _, err = requestBodyBuf.ReadFrom(r.Body); err != nil { errorHandler.ServeError(w, r, err) return } requestBodyBytes := requestBodyBuf.Bytes() var requestHttpBody cipherRequestHttpBody if err = xmlUnmarshal(requestBodyBytes, &requestHttpBody); err != nil { errorHandler.ServeError(w, r, err) return } haveToUserName := requestHttpBody.ToUserName wantToUserName := srv.oriId if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) { err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) errorHandler.ServeError(w, r, err) return } wantMsgSignature := util.MsgSign(token, timestampString, nonce, string(requestHttpBody.Base64EncryptedMsg)) if !security.SecureCompareString(haveMsgSignature, wantMsgSignature) { err = fmt.Errorf("check msg_signature failed, have: %s, want: %s", haveMsgSignature, wantMsgSignature) errorHandler.ServeError(w, r, err) return } encryptedMsg := make([]byte, base64.StdEncoding.DecodedLen(len(requestHttpBody.Base64EncryptedMsg))) encryptedMsgLen, err := base64.StdEncoding.Decode(encryptedMsg, requestHttpBody.Base64EncryptedMsg) if err != nil { errorHandler.ServeError(w, r, err) return } encryptedMsg = encryptedMsg[:encryptedMsgLen] var aesKey []byte currentAESKey, lastAESKey := srv.getAESKey() if currentAESKey == nil { err = errors.New("aes key was not set for Server, see NewServer function or Server.SetAESKey method") errorHandler.ServeError(w, r, err) return } aesKey = currentAESKey random, msgPlaintext, haveAppIdBytes, err := util.AESDecryptMsg(encryptedMsg, aesKey) if err != nil { if lastAESKey == nil { errorHandler.ServeError(w, r, err) return } aesKey = lastAESKey random, msgPlaintext, haveAppIdBytes, err = util.AESDecryptMsg(encryptedMsg, aesKey) if err != nil { errorHandler.ServeError(w, r, err) return } } else { if lastAESKey != nil { srv.removeLastAESKey(lastAESKey) } } callback.DebugPrintPlainRequestMessage(msgPlaintext) haveAppId := string(haveAppIdBytes) wantAppId := srv.appId if wantAppId != "" && !security.SecureCompareString(haveAppId, wantAppId) { err = fmt.Errorf("the message AppId mismatch, have: %s, want: %s", haveAppId, wantAppId) errorHandler.ServeError(w, r, err) return } var mixedMsg MixedMsg if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil { errorHandler.ServeError(w, r, err) return } if haveToUserName != mixedMsg.ToUserName { err = fmt.Errorf("the message ToUserName mismatch between ciphertext and plaintext, %q != %q", haveToUserName, mixedMsg.ToUserName) errorHandler.ServeError(w, r, err) return } ctx := &Context{ ResponseWriter: w, Request: r, QueryParams: queryParams, EncryptType: encryptType, MsgSignature: haveMsgSignature, Signature: haveSignature, Timestamp: timestamp, Nonce: nonce, MsgCiphertext: requestHttpBody.Base64EncryptedMsg, MsgPlaintext: msgPlaintext, MixedMsg: &mixedMsg, Token: token, AESKey: aesKey, Random: random, AppId: haveAppId, handlerIndex: initHandlerIndex, } srv.handler.ServeMsg(ctx) case "", "raw": haveSignature := queryParams.Get("signature") if haveSignature == "" { errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) return } timestampString := queryParams.Get("timestamp") if timestampString == "" { errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) return } timestamp, err := strconv.ParseInt(timestampString, 10, 64) if err != nil { err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString) errorHandler.ServeError(w, r, err) return } nonce := queryParams.Get("nonce") if nonce == "" { errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) return } var token string currentToken, lastToken := srv.getToken() if currentToken == "" { err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method") errorHandler.ServeError(w, r, err) return } token = currentToken wantSignature := util.Sign(token, timestampString, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { if lastToken == "" { err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } token = lastToken wantSignature = util.Sign(token, timestampString, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } } else { if lastToken != "" { srv.removeLastToken(lastToken) } } msgPlaintext, err := ioutil.ReadAll(r.Body) if err != nil { errorHandler.ServeError(w, r, err) return } callback.DebugPrintPlainRequestMessage(msgPlaintext) var mixedMsg MixedMsg if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil { errorHandler.ServeError(w, r, err) return } haveToUserName := mixedMsg.ToUserName wantToUserName := srv.oriId if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) { err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s", haveToUserName, wantToUserName) errorHandler.ServeError(w, r, err) return } ctx := &Context{ ResponseWriter: w, Request: r, QueryParams: queryParams, EncryptType: encryptType, Signature: haveSignature, Timestamp: timestamp, Nonce: nonce, MsgPlaintext: msgPlaintext, MixedMsg: &mixedMsg, Token: token, handlerIndex: initHandlerIndex, } srv.handler.ServeMsg(ctx) default: errorHandler.ServeError(w, r, errors.New("unknown encrypt_type: "+encryptType)) } case "GET": // 验证回调URL是否有效 haveSignature := queryParams.Get("signature") if haveSignature == "" { errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) return } timestamp := queryParams.Get("timestamp") if timestamp == "" { errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) return } nonce := queryParams.Get("nonce") if nonce == "" { errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) return } echostr := queryParams.Get("echostr") if echostr == "" { errorHandler.ServeError(w, r, errors.New("not found echostr query parameter")) return } var token string currentToken, lastToken := srv.getToken() if currentToken == "" { err := errors.New("token was not set for Server, see NewServer function or Server.SetToken method") errorHandler.ServeError(w, r, err) return } token = currentToken wantSignature := util.Sign(token, timestamp, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { if lastToken == "" { err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } token = lastToken wantSignature = util.Sign(token, timestamp, nonce) if !security.SecureCompareString(haveSignature, wantSignature) { err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) errorHandler.ServeError(w, r, err) return } } else { if lastToken != "" { srv.removeLastToken(lastToken) } } io.WriteString(w, echostr) } }