python 覆盖simple-jwt的TokenObainPairSerializer以实现2FA

jjjwad0x  于 2023-02-18  发布在  Python
关注(0)|答案(1)|浏览(206)

目前我正在Django应用中尝试实现双因素认证,我做的第一件事是修改UserSerializer类中的Meta类,添加两个字段enabled(表示用户是否启用了双因素认证)和secret_key(生成OTP的密钥,当用户启用双因素认证时共享给用户)。
为了最小限度地修改登录流程,我修改了发送来生成访问令牌的表单,使其包含一个新字段“OTP”。用户可以填写或不填写该字段,后端将检查用户是否启用了2FA,如果启用了,则检查OTP是否正确。
如果没有2FA,登录只是一个主体为{"username": usr, "password": pwd}的POST请求。这已经成为一个主体为{"username": usr, "password": pwd, "otp": otp}的POST请求。如果用户user没有启用2FA,他可以简单地将opt字段留空。
我的urls.py看起来像这样:

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

我的想法是重写TokenObtainPairView以适应新的请求。根据我的发现,我必须更改validate方法,但我并不知道如何执行此操作,我可能需要获取用户的enabledsecret_key字段的值(基于用户名)来生成OTP(如果相关),并根据otp字段检查它。问题是,我不知道如何做到这一点,我在简单的jwt实现中有点迷失。

dffbzjpn

dffbzjpn1#

首先,我不会提供你的问题的全部解决方案,但这可能是一个好的开始。
创建自定义登录视图

class LoginView(TokenObtainPairView):
    serializer_class = LoginSerializer

实现您自己的序列化程序,该程序继承TokenObainPairSerializer

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

更改网址.py

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

令牌获取序列化程序如下所示:

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)。

相关问题