perf: 优化跳转页

pull/12201/head
ibuler 2023-11-16 11:26:26 +08:00 committed by Eric_Lee
parent 64f3509c8c
commit ba38852354
8 changed files with 24 additions and 9 deletions

View File

@ -14,7 +14,7 @@ from common.api import JMSGenericViewSet
from common.const.http import POST, GET from common.const.http import POST, GET
from common.permissions import OnlySuperUser from common.permissions import OnlySuperUser
from common.serializers import EmptySerializer from common.serializers import EmptySerializer
from common.utils import reverse from common.utils import reverse, safe_next_url
from common.utils.timezone import utc_now from common.utils.timezone import utc_now
from users.models import User from users.models import User
from ..errors import SSOAuthClosed from ..errors import SSOAuthClosed
@ -45,6 +45,7 @@ class SSOViewSet(AuthMixin, JMSGenericViewSet):
username = serializer.validated_data['username'] username = serializer.validated_data['username']
user = User.objects.get(username=username) user = User.objects.get(username=username)
next_url = serializer.validated_data.get(NEXT_URL) next_url = serializer.validated_data.get(NEXT_URL)
next_url = safe_next_url(next_url, request=request)
operator = request.user.username operator = request.user.username
# TODO `created_by` 和 `created_by` 可以通过 `ThreadLocal` 统一处理 # TODO `created_by` 和 `created_by` 可以通过 `ThreadLocal` 统一处理

View File

@ -20,10 +20,11 @@ from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponseRedirect, QueryDict from django.http import HttpResponseRedirect, QueryDict
from django.urls import reverse from django.urls import reverse
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.http import url_has_allowed_host_and_scheme, urlencode from django.utils.http import urlencode
from django.views.generic import View from django.views.generic import View
from authentication.utils import build_absolute_uri_for_oidc from authentication.utils import build_absolute_uri_for_oidc
from common.utils import safe_next_url
from .utils import get_logger from .utils import get_logger
logger = get_logger(__file__) logger = get_logger(__file__)
@ -100,8 +101,7 @@ class OIDCAuthRequestView(View):
# Stores the "next" URL in the session if applicable. # Stores the "next" URL in the session if applicable.
logger.debug(log_prompt.format('Stores next url in the session')) logger.debug(log_prompt.format('Stores next url in the session'))
next_url = request.GET.get('next') next_url = request.GET.get('next')
request.session['oidc_auth_next_url'] = next_url \ request.session['oidc_auth_next_url'] = safe_next_url(next_url, request=request)
if url_has_allowed_host_and_scheme(url=next_url, allowed_hosts=(request.get_host(),)) else None
# Redirects the user to authorization endpoint. # Redirects the user to authorization endpoint.
logger.debug(log_prompt.format('Construct redirect url')) logger.debug(log_prompt.format('Construct redirect url'))

View File

@ -18,7 +18,7 @@ from authentication.permissions import UserConfirmation
from common.sdk.im.dingtalk import URL, DingTalk from common.sdk.im.dingtalk import URL, DingTalk
from common.utils import get_logger from common.utils import get_logger
from common.utils.common import get_request_ip from common.utils.common import get_request_ip
from common.utils.django import get_object_or_none, reverse from common.utils.django import get_object_or_none, reverse, safe_next_url
from common.utils.random import random_string from common.utils.random import random_string
from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin
from users.models import User from users.models import User
@ -185,6 +185,7 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View):
def get(self, request: HttpRequest): def get(self, request: HttpRequest):
redirect_url = request.GET.get('redirect_url') or reverse('index') redirect_url = request.GET.get('redirect_url') or reverse('index')
next_url = self.get_next_url_from_meta() or reverse('index') next_url = self.get_next_url_from_meta() or reverse('index')
next_url = safe_next_url(next_url, request=request)
redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True) redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True)
redirect_uri += '?' + urlencode({ redirect_uri += '?' + urlencode({

View File

@ -24,7 +24,7 @@ from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic.base import TemplateView, RedirectView from django.views.generic.base import TemplateView, RedirectView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from common.utils import FlashMessageUtil, static_or_direct from common.utils import FlashMessageUtil, static_or_direct, safe_next_url
from users.utils import ( from users.utils import (
redirect_user_first_login_or_index redirect_user_first_login_or_index
) )
@ -202,6 +202,7 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
auth_name, redirect_url = auth_method['name'], auth_method['url'] auth_name, redirect_url = auth_method['name'], auth_method['url']
next_url = request.GET.get('next') or '/' next_url = request.GET.get('next') or '/'
next_url = safe_next_url(next_url, request=request)
query_string = request.GET.urlencode() query_string = request.GET.urlencode()
redirect_url = '{}?next={}&{}'.format(redirect_url, next_url, query_string) redirect_url = '{}?next={}&{}'.format(redirect_url, next_url, query_string)

View File

@ -19,7 +19,7 @@ from common.sdk.im.wecom import URL
from common.sdk.im.wecom import WeCom from common.sdk.im.wecom import WeCom
from common.utils import get_logger from common.utils import get_logger
from common.utils.common import get_request_ip from common.utils.common import get_request_ip
from common.utils.django import reverse, get_object_or_none from common.utils.django import reverse, get_object_or_none, safe_next_url
from common.utils.random import random_string from common.utils.random import random_string
from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin
from users.models import User from users.models import User
@ -182,6 +182,7 @@ class WeComQRLoginView(WeComQRMixin, METAMixin, View):
def get(self, request: HttpRequest): def get(self, request: HttpRequest):
redirect_url = request.GET.get('redirect_url') or reverse('index') redirect_url = request.GET.get('redirect_url') or reverse('index')
next_url = self.get_next_url_from_meta() or reverse('index') next_url = self.get_next_url_from_meta() or reverse('index')
next_url = safe_next_url(next_url, request=request)
redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True) redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True)
redirect_uri += '?' + urlencode({ redirect_uri += '?' + urlencode({
'redirect_url': redirect_url, 'redirect_url': redirect_url,

View File

@ -7,6 +7,7 @@ from django.db import models
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.shortcuts import reverse as dj_reverse from django.shortcuts import reverse as dj_reverse
from django.utils import timezone from django.utils import timezone
from django.utils.http import url_has_allowed_host_and_scheme
UUID_PATTERN = re.compile(r'[0-9a-zA-Z\-]{36}') UUID_PATTERN = re.compile(r'[0-9a-zA-Z\-]{36}')
@ -94,3 +95,12 @@ def get_request_os(request):
return 'linux' return 'linux'
else: else:
return 'unknown' return 'unknown'
def safe_next_url(next_url, request=None):
safe_hosts = [*settings.ALLOWED_HOSTS]
if request:
safe_hosts.append(request.get_host())
if not next_url or not url_has_allowed_host_and_scheme(next_url, safe_hosts):
next_url = '/'
return next_url

View File

@ -11,7 +11,7 @@ from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from common.tasks import send_mail_async from common.tasks import send_mail_async
from common.utils import reverse, get_object_or_none, ip from common.utils import reverse, get_object_or_none, ip, safe_next_url
from .models import User from .models import User
logger = logging.getLogger('jumpserver.users') logger = logging.getLogger('jumpserver.users')
@ -49,6 +49,7 @@ def redirect_user_first_login_or_index(request, redirect_field_name):
url = request.POST.get(redirect_field_name) url = request.POST.get(redirect_field_name)
if not url: if not url:
url = request.GET.get(redirect_field_name) url = request.GET.get(redirect_field_name)
url = safe_next_url(url, request=request)
# 防止 next 地址为 None # 防止 next 地址为 None
if not url or url.lower() in ['none']: if not url or url.lower() in ['none']:
url = reverse('index') url = reverse('index')

View File

@ -30,7 +30,7 @@ class UserVerifyPasswordView(AuthMixin, FormView):
try: try:
password = form.cleaned_data['password'] password = form.cleaned_data['password']
except errors.AuthFailedError as e: except errors.AuthFailedError as e:
form.add_error("password", _(f"Password invalid") + f'({e.msg})') form.add_error("password", _("Password invalid") + f'({e.msg})')
return self.form_invalid(form) return self.form_invalid(form)
user = authenticate(request=self.request, username=user.username, password=password) user = authenticate(request=self.request, username=user.username, password=password)