随着互联网应用的普及,扫码登录已成为非常常见的用户验证方式。尤其是微信公众平台的扫码登录,极大地简化了用户的登录流程。本文将介绍如何实现一个微信公众平台的扫码登录系统,并结合 Redis 和 MySQL 来处理登录状态与用户信息。
扫码登录的基本流程如下:
Ticket
参数。Ticket
和用户的 openid
等信息。Ticket
和用户信息。Ticket
是否有效,从而判断用户是否成功登录。在实现微信扫码登录的过程中,我们使用以下几项技术:
ticket_hash
和 token
,并设置有效期。openid
和用户的相关信息。接下来我们详细讨论每个模块的实现细节。
当用户扫描二维码时,微信会向你的服务器发送事件通知,包含 FromUserName
(即 openid
)、Ticket
等重要信息。我们的任务是接收这些数据并进行处理。
def callback(ch, method, properties, body):
"""消息处理回调函数,打印接收到的JSON消息,并从Redis中获取token发送模板消息"""
try:
# 将消息体(字节流)解码为字符串
message_str = body.decode('utf-8')
# 将字符串反序列化为字典
message_dict = json.loads(message_str)
# 打印接收到的字典格式的消息
logger.info(f"接收到来自 {RABBITMQ_QUEUE} 队列的消息: {message_dict}")
# 从消息中提取 openid 和用户信息
openid = message_dict.get('FromUserName')
ticket = message_dict.get('Ticket', '')
# 检查和存储用户信息
if openid:
check_and_store_user(openid)
# 生成 ticket 的 hash 作为 Redis 的 key
if ticket:
ticket_hash = hashlib.sha256(ticket.encode()).hexdigest()
logger.info(f"Generated ticket hash for {openid}: {ticket_hash}")
# 生成安全的随机 token
token = secrets.token_urlsafe()
logger.info(f"Generated random token for {openid}: {token}")
# 存储 ticket_hash 和 token 到 Redis(db1),并设置 5 分钟(300秒)的过期时间
redis_client_db1.set(ticket_hash, token, ex=300) # 300秒 = 5分钟
logger.info(f"Stored ticket hash and token in Redis DB 1 with 5 min expiry: {ticket_hash} -> {token}")
if openid:
# 从 Redis (db0) 中获取全局的 access_token
global_access_token = redis_client_db0.get('global_access_token')
if not global_access_token:
logger.error("从 Redis 获取 global_access_token 失败")
return
# 构造模板消息的数据,通知用户成功登录
template_message_payload = {
"touser": openid,
"template_id": "C7CaKWQHaz9vc4x0fKWzU6-b62VKINsuWgAk",
"url": "",
"data": {
"time": {"value": "现在时刻", "color": "#173177"},
"status": {"value": "登录成功", "color": "#173177"},
"ip": {"value": "用户IP", "color": "#173177"}
}
}
# 发送模板消息到微信服务器
response = requests.post(f"{SEND_MESSAGE_URL}?access_token={global_access_token}",
json=template_message_payload)
# 检查响应状态
if response.status_code == 200:
logger.info(f"模板消息成功发送给用户 {openid}.")
else:
logger.error(f"模板消息发送失败: {response.text}")
else:
logger.error("消息中未找到 FromUserName 字段")
except json.JSONDecodeError as e:
# 如果消息不是合法的JSON格式,捕获异常
logger.error(f"无法解析接收到的消息: {str(e)}")
ticket_hash
和 token
为了保证用户登录状态的有效性,我们将 ticket_hash
和随机生成的 token
存储在 Redis 中,并设置 5 分钟的过期时间。
# 生成 ticket 的 hash 作为 Redis 的 key
if ticket:
ticket_hash = hashlib.sha256(ticket.encode()).hexdigest()
logger.info(f"Generated ticket hash for {openid}: {ticket_hash}")
# 生成安全的随机 token
token = secrets.token_urlsafe()
logger.info(f"Generated random token for {openid}: {token}")
# 存储 ticket_hash 和 token 到 Redis(db1),并设置 5 分钟(300秒)的过期时间
redis_client_db1.set(ticket_hash, token, ex=300) # 300秒 = 5分钟
logger.info(f"Stored ticket hash and token in Redis DB 1 with 5 min expiry: {ticket_hash} -> {token}")
通过 Redis 的过期时间控制,我们确保登录票据 ticket_hash
在规定时间内有效,超时后会自动过期删除,保证了系统的安全性。
每次扫码时,我们都要检查该用户是否是新用户。如果是新用户,我们需要将 openid
等用户信息写入 MySQL,以便日后管理和查询。
def check_and_store_user(openid):
"""检查用户是否已存在,若不存在则存储新用户信息"""
conn = get_mysql_connection()
cursor = conn.cursor()
# 检查用户是否已存在
cursor.execute("SELECT * FROM users WHERE wechat_open_id = %s", (openid,))
user = cursor.fetchone()
if user:
# 用户已存在,返回相关信息
logger.info(f"用户 {openid} 已存在.")
else:
# 用户不存在,存储用户信息
cursor.execute("INSERT INTO users (wechat_open_id) VALUES (%s)", (openid,))
conn.commit()
logger.info(f"存储新用户 {openid}")
cursor.close()
conn.close()
当用户扫描二维码后,客户端将通过轮询请求不断检查该 ticket_hash
是否已经存在于 Redis 中。一旦找到对应的 ticket_hash
,说明用户已经扫码成功,服务器可以返回登录状态给客户端。
微信扫码登录的整个流程包含多个步骤,涉及 Redis 存储登录状态、MySQL 管理用户信息、RabbitMQ 进行异步消息处理等多个环节。通过将这些模块有机地结合在一起,能够有效地实现扫码登录、登录状态管理及用户信息的持久化。
通过这种机制,不仅简化了用户的登录流程,还大大提高了系统的安全性和扩展性。在实际项目中,可以根据不同的业务需求,进一步优化这些流程。
希望通过本文的介绍,大家能够对微信扫码登录有一个全面的理解,并能够在自己的项目中加以实现和应用。