【Exception】微信JS-SDK接入报错: invalid signature 签名无效 解决思路

【Exception】微信JS-SDK接入报错: invalid signature 签名无效 解决思路

 

一、问题描述

1、接入微信 JS-SDK实现分享链接时,显示公司LOGO,便于品牌效应传播。

2、遇到一个问题: JS-SDK 签名一直无法通过,按照 【附录5-常见错误及解决方法】逐一排除后,且本地加密后签名和【微信 JS 接口签名校验工具】得出的结果一致的情况下,仍然报错: "invalid signature" 。

 

 

3、心情如下图:

 

 

二、问题解决

1、确认要分享的页面URL是js动态获取的,而非静态域名地址。

  • 正确: var url = location.href.split('#')[0];
  • 错误: var url = "www.abc.com/xx.html".split('#')[0];

 

2、确认当前测试服务地址和JS安全域名IP地址一致。

  • 如:js 安全域名地址为 abc.com ,对应ip为: 11.22.33.44
  • 本地启动 Tomcat,测试地址为: http://localhost:999/xx.html 这种情况下,获取到的signature 签名,和 【微信 JS 接口签名校验工具】得出的结果是一样的,但是会仍然报错: "invalid signature" 。

 

三、问题总结

1、"invalid signature" 这个问题,对于新手来说,按照【附录5-常见错误及解决方法】逐一排除后,仍未解决,会有点郁闷,怀疑人生的! 这里记录下,希望对后续遇到此类问题的朋友能有所启发。

2、不知道为啥微信不支持静态域名地址分享!!!

3、若问题描述,解决思路表述不清晰,不理解,可私信联系,交流沟通,谢谢。

 

 

参考资料: 微信JS-SDK说明文档

        微信 JS 接口签名校验工具

        微信web开发者工具

 

 

 

 

 

 

 

### 修复 `type object 'WeChatService' has no attribute 'parse_message'` 错误并提供完整代码 错误的根本原因在于类 `WeChatService` 中未定义方法 `parse_message`,或者该方法的定义被遗漏或拼写错误[^1]。以下是修复后的完整代码,确保所有功能正常运行。 --- ### 修复后的完整代码 ```python import json import logging import asyncio from typing import Optional, Dict, Any from fastapi import FastAPI, Request, Response, HTTPException from pydantic import BaseModel, Field import httpx from pydantic_settings import BaseSettings from openai import OpenAI # 导入OpenAI SDK import re import xml.etree.ElementTree as ET # ==================== 配置类 ==================== class Settings(BaseSettings): version: str = "1.0" app_name: str = "拖车调度客服系统" host: str = "0.0.0.0" port: int = 8081 wechat_appid: str = "wx_V4GKlfb0V4NLpW5HHKR5U" wechat_device_id: str = "wx_wR_U4zPj2M_OTS3BCyoE4" target_group: str = "52692331298@chatroom" vllm_api_url: str = "http://localhost:8000/v1" vllm_model: str = "Qwen" ai_timeout: int = 30 forward_api_url: str = "http://api.geweapi.com/gewe/v2/api/message/postText" forward_api_token: str = "019abe72-4122-48e3-9a7c-ba47a560da5d" forward_timeout: int = 10 class Config: env_file = ".env" settings = Settings() # ==================== 数据模型 ==================== class WeChatMessage(BaseModel): TypeName: str Appid: str Wxid: str Data: Dict[str, Any] class ApifoxModel(BaseModel): token: str = Field(..., alias="X-GEWE-TOKEN", description="API令牌", serialization_alias="X-GEWE-TOKEN") app_id: str = Field(..., alias="appId", description="设备ID") ats: Optional[str] = Field(None, description="@的好友列表") content: str = Field(..., description="消息内容") to_wxid: str = Field(..., alias="toWxid", description="接收方ID") class Config: populate_by_name = True json_encoders = { str: lambda v: v.replace("\u2005", " ") if v else v } # ==================== 日志配置 ==================== logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('wechat_service.log') ] ) logger = logging.getLogger("wechat.service") # ==================== 服务类 ==================== class WeChatService: @staticmethod def parse_message(raw_data: bytes) -> WeChatMessage: """解析微信消息""" try: data = json.loads(raw_data.decode('utf-8')) return WeChatMessage(**data) except Exception as e: logger.error(f"消息解析失败: {str(e)}") raise HTTPException(400, detail="Invalid message format") @staticmethod def extract_content(msg: WeChatMessage) -> Dict[str, Any]: """提取消息内容""" content = msg.Data["Content"]["string"] speaker_id, _, actual_content = content.partition(":") return { "is_group": "@chatroom" in msg.Data["FromUserName"]["string"], "group_id": msg.Data["FromUserName"]["string"].strip(), "speaker_id": speaker_id.strip(), "speaker_nickname": msg.Data.get("PushContent", "").split(":")[0].strip(), "content": actual_content.strip(), "msg_type": msg.Data["MsgType"], "msg_id": msg.Data.get("MsgId", ""), "original_msg": msg.Data # 保留原始消息数据用于引用 } @staticmethod def parse_location_message(content: str) -> Optional[str]: """解析位置消息""" try: xml_match = re.search(r'<msg>.*?</msg>', content, re.DOTALL) if not xml_match: return None xml_content = xml_match.group(0) root = ET.fromstring(xml_content) location = root.find('location') if location is None: return None label = location.get('label', '未知位置') x = location.get('x', '0.0') # 经度 y = location.get('y', '0.0') # 纬度 formatted_location = f"位置:{label},经纬度:{x},{y}" return formatted_location except Exception as e: logger.error(f"解析位置消息失败: {str(e)}") return None class AIService: @staticmethod async def generate_reply(prompt: str, quoted_content: Optional[str] = None) -> str: try: if quoted_content: prompt = f"""你正在回复一条消息,请根据上下文进行回复。 原消息内容: "{quoted_content}" 当前需要回复的内容: "{prompt}" 请直接回复用户的问题,保持专业和礼貌:""" client = OpenAI(base_url=settings.vllm_api_url, api_key="EMPTY") response = client.completions.create( model=settings.vllm_model, prompt=prompt, max_tokens=100, temperature=0.7, top_p=0.9, stop=["\n\n", "。", "!", "?"], ) return response.choices[0].text.strip() except Exception as e: logger.error(f"AI生成失败: {str(e)}") return "收到您的请求,客服将尽快处理" class MessageSender: @staticmethod async def send(content: str, to_wxid: str, at_wxid: Optional[str] = None, original_msg: Optional[Dict] = None) -> bool: try: clean_content = content.split('\n')[0].strip() ats = at_wxid if at_wxid else None message = ApifoxModel( token=settings.forward_api_token, app_id=settings.wechat_appid, content=clean_content, to_wxid=to_wxid, ats=ats ) if original_msg: msg_id = original_msg.get("MsgId", "") from_user = original_msg.get("FromUserName", {}).get("string", "") original_content = original_msg.get("Content", {}).get("string", "") if ':' in original_content: quoted_sender, quoted_content = original_content.split(':', 1) quoted_content = quoted_content.strip() else: quoted_sender = from_user quoted_content = original_content xml_template = """<?xml version="1.0"?> <msg> <appmsg appid="" sdkver="0"> <title>{title}</title> <action>view</action> <type>57</type> <showtype>0</showtype> <refermsg> <type>1</type> <svrid>{msg_id}</svrid> <fromusr>{quoted_sender}</fromusr> <content>{quoted_content}</content> </refermsg> </appmsg> </msg>""" xml_content = xml_template.format( title=clean_content[:60], msg_id=msg_id, quoted_sender=quoted_sender, quoted_content=quoted_content ) forward_url = "http://api.geweapi.com/gewe/v2/api/message/forwardUrl" forward_data = { "appId": settings.wechat_appid, "toWxid": to_wxid, "xml": xml_content } async with httpx.AsyncClient(timeout=settings.forward_timeout) as client: response = await client.post( forward_url, json=forward_data, headers={ "X-GEWE-TOKEN": settings.forward_api_token, "Content-Type": "application/json" } ) return response.json().get("ret", -1) == 0 else: async with httpx.AsyncClient(timeout=settings.forward_timeout) as client: response = await client.post( settings.forward_api_url, json=message.model_dump(by_alias=True, exclude_none=True), headers={ "X-GEWE-TOKEN": settings.forward_api_token, "Content-Type": "application/json" } ) return response.json().get("ret", -1) == 0 except Exception as e: logger.error(f"发送失败: {str(e)}") return False # ==================== FastAPI应用 ==================== app = FastAPI( title=settings.app_name, description="智能拖车调度服务接口", version=settings.version ) @app.post("/process") async def process_message(request: Request): try: raw_data = await request.body() logger.info(f"接收到的原始字节: {raw_data}") wechat_msg = WeChatService.parse_message(raw_data) # 修复此处调用 msg_info = WeChatService.extract_content(wechat_msg) logger.info(f"接收到的消息内容: {msg_info['content']}") if not (msg_info["is_group"] and msg_info["group_id"] == settings.target_group and msg_info["msg_type"] in [1, 48]): return Response(content="success") if msg_info["msg_type"] == 48: location_label = WeChatService.parse_location_message(msg_info["content"]) if location_label: query_content = f"位置:{location_label}" logger.info(f"解析到的位置信息: {query_content}") else: error_msg = "无法解析位置信息" logger.error(error_msg) return Response(content="success") else: query_content = msg_info["content"].strip() logger.info(f"提取到的文本内容: {query_content}") if query_content == "在吗": reply = "您好,请问有什么可以帮您?" elif "拖车" in query_content: reply = "好的,请您发一下电话和定位,我马上为您安排服务。" else: prompt = f"""您咨询的问题是:"{query_content}"。请直接回答用户的问题,不要包含其他提示词或说明。""" reply = await AIService.generate_reply(prompt) logger.info(f"生成的回复内容: {reply}") await MessageSender.send( content=reply, to_wxid=msg_info["group_id"], at_wxid=msg_info["speaker_id"], original_msg=msg_info["original_msg"] ) return Response(content="success") except Exception as e: logger.error(f"处理异常: {str(e)}") return Response(content="success", status_code=500) if __name__ == "__main__": import uvicorn uvicorn.run(app, host=settings.host, port=settings.port) ``` --- ### 修改点说明 1. **修复 `parse_message` 方法**:确保 `WeChatService` 类中定义了 `parse_message` 方法,并正确解析传入的原始数据为 `WeChatMessage` 对象[^1]。 2. **增强日志记录**:增加了对每个关键步骤的日志记录,便于排查问题。 3. **XML构造修正**:确保引用消息的XML格式符合微信服务器的要求[^2]。 4. **错误处理改进**:在多个关键步骤中添加了异常捕获逻辑,避免因单个错误导致整个服务崩溃。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值