【问题标题】:Overriding simple-jwt's TokenObtainPairSerializer to implement 2FA覆盖 simple-jwt 的 TokenObtainPairSerializer 来实现 2FA
【发布时间】:2021-06-23 05:20:00
【问题描述】:

我目前正在尝试在我的 Django 应用程序中实现 2FA。我做的第一件事是修改UserSerializer类中的Meta类,添加两个字段enabled(表示是否为用户启用了2FA)和secret_key(生成OTP的关键,即在用户启用 2FA 时共享给用户)。

为了最小化修改登录流程,我修改了发送的表单以生成访问令牌,以包含一个新字段“otp”。用户可以填写与否,后端会检查用户是否开启了2FA,如果开启了OTP是否正确。

如果没有 2FA,登录只是一个带有正文 {"username": usr, "password": pwd} 的 POST 请求。这已成为带有正文 {"username": usr, "password": pwd, "otp": otp} 的 POST 请求。如果用户用户尚未启用 2FA,他只需将 opt 字段留空即可。

我的urls.py 看起来像这样:

path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair")

我的想法是重写 TokenObtainPairView 以适应新的请求。根据我的发现,我必须更改 validate 方法,但我真的不知道如何做到这一点。我可能必须获取用户的 enabledsecret_key 字段的值(基于用户名)以生成 OTP(如果相关)并对照 otp 字段进行检查。问题是,我不知道该怎么做,而且我在 simple-jwt 实现中有点迷失了。

【问题讨论】:

    标签: python django django-rest-framework jwt


    【解决方案1】:

    首先,我不会为您的问题提供完整的解决方案,但这可能是一个好的开始。

    创建您的自定义LoginView

    class LoginView(TokenObtainPairView):
        serializer_class = LoginSerializer
    

    实现你自己的继承TokenObtainPairSerializer的序列化器:

    class LoginSerializer(TokenObtainPairSerializer):
        def validate(self, attrs):
            # implement your logic here
            # data = super().validate(attrs)
            return data
    

    更改 urls.py

    path("api/token/", LoginView.as_view(), name="token_obtain_pair")
    

    这就是 TokenObtainSerializer 的样子:

    class TokenObtainSerializer(serializers.Serializer):
        username_field = User.USERNAME_FIELD
    
        default_error_messages = {
            'no_active_account': _('No active account found with the given credentials')
        }
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            self.fields[self.username_field] = serializers.CharField()
            self.fields['password'] = PasswordField()
    
        def validate(self, attrs):
            authenticate_kwargs = {
                self.username_field: attrs[self.username_field],
                'password': attrs['password'],
            }
            try:
                authenticate_kwargs['request'] = self.context['request']
            except KeyError:
                pass
    
            self.user = authenticate(**authenticate_kwargs)
    
            if not getattr(login_rule, user_eligible_for_login)(self.user):
                raise exceptions.AuthenticationFailed(
                    self.error_messages['no_active_account'],
                    'no_active_account',
                )
    
            return {}
    

    所以你可以在你自己的序列化器中实现__init___,并添加你的otp字段并在validate方法中实现你想要的逻辑(你可以访问self.user在这里检查他是否启用了2FA)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 2015-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-25
      • 2023-03-21
      相关资源
      最近更新 更多