perf: Client login

pull/14684/head
feng 2024-12-18 17:25:40 +08:00 committed by feng626
parent 3cd68ba0a9
commit 7a9a71197a
17 changed files with 83 additions and 29 deletions

View File

@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
#
from django.urls import path
import django_cas_ng.views
from django.urls import path
from .views import CASLoginView
from .views import CASLoginView, CASCallbackClientView
urlpatterns = [
path('login/', CASLoginView.as_view(), name='cas-login'),
path('logout/', django_cas_ng.views.LogoutView.as_view(), name='cas-logout'),
path('callback/', django_cas_ng.views.CallbackView.as_view(), name='cas-proxy-callback'),
path('login/client', CASCallbackClientView.as_view(), name='cas-proxy-callback-client'),
]

View File

@ -1,9 +1,12 @@
from django_cas_ng.views import LoginView
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect
from django.views.generic import View
from django_cas_ng.views import LoginView
__all__ = ['LoginView']
from authentication.views.utils import redirect_to_guard_view
class CASLoginView(LoginView):
def get(self, request):
@ -13,3 +16,8 @@ class CASLoginView(LoginView):
return HttpResponseRedirect('/')
class CASCallbackClientView(View):
http_method_names = ['get', ]
def get(self, request):
return redirect_to_guard_view(query_string='next=client')

View File

@ -4,9 +4,9 @@ from django.urls import path
from . import views
urlpatterns = [
path('login/', views.OAuth2AuthRequestView.as_view(), name='login'),
path('callback/', views.OAuth2AuthCallbackView.as_view(), name='login-callback'),
path('callback/client/', views.OAuth2AuthCallbackClientView.as_view(), name='login-callback-client'),
path('logout/', views.OAuth2EndSessionView.as_view(), name='logout')
]

View File

@ -1,16 +1,16 @@
from django.views import View
from django.conf import settings
from django.contrib import auth
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.http import urlencode
from django.views import View
from authentication.mixins import authenticate
from authentication.utils import build_absolute_uri
from authentication.views.mixins import FlashMessageMixin
from authentication.mixins import authenticate
from authentication.views.utils import redirect_to_guard_view
from common.utils import get_logger
logger = get_logger(__file__)
@ -67,6 +67,13 @@ class OAuth2AuthCallbackView(View, FlashMessageMixin):
return HttpResponseRedirect(redirect_url)
class OAuth2AuthCallbackClientView(View):
http_method_names = ['get', ]
def get(self, request):
return redirect_to_guard_view(query_string='next=client')
class OAuth2EndSessionView(View):
http_method_names = ['get', 'post', ]

View File

@ -12,9 +12,9 @@ from django.urls import path
from . import views
urlpatterns = [
path('login/', views.OIDCAuthRequestView.as_view(), name='login'),
path('callback/', views.OIDCAuthCallbackView.as_view(), name='login-callback'),
path('callback/client/', views.OIDCAuthCallbackClientView.as_view(), name='login-callback-client'),
path('logout/', views.OIDCEndSessionView.as_view(), name='logout'),
]

View File

@ -22,13 +22,14 @@ from django.http import HttpResponseRedirect, QueryDict
from django.urls import reverse
from django.utils.crypto import get_random_string
from django.utils.http import urlencode
from django.views.generic import View
from django.utils.translation import gettext_lazy as _
from django.views.generic import View
from authentication.utils import build_absolute_uri_for_oidc
from authentication.views.mixins import FlashMessageMixin
from common.utils import safe_next_url
from .utils import get_logger
from ...views.utils import redirect_to_guard_view
logger = get_logger(__file__)
@ -208,6 +209,13 @@ class OIDCAuthCallbackView(View, FlashMessageMixin):
return HttpResponseRedirect(settings.AUTH_OPENID_AUTHENTICATION_FAILURE_REDIRECT_URI)
class OIDCAuthCallbackClientView(View):
http_method_names = ['get', ]
def get(self, request):
return redirect_to_guard_view(query_string='next=client')
class OIDCEndSessionView(View):
""" Allows to end the session of any user authenticated using OpenID Connect.

View File

@ -4,10 +4,10 @@ from django.urls import path
from . import views
urlpatterns = [
path('login/', views.Saml2AuthRequestView.as_view(), name='saml2-login'),
path('logout/', views.Saml2EndSessionView.as_view(), name='saml2-logout'),
path('callback/', views.Saml2AuthCallbackView.as_view(), name='saml2-callback'),
path('callback/client/', views.Saml2AuthCallbackClientView.as_view(), name='saml2-callback-client'),
path('metadata/', views.Saml2AuthMetadataView.as_view(), name='saml2-metadata'),
]

View File

@ -19,6 +19,7 @@ from onelogin.saml2.idp_metadata_parser import (
from authentication.views.mixins import FlashMessageMixin
from common.utils import get_logger
from .settings import JmsSaml2Settings
from ...views.utils import redirect_to_guard_view
logger = get_logger(__file__)
@ -298,6 +299,13 @@ class Saml2AuthCallbackView(View, PrepareRequestMixin, FlashMessageMixin):
return super().dispatch(*args, **kwargs)
class Saml2AuthCallbackClientView(View):
http_method_names = ['get', ]
def get(self, request):
return redirect_to_guard_view(query_string='next=client')
class Saml2AuthMetadataView(View, PrepareRequestMixin):
def get(self, request):

View File

@ -110,6 +110,9 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, IMClientMixin, View):
msg = e.msg
response = self.get_failed_response(login_url, title=msg, msg=msg)
return response
if 'next=client' in redirect_url:
self.request.META['QUERY_STRING'] += '&next=client'
return self.redirect_to_guard_view()

View File

@ -175,6 +175,8 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View):
def get(self, request: HttpRequest):
redirect_url = request.GET.get('redirect_url') or reverse('index')
query_string = request.GET.urlencode()
redirect_url = f'{redirect_url}?{query_string}'
next_url = self.get_next_url_from_meta() or reverse('index')
next_url = safe_next_url(next_url, request=request)

View File

@ -110,6 +110,8 @@ class FeiShuQRLoginView(FeiShuQRMixin, View):
def get(self, request: HttpRequest):
redirect_url = request.GET.get('redirect_url') or reverse('index')
query_string = request.GET.urlencode()
redirect_url = f'{redirect_url}?{query_string}'
redirect_uri = reverse(f'authentication:{self.category}-qr-login-callback', external=True)
redirect_uri += '?' + urlencode({
'redirect_url': redirect_url,

View File

@ -45,69 +45,70 @@ class UserLoginContextMixin:
error_origin: str
def get_support_auth_methods(self):
query_string = self.request.GET.urlencode()
auth_methods = [
{
'name': 'OpenID',
'enabled': settings.AUTH_OPENID,
'url': reverse('authentication:openid:login'),
'url': f"{reverse('authentication:openid:login')}?{query_string}",
'logo': static('img/login_oidc_logo.png'),
'auto_redirect': True # 是否支持自动重定向
},
{
'name': 'CAS',
'enabled': settings.AUTH_CAS,
'url': reverse('authentication:cas:cas-login'),
'url': f"{reverse('authentication:cas:cas-login')}?{query_string}",
'logo': static('img/login_cas_logo.png'),
'auto_redirect': True
},
{
'name': 'SAML2',
'enabled': settings.AUTH_SAML2,
'url': reverse('authentication:saml2:saml2-login'),
'url': f"{reverse('authentication:saml2:saml2-login')}?{query_string}",
'logo': static('img/login_saml2_logo.png'),
'auto_redirect': True
},
{
'name': settings.AUTH_OAUTH2_PROVIDER,
'enabled': settings.AUTH_OAUTH2,
'url': reverse('authentication:oauth2:login'),
'url': f"{reverse('authentication:oauth2:login')}?{query_string}",
'logo': static_or_direct(settings.AUTH_OAUTH2_LOGO_PATH),
'auto_redirect': True
},
{
'name': _('WeCom'),
'enabled': settings.AUTH_WECOM,
'url': reverse('authentication:wecom-qr-login'),
'url': f"{reverse('authentication:wecom-qr-login')}?{query_string}",
'logo': static('img/login_wecom_logo.png'),
},
{
'name': _('DingTalk'),
'enabled': settings.AUTH_DINGTALK,
'url': reverse('authentication:dingtalk-qr-login'),
'url': f"{reverse('authentication:dingtalk-qr-login')}?{query_string}",
'logo': static('img/login_dingtalk_logo.png')
},
{
'name': _('FeiShu'),
'enabled': settings.AUTH_FEISHU,
'url': reverse('authentication:feishu-qr-login'),
'url': f"{reverse('authentication:feishu-qr-login')}?{query_string}",
'logo': static('img/login_feishu_logo.png')
},
{
'name': 'Lark',
'enabled': settings.AUTH_LARK,
'url': reverse('authentication:lark-qr-login'),
'url': f"{reverse('authentication:lark-qr-login')}?{query_string}",
'logo': static('img/login_lark_logo.png')
},
{
'name': _('Slack'),
'enabled': settings.AUTH_SLACK,
'url': reverse('authentication:slack-qr-login'),
'url': f"{reverse('authentication:slack-qr-login')}?{query_string}",
'logo': static('img/login_slack_logo.png')
},
{
'name': _("Passkey"),
'enabled': settings.AUTH_PASSKEY,
'url': reverse('api-auth:passkey-login'),
'url': f"{reverse('api-auth:passkey-login')}?{query_string}",
'logo': static('img/login_passkey.png')
}
]
@ -220,7 +221,7 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
'redirect_url': redirect_url,
'interval': 3,
'has_cancel': True,
'cancel_url': reverse('authentication:login') + '?admin=1'
'cancel_url': reverse('authentication:login') + f'?admin=1&{query_string}'
}
redirect_url = FlashMessageUtil.gen_message_url(message_data)
return redirect_url
@ -306,6 +307,7 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView):
login_url = reverse_lazy('authentication:login')
login_mfa_url = reverse_lazy('authentication:login-mfa')
login_confirm_url = reverse_lazy('authentication:login-wait-confirm')
query_string = True
def format_redirect_url(self, url):
args = self.request.META.get('QUERY_STRING', '')

View File

@ -2,13 +2,14 @@
#
from __future__ import unicode_literals
from django.views.generic.edit import FormView
from django.shortcuts import redirect, reverse
from django.views.generic.edit import FormView
from common.utils import get_logger
from users.views import UserFaceCaptureView
from .. import forms, errors, mixins
from .utils import redirect_to_guard_view
from .. import forms, errors, mixins
from ..const import MFAType
logger = get_logger(__name__)
@ -48,7 +49,8 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
self._do_check_user_mfa(code, mfa_type)
user, ip = self.get_user_from_session(), self.get_request_ip()
MFABlockUtils(user.username, ip).clean_failed_count()
return redirect_to_guard_view('mfa_ok')
query_string = self.request.GET.urlencode()
return redirect_to_guard_view('mfa_ok', query_string)
except (errors.MFAFailedError, errors.BlockMFAError) as e:
form.add_error('code', e.msg)
return super().form_invalid(form)

View File

@ -98,6 +98,8 @@ class SlackQRLoginView(SlackMixin, View):
def get(self, request: Request):
redirect_url = request.GET.get('redirect_url') or reverse('index')
query_string = request.GET.urlencode()
redirect_url = f'{redirect_url}?{query_string}'
redirect_uri = reverse('authentication:slack-qr-login-callback', external=True)
redirect_uri += '?' + urlencode({
'redirect_url': redirect_url,

View File

@ -1,8 +1,18 @@
# -*- coding: utf-8 -*-
#
from urllib.parse import urlencode, parse_qsl
from django.shortcuts import reverse, redirect
def redirect_to_guard_view(comment=''):
continue_url = reverse('authentication:login-guard') + '?_=' + comment
def redirect_to_guard_view(comment='', query_string=None):
params = {'_': comment}
base_url = reverse('authentication:login-guard')
if query_string:
params.update(dict(parse_qsl(query_string)))
query_string = urlencode(params)
continue_url = f"{base_url}?{query_string}"
return redirect(continue_url)

View File

@ -22,7 +22,6 @@ from users.views import UserVerifyPasswordView
from .base import BaseLoginCallbackView, BaseBindCallbackView
from .mixins import METAMixin, FlashMessageMixin
logger = get_logger(__file__)
@ -86,6 +85,8 @@ class WeComQRBindView(WeComQRMixin, View):
def get(self, request: HttpRequest):
redirect_url = request.GET.get('redirect_url')
query_string = request.GET.urlencode()
redirect_url = f'{redirect_url}?{query_string}'
redirect_uri = reverse('authentication:wecom-qr-bind-callback', external=True)
redirect_uri += '?' + urlencode({'redirect_url': redirect_url})

View File

@ -9,7 +9,6 @@ import time
import pyotp
from django.conf import settings
from django.contrib.auth import logout as auth_logout
from django.core.cache import cache
from django.utils.translation import gettext as _
@ -75,7 +74,6 @@ def redirect_user_first_login_or_index(request, redirect_field_name):
if url == 'client':
url = get_redirect_client_url(request)
auth_logout(request)
url = safe_next_url(url, request=request)
# 防止 next 地址为 None