def _parse_uri(self, client): """ 解析请求的 uri :type client: Client :return: """ try: _, req_endpoint, version, uri = self.handler.request.uri.split( '/', 3) except ValueError: raise AuthRequestException( 'Invalid Request Uri, Fail to Get Endpoint and Version') endpoints = client.config.get('endpoints', {}) endpoint = endpoints.get('%s:%s' % (req_endpoint, version)) if endpoint is None: raise AuthRequestException('No Permission to Access %s' % req_endpoint) if not endpoint.get('enable', True): raise AuthRequestException('Disabled Endpoint') # version = endpoint.get('version') # if version and version != '': # try: # # 处理 uri 前的版本号 # _, uri = uri.split(version, 1) # except ValueError: # raise AuthRequestException('Invalid Request API Version') if not uri.startswith('/'): uri = '/' + uri if endpoint.get('builtin_endpoint', False): # 如果是内置的 endpoint, 就没有 forward_url forward_url = None else: # 解析要转发的地址 endpoint_url = endpoint.get('url') if endpoint_url is None: raise AuthRequestException('No Endpoint Url Config') endpoint_netloc = endpoint.get('netloc') if endpoint_netloc is None: url_parsed = urlparse(endpoint_url) endpoint['netloc'] = url_parsed.netloc if endpoint_url.endswith('/'): forward_url = endpoint_url + uri[1:] else: forward_url = endpoint_url + uri request_data = { 'endpoint': endpoint, 'uri': uri, 'forward_url': forward_url, } logger.debug(request_data) return request_data
def auth_request(self, request): try: timestamp = int(request.headers.get('X-Api-Timestamp')) except ValueError: raise AuthRequestException('Invalid X-Api-Timestamp Header') now_ts = int(time.time()) if abs(timestamp - now_ts) > settings.SIGNATURE_EXPIRE_SECONDS: logger.debug('Expired signature, timestamp: %s' % timestamp) raise AuthRequestException('Expired Signature') signature = request.headers.get('X-Api-Signature') if signature: del request.headers['X-Api-Signature'] else: logger.debug('No Signature Provided') raise AuthRequestException('No Signature Provided') string_to_sign = self._request_string_to_sign(request) # 如果不是 unicode 输出会引发异常 # logger.debug('string_to_sign: %s' % string_to_sign.decode('utf-8')) hash_value = sha256(utf8(string_to_sign)).hexdigest() real_signature = self.sign_string(hash_value) if signature != real_signature: logger.debug('Signature not match: %s, %s' % (signature, real_signature)) raise AuthRequestException('Invalid Signature')
def post(self, *args, **kwargs): logger.debug('AuthLoginHandler') login_auth_url = self.client.config.get('login_auth_url') logger.debug(login_auth_url) if login_auth_url is None: raise AuthRequestException( 'Missing Login Auth Url in Client Config') # access_token 多少秒后过期 access_token_ex = self.client.config.get('access_token_ex') refresh_token_ex = self.client.config.get('refresh_token_ex') # 设置超时时间 async_http_connect_timeout = ASYNC_HTTP_CONNECT_TIMEOUT async_http_request_timeout = ASYNC_HTTP_REQUEST_TIMEOUT headers = {'Content-Type': 'application/json; charset=utf-8'} try: response = yield AsyncHTTPClient().fetch( HTTPRequest(url=login_auth_url, method=self.request.method, body=self.request.body, headers=headers, connect_timeout=async_http_connect_timeout, request_timeout=async_http_request_timeout)) except Exception as e: logger.error(e) logger.error(traceback.format_exc()) raise AuthRequestException('Fail to Request Login Auth Url') json_data = json.loads(response.body) if json_data['code'] == APIStatusCode.SUCCESS: user_info = json_data['data'] token_info = { 'access_key': self.client.access_key, 'user_info': user_info } token_info = RedisHelper.set_token_info(token_info, access_token_ex, refresh_token_ex) if token_info is None: self.error(msg='Save Access Token Error') else: data = { 'access_token': token_info['access_token'], 'refresh_token': token_info['refresh_token'], # access_token 过期时间 'expires_in': int(time.time()) + access_token_ex, 'user_info': user_info } self.success(data) else: self.fail(msg=json_data['msg'])
def process_request(self, *args, **kwargs): logger.debug('process_request') if 'X-Api-User-Json' in self.handler.request.headers: del self.handler.request.headers['X-Api-User-Json'] endpoint = self.handler.client.request['endpoint'] require_login = endpoint.get('require_login', False) if not require_login: return access_token = self.handler.get_query_argument('access_token', None) token_info = RedisHelper.get_access_token_info(access_token) if token_info is not None: if token_info['access_key'] != self.handler.client.access_key: raise AuthRequestException( 'This Access Token is Belongs to Another Client App') logger.debug('允许访问') try: json_str = json.dumps(token_info['user_info'], ensure_ascii=False) # 用户信息使用 json 存储,并编码为 base64 self.handler.request.headers[ 'X-Api-User-Json'] = base64.b64encode( json_str.encode('utf8')) except Exception, e: logger.error('设置 X-Api-User-Json 失败') logger.error(e.message) logger.error(traceback.format_exc())
def _acl_filter(self): """ 如果启用访问控制列表,就需要检查URI是否允许访问 :return: """ client = self.handler.client uri = client.request['uri'] endpoint = client.request['endpoint'] enable_acl = endpoint.get('enable_acl', False) if enable_acl: acl_rules = endpoint.get('acl_rules', []) # 如果都没有找到匹配的规则,默认返回Tue,放行 allow_access = True for r in acl_rules: re_uri, is_permit = r['re_uri'], r['is_permit'] pattern = re.compile(re_uri) match = pattern.search(uri) if match: allow_access = is_permit break # 禁止访问该 uri if not allow_access: logger.info('forbidden uri %s' % uri) raise AuthRequestException('Forbidden Uri')
def process_request(self, *args, **kwargs): request = self.handler.request client = self.handler.client logger.debug('client: %s' % client) client.encrypt_type = request.headers.get('X-Api-Encrypt-Type', 'raw') if client.encrypt_type != 'aes': return def decrypt_data(): aes_cipher = AESCipher(client.secret_key) encrypted_uri = self.handler.request.headers.get( 'X-Api-Encrypted-Uri') if encrypted_uri: request.uri = aes_cipher.decrypt(utf8(encrypted_uri)) logger.debug('decrypted uri %s' % request.uri) encrypted_headers = self.handler.request.headers.get( 'X-Api-Encrypted-Headers') if encrypted_headers: headers_str = aes_cipher.decrypt(utf8(encrypted_headers)) headers = dict(json.loads(headers_str)) logger.debug('raw headers %s' % request.headers) for k, v in headers.iteritems(): # 要全部使用 text_type,否则会出现有的为 str,有的为 unicode # 导致422错误 request.headers[text_type(k)] = text_type(v) logger.debug('decrypted headers %s' % request.headers) if request.body and len(request.body) > 0: logger.debug('解密 body') logger.debug(request.body) request.body = aes_cipher.decrypt(utf8(request.body)) # 解密完之后不需要重新计算 Content-Length, # 因为请求后端 API 时不带 Content-Length try: decrypt_data() except Exception as e: logger.error('解密数据出错') logger.error(e) logger.error(traceback.format_exc()) raise AuthRequestException('AES decrypt error')
def get_client_config(self): config_data = RedisHelper.get_client_config(self.access_key) if config_data is None: raise ClientBadConfigException('No Client Config') else: # 校验 config 数据是否正确 v = Validator(self.config_schema, allow_unknown=True) if not v.validate(config_data): logger.error(v.errors) raise ClientBadConfigException('Bad Client Config') logger.debug(config_data) if not config_data.get('enable', True): raise AuthRequestException('Disabled Client') self.secret_key = config_data.get('secret_key') # 添加内置的 endpoint for t in settings.BUILTIN_ENDPOINTS: endpoint = t['config'] k = '%s:%s' % (endpoint['name'], endpoint['version']) config_data['endpoints'][k] = endpoint self.config = config_data
endpoint = self.handler.client.request['endpoint'] require_login = endpoint.get('require_login', False) if not require_login: return access_token = self.handler.get_query_argument('access_token', None) token_info = RedisHelper.get_access_token_info(access_token) if token_info is not None: if token_info['access_key'] != self.handler.client.access_key: raise AuthRequestException( 'This Access Token is Belongs to Another Client App') logger.debug('允许访问') try: json_str = json.dumps(token_info['user_info'], ensure_ascii=False) # 用户信息使用 json 存储,并编码为 base64 self.handler.request.headers[ 'X-Api-User-Json'] = base64.b64encode( json_str.encode('utf8')) except Exception, e: logger.error('设置 X-Api-User-Json 失败') logger.error(e.message) logger.error(traceback.format_exc()) else: logger.info('没有获取到用户信息,不允许访问') # 获取用户信息失败 raise AuthRequestException('Expired or Invalid Access Token')