我正在使用aiohttp开发api网关服务,该服务跨多个内部服务执行聚合操作,并将它们打包为207批量响应。
在内部,我们使用Django会话cookie作为主要的身份验证手段(计划于今年第二季度移至JWT),这意味着对于受此网关攻击的服务,我需要从浏览器或Rest API请求。
似乎aiohttp根本没有这样做,也没有在Postman Cookies
标头中提供任何cookie。但是,如果我使用相同的Cookie直接访问该服务,则会得到预期的响应。
现在的代码:
class AggregateView(BaseView):
async def post(self):
requests = await self.request.json()
logger.debug(f'Making [{len(requests)}] service calls at [{datetime.now()}]')
queries = []
async with ClientSession(cookie_jar=CookieJar(unsafe=True)) as session:
for req in requests:
queries.append(self._retrieve(req, session))
logger.debug(f'Async calls created [{datetime.now()}]')
results = await asyncio.gather(*queries)
logger.debug(f'Async calls completed and aggregated [{datetime.now()}]')
return web.json_response(
status=207,
data=results
)
class BaseView(web.View):
@cache
async def _retrieve(self, req, session):
"""
Unpacks a request body and executes the appropriate service call
:param request: request body as dictionary
:param session: aiohttp ClientSession
:return:
"""
url, path, query_params = await self._unpack_request(req)
logger.info(f'{url}/{path}\t{query_params}')
logger.info(f'Starting request for url [{url}/{path}] at [{datetime.now()}]')
logger.info(f'with query params {query_params}')
func = session.get
params = {
'url': f'{url}/{path}',
'params': query_params,
'headers': {
'Vary': 'Accept, Origin, Accept, Accept-Encoding, Authorization, Cookie'
}
}
async with AsyncBreaker(circuit_breaker, params['url'], func, **params) as resp:
logger.info(f'received request for url [{url}/{path}] at [{datetime.now()}]')
logger.info(resp.headers)
logger.info(resp.cookies)
return {
'url': f'{url}/{path}',
'status': resp.status,
'response': await resp.json()
}
def cache(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
request = args[1]
access_logger.info(args[2].cookie_jar._cookies.keys())
requested_service = request.get('service').upper()
service = SERVICES.get(requested_service)
if service and service.get('cache'):
access_logger.info('This response is cacheable.')
pass
return await func(*args, **kwargs)
return wrapper
dict_keys([]) # output in #cache for cookie jar inspection
This response is cacheable.
Starting request for url [https://service.company.com/api/v1/home-editorial/]
at [2019-01-11 15:01:03.983993]
with query params None
received request for url [https://service.company.com/api/v1/home-editorial/]
at [2019-01-11 15:01:04.434696]
Header output: <CIMultiDictProxy('Server': 'nginx/1.11.3', 'Content-Type':
'application/json', 'Www-Authenticate': 'Token', 'Allow': 'GET, HEAD,
OPTIONS', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security':
'max-age=15724800; includeSubDomains; preload', 'Accept-Ranges': 'bytes',
'Content-Length': '58', 'Accept-Ranges': 'bytes', 'Date':
'Fri, 11 Jan 2019 15:01:04 GMT', 'Via': '1.1 varnish', 'Connection':
'keep-alive', 'X-Client-IP': '192.150.73.149', 'X-Served-By':
'cache-bos8232-BOS', 'X-Cache': 'MISS', 'X-Cache-Hits': '0',
'X-Timer': 'S1547218864.246313,VS0,VE152', 'Vary': 'Accept, Origin')>
注意:
AsyncBreaker
使用aiobreaker
来使用断路器模式来保护服务免受过载@cache
装饰器当前只包装了检索方法,并用于检查AggregateView
和ClientSession
的属性。解决此问题后,它将在aiocache中具有更全面的功能。