# -*- coding: utf-8 -*- # !/usr/bin/env python import logging import urllib from typing import TYPE_CHECKING from apilib.utils_url import add_query from apps.web.core import AlipayMixin from library.alipay import AliException from .base import AuthBridge if TYPE_CHECKING: from apps.web.core.models import AliApp logger = logging.getLogger(__name__) class AlipayAuthBridge(AuthBridge, AlipayMixin): auth_code_key = 'auth_code' # 静默跳转,无需用户点击授权 AUTH_SCOPE_BASE = 'auth_base' # 需要用户显式点击授权 AUTH_SCOPE_USER = 'auth_user' # 测试网关oauth DEV_ALIPAY_OAUTH_URL = \ 'https://openauth.alipaydev.com/oauth2/publicAppAuthorize.htm?' + \ 'app_id={appid}&scope={scope}&redirect_uri={encoded_return_uri}&state={state}' # 生产环境网关oauth PRODUCTION_ALIPAY_OAUTH_URL = \ 'https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?' + \ 'app_id={appid}&scope={scope}&redirect_uri={encoded_return_uri}&state={state}' def __init__(self, app): # type: (AliApp)->None super(AlipayAuthBridge, self).__init__(app) if self.debug: self._auth_gateway_tmpl = self.DEV_ALIPAY_OAUTH_URL else: self._auth_gateway_tmpl = self.PRODUCTION_ALIPAY_OAUTH_URL def generate_auth_url(self, redirect_uri, payload, scope): """ 参数名 是否必须 描述 app_id 是 开发者应用的app_id scope 是 接口权限值,目前只支持auth_user和auth_base两个值 redirect_uri 是 回调页面 state 否 商户自定义参数,用户授权后, 重定向到redirect_uri时会原样回传给商户。 为防止CSRF攻击,建议开发者请求授权时传入state参数, 该参数要做到既不可预测,又可以证明客户端和当前第三方网站的登录认证状态存在关联。 ``` 关于scope的说明: auth_base:以auth_base为scope发起的网页授权,是用来获取进入页面的用户的userId的, 并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(通常是业务页面)。 auth_user:以auth_user为scope发起的网页授权,是用来获取用户的基本信息的(比如头像、昵称等)。 但这种授权需要用户手动同意,用户同意后,就可在授权后获取到该用户的基本信息。若想获取用户信息,scope的值中需要有该值存在, 如scope=auth_user, auth_base。 ``` :return: url """ logger.debug('generate_auth_callback_url enter. bridge = {}, ' 'callback url = {}; payload = {}'.format(repr(self), redirect_uri, payload)) callback_url = add_query(redirect_uri, {'payload': payload}) encoded_return_uri = urllib.quote_plus(callback_url) result = self._auth_gateway_tmpl.format(appid = self.appid, scope = scope, encoded_return_uri = encoded_return_uri, state = '') logger.debug('generate_auth_callback_url success. result = {}'.format(str(result))) return result def generate_auth_url_base_scope(self, redirect_uri, payload = None): return self.generate_auth_url(payload = payload, scope = self.AUTH_SCOPE_BASE, redirect_uri = redirect_uri) def generate_auth_url_user_scope(self, redirect_uri, payload = None): return self.generate_auth_url(payload = payload, scope = self.AUTH_SCOPE_USER, redirect_uri = redirect_uri) def get_user_info(self, auth_code, refresh_token = None): logger.debug( 'get alipay user info by bridge = {}, auth_code = {}, refresh_token = {}'.format( repr(self), auth_code, refresh_token)) auth_token = self.client.api_alipay_system_oauth_token(auth_code = auth_code, refresh_token = refresh_token) response = self.client.api_alipay_user_info_share(auth_token = auth_token['access_token']) logger.debug('alipay user info = {}'.format(response)) if response['code'] == u'10000': result = { 'openId': response.get('user_id'), 'avatar': response.get('avatar', ''), 'nickname': response.get('nick_name', ''), 'province': response.get('province', ''), 'city': response.get('city', ''), 'extra': { 'is_student_certified': response.get('is_student_certified', ''), 'user_type': response.get('user_type', ''), 'user_status': response.get('user_status', ''), 'is_certified': response.get('is_certified', '') } } sex = response.get('gender', 'unknown').lower() if sex == 'm': result['sex'] = 1 elif sex == 'f': result['sex'] = 2 else: result['sex'] = 0 return result else: raise AliException( errCode = response['code'], errMsg = response['msg'], client = self.client)