Python 实现微信小程序的用户登录
小程序可以通过官方提供的登录能力来获取用户身份的标示,具体文档可以参考 这里,通过流程时序可以看到,对于需要和前端配合的服务端开发,主要实现的就是通过小程序提供的 code 换取用户的 openid 和 session_key,并用换取到的 openid 和 secret_key 作为自定义的登录态。分析后得知,作为小程序后端的开发,主要实现以下几部分内容:
- 提供一个 HTTP 接口,供小程序方使用,传递 code;
- 换取用户身份标识;
- 维护一个自定义的登录态;
- 需要存储用户的 openid,以备后续使用。
1. 提供给小程序一个 HTTP 接口,接口使用 Tornado 框架
为了简化思路,下面代码都没有做各种异常处理!
class LoginHandler(RequestHandler): def post(self): req_data = json.loads(self.request.body) js_code = req_data.get('js_code') # 这里是换取用户的信息 user_info = get_user_info(js_code=js_code) openid = user_info['openid'] session_key = user_info['session_key'] user_uuid = str(uuid.uuid4()) # 暴露给小程序端的用户标示 # 用来维护用户的登录态 User.save_user_session( user_uuid=user_uuid, openid=openid, session_key=session_key ) # 微信小程序不能设置cookie,把用户信息存在了 headers 中 self.set_header('Authorization', user_uuid) # 存储用户信息 User.save_user_info(open_id=openid) self.set_status(204)
2. 换取用户身份标示,直接使用 Requests 请求微信的相关接口,获取数据
1 def get_user_info(js_code): 2 3 req_params = { 4 "appid": 'app_id', # 小程序的 ID 5 "secret": 'secret', # 小程序的 secret 6 "js_code": js_code, 7 "grant_type": 'authorization_code' 8 } 9 req_result = requests.get('https://api.weixin.qq.com/sns/jscode2session', 10 params=req_params, timeout=3, verify=False) 11 return req_result.json()
3. 维护一个自定义的登录态,使用了 Redis
1 user_redis = StrictRedis.from_url('redis//localhost:6379') 2 3 4 class User(object): 5 6 REDIS_EXPIRES = 7 * 24 * 60 * 60 7 8 @classmethod 9 def save_user_session(cls, user_uuid, openid, session_key): 10 user_session_value = { 11 'openid': openid, 12 'session_key': session_key 13 } 14 user_session_key = 'US:' + user_uuid 15 with user_redis.pipeline(transaction=False) as pipe: 16 pipe.hmset(user_session_key, user_session_value) 17 pipe.expire(user_session_key, cls.REDIS_EXPIRES) 18 pipe.execute()
4. 存储用户信息,以备后用,这里使用了 MySQL,ORM 使用的是 SQLAlchemy
1 from sqlalchemy import create_engine 2 from sqlalchemy.ext.declarative import declarative_base 3 4 # mysql 相关设置 5 engine = create_engine('mysql://root:@localhost/wechat') 6 conn = engine.connect() 7 8 Base = declarative_base() 9 Base.metadata.reflect(engine) 10 tables = Base.metadata.tables 11 12 class User(object): 13 table = tables['user'] 14 15 @classmethod 16 def save_user_info(cls, open_id): 17 # 存储用户信息 18 sql = cls.table.insert().values(open_id=open_id) 19 conn.execute(sql)
SQL 语句
CREATE TABLE `user` ( `id` int(20) unsigned NOT NULL AUTO_INCREMENT, `open_id` varchar(32) NOT NULL COMMENT '用户 open_id', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_oid` (`open_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
全部代码可以点击 这里 获取!