Skip to content

Commit d52c5b4

Browse files
committed
feat: Apikey call supports cross domain and application whitelist
1 parent f83336d commit d52c5b4

File tree

6 files changed

+203
-0
lines changed

6 files changed

+203
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: application_access_token_cache.py
6+
@date:2024/7/25 11:34
7+
@desc:
8+
"""
9+
from django.core.cache import cache
10+
from django.db.models import QuerySet
11+
12+
from application.models import ApplicationAccessToken
13+
from common.utils.cache_util import get_cache
14+
15+
16+
@get_cache(cache_key=lambda access_token, use_get_data: access_token,
17+
use_get_data=lambda access_token, use_get_data: use_get_data,
18+
version='APPLICATION_ACCESS_TOKEN_CACHE')
19+
def get_application_access_token(access_token, use_get_data):
20+
application_access_token = QuerySet(ApplicationAccessToken).filter(access_token=access_token).first()
21+
if application_access_token is None:
22+
return None
23+
return {'white_active': application_access_token.white_active,
24+
'white_list': application_access_token.white_list,
25+
'application_icon': application_access_token.application.icon,
26+
'application_name': application_access_token.application.name}
27+
28+
29+
def del_application_access_token(access_token):
30+
cache.delete(access_token, version='APPLICATION_ACCESS_TOKEN_CACHE')

apps/common/middleware/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: __init__.py
6+
@date:2025/7/11 10:43
7+
@desc:
8+
"""
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎
5+
@file: static_headers_middleware.py
6+
@date:2024/3/13 18:26
7+
@desc:
8+
"""
9+
from django.utils.deprecation import MiddlewareMixin
10+
11+
from common.cache_data.application_access_token_cache import get_application_access_token
12+
from maxkb.const import CONFIG
13+
14+
15+
class ChatHeadersMiddleware(MiddlewareMixin):
16+
def process_response(self, request, response):
17+
18+
if request.path.startswith(CONFIG.get_chat_path()) and not request.path.startswith(
19+
CONFIG.get_chat_path() + '/api'):
20+
access_token = request.path.replace(CONFIG.get_chat_path() + '/', '')
21+
if access_token.__contains__('/') or access_token == 'undefined':
22+
return response
23+
application_access_token = get_application_access_token(access_token, True)
24+
if application_access_token is not None:
25+
white_active = application_access_token.get('white_active', False)
26+
white_list = application_access_token.get('white_list', [])
27+
application_icon = application_access_token.get('application_icon')
28+
application_name = application_access_token.get('application_name')
29+
if white_active:
30+
# 添加自定义的响应头
31+
response[
32+
'Content-Security-Policy'] = f'frame-ancestors {" ".join(white_list)}'
33+
response.content = (response.content.decode('utf-8').replace(
34+
'<link rel="icon" href="./favicon.ico"/>',
35+
f'<link rel="icon" href="{application_icon}" />')
36+
.replace('<title>MaxKB</title>', f'<title>{application_name}</title>').encode(
37+
"utf-8"))
38+
return response
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎虎
5+
@file: cross_domain_middleware.py
6+
@date:2024/5/8 13:36
7+
@desc:
8+
"""
9+
from django.http import HttpResponse
10+
from django.utils.deprecation import MiddlewareMixin
11+
12+
from common.cache_data.application_api_key_cache import get_application_api_key
13+
14+
15+
class CrossDomainMiddleware(MiddlewareMixin):
16+
17+
def process_request(self, request):
18+
if request.method == 'OPTIONS':
19+
return HttpResponse(status=200,
20+
headers={
21+
"Access-Control-Allow-Origin": "*",
22+
"Access-Control-Allow-Methods": "GET,POST,DELETE,PUT",
23+
"Access-Control-Allow-Headers": "Origin,X-Requested-With,Content-Type,Accept,Authorization,token"})
24+
25+
def process_response(self, request, response):
26+
auth = request.META.get('HTTP_AUTHORIZATION')
27+
origin = request.META.get('HTTP_ORIGIN')
28+
if auth is not None and str(auth).startswith("application-") and origin is not None:
29+
application_api_key = get_application_api_key(str(auth), True)
30+
cross_domain_list = application_api_key.get('cross_domain_list', [])
31+
allow_cross_domain = application_api_key.get('allow_cross_domain', False)
32+
if allow_cross_domain:
33+
response['Access-Control-Allow-Methods'] = 'GET,POST,DELETE,PUT'
34+
response[
35+
'Access-Control-Allow-Headers'] = "Origin,X-Requested-With,Content-Type,Accept,Authorization,token"
36+
if cross_domain_list is None or len(cross_domain_list) == 0:
37+
response['Access-Control-Allow-Origin'] = "*"
38+
elif cross_domain_list.__contains__(origin):
39+
response['Access-Control-Allow-Origin'] = origin
40+
return response

apps/common/middleware/gzip.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# coding=utf-8
2+
"""
3+
@project: MaxKB
4+
@Author:虎
5+
@file: gzip.py
6+
@date:2025/2/27 10:03
7+
@desc:
8+
"""
9+
from django.utils.cache import patch_vary_headers
10+
from django.utils.deprecation import MiddlewareMixin
11+
from django.utils.regex_helper import _lazy_re_compile
12+
from django.utils.text import compress_sequence, compress_string
13+
14+
re_accepts_gzip = _lazy_re_compile(r"\bgzip\b")
15+
16+
17+
class GZipMiddleware(MiddlewareMixin):
18+
"""
19+
Compress content if the browser allows gzip compression.
20+
Set the Vary header accordingly, so that caches will base their storage
21+
on the Accept-Encoding header.
22+
"""
23+
24+
max_random_bytes = 100
25+
26+
def process_response(self, request, response):
27+
if request.method != 'GET' or request.path.startswith('/api'):
28+
return response
29+
# It's not worth attempting to compress really short responses.
30+
if not response.streaming and len(response.content) < 200:
31+
return response
32+
33+
# Avoid gzipping if we've already got a content-encoding.
34+
if response.has_header("Content-Encoding"):
35+
return response
36+
37+
patch_vary_headers(response, ("Accept-Encoding",))
38+
39+
ae = request.META.get("HTTP_ACCEPT_ENCODING", "")
40+
if not re_accepts_gzip.search(ae):
41+
return response
42+
43+
if response.streaming:
44+
if response.is_async:
45+
# pull to lexical scope to capture fixed reference in case
46+
# streaming_content is set again later.
47+
original_iterator = response.streaming_content
48+
49+
async def gzip_wrapper():
50+
async for chunk in original_iterator:
51+
yield compress_string(
52+
chunk,
53+
max_random_bytes=self.max_random_bytes,
54+
)
55+
56+
response.streaming_content = gzip_wrapper()
57+
else:
58+
response.streaming_content = compress_sequence(
59+
response.streaming_content,
60+
max_random_bytes=self.max_random_bytes,
61+
)
62+
# Delete the `Content-Length` header for streaming content, because
63+
# we won't know the compressed size until we stream it.
64+
del response.headers["Content-Length"]
65+
else:
66+
# Return the compressed content only if it's actually shorter.
67+
compressed_content = compress_string(
68+
response.content,
69+
max_random_bytes=self.max_random_bytes,
70+
)
71+
if len(compressed_content) >= len(response.content):
72+
return response
73+
response.content = compressed_content
74+
response.headers["Content-Length"] = str(len(response.content))
75+
76+
# If there is a strong ETag, make it weak to fulfill the requirements
77+
# of RFC 9110 Section 8.8.1 while also allowing conditional request
78+
# matches on ETags.
79+
etag = response.get("ETag")
80+
if etag and etag.startswith('"'):
81+
response.headers["ETag"] = "W/" + etag
82+
response.headers["Content-Encoding"] = "gzip"
83+
84+
return response

apps/maxkb/settings/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
'django.middleware.security.SecurityMiddleware',
5757
'django.contrib.sessions.middleware.SessionMiddleware',
5858
'django.middleware.common.CommonMiddleware',
59+
'common.middleware.gzip.GZipMiddleware',
60+
'common.middleware.chat_headers_middleware.ChatHeadersMiddleware',
61+
'common.middleware.cross_domain_middleware.CrossDomainMiddleware',
5962

6063
]
6164

0 commit comments

Comments
 (0)