token的使用基于Python的登录注册功能

使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:

  • 客户端使用用户名跟密码请求登录
  • 服务端收到请求,去验证用户名与密码
  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

1.jwt的数据返回设置

重写jwt_response_payload_handler 可以实现 返回token和其他数据

登录成功后 查看调试工具中Resource 中 localstorage ,其中会存放token


    ''' utils/users.py '''
    
    # user 就是我们认证之后的user

    def jwt_response_payload_handler(token, user=None, request=None):

        return {
            'token':token,
            'username':user.username,
            'user_id':user.id
        }

    
    ''' settings.py'''
    
	REST_FRAMEWORK = {
	    'DEFAULT_AUTHENTICATION_CLASSES': (
	        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
	        'rest_framework.authentication.SessionAuthentication',
	        'rest_framework.authentication.BasicAuthentication',
	    ),
	}

    # 设置jwt 返回数据的函数
    JWT_AUTH = {
        'JWT_RESPONSE_PAYLOAD_HANDLER':'utils.user.jwt_response_payload_handler'
    }

       

2.注册成功之后返回token

笔者出现过的bug:在create方法中没有给user对象添加token字段时,注册账号,之后在create方法中添加token字段后,用该账号登录时,其对象并没有token字段, 所以会请求不到token。

确认注册完成后 生成token
即在数据入库后 注册完成 生成token
点击注册完成后 触发的一系列事件
其中在create中 save入库后 即注册完成

生成token 的代码:


    def create():

        ...

        # 此处注意导包  很容易导入错误的包
        from rest_framework_jwt.settings import api_settings

        # 需要获取2个方法
        jwt_payload_handler = api.settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api.settings.JWT_ENCODE_HANDLER

        # 1.jwt_payload_handler需要将用户的信息传递给这个函数,函数自己会获取user中的数据
        #   获取之后,会生成一个字符串
        payload = jwt_payload_handler(user)
        # 2.需要对payload 进行编码  编码之后的才是token
        token = jwt_encode_handler(payload)

        # 序列化的时候返回token
        # 序列化器中有该字段  但模型类中没有这个字段
        # 所以动态添加属性
        user.token = token


        return user


    ######针对上面  user.token = token  举的例子   #########

    def Person(object):
        name = ''
    
    p = Person()
    p.name = 'it'
    print(p.name)

    # 这个可以打印  动态添加属性  只对当前对象起作用
    p.age = 20
    print(p.age)

    # 这个不能打印
    p2 = Person()
    print(p2.age)






    
    ''' serializer.py  ''''

    class RegisterCreateSerializer(serializers.ModelSerializer):

        ...

        # 添加新字段
        token = serializers.CharField(label='token',read_only=True)
        # token 只是应用于 序列化(对象转换为JSON)操作的时候使用,所以只能读  需要添加read_only
        # 不加read_only会报错  提示 token字段为必填项
        # read_only 表示只在序列化的时候该字段起作用
        class Meta:
                model = Users
                fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token')  # 增加token
       


	''' js/register.js '''

在前端文件中增加保存token


	var vm = new Vue({
	    ...
	    methods: {
	        on_submit: function(){
	            axios.post(...)
	                .then(response => {
	                    // 记录用户的登录状态
	                    sessionStorage.clear();
	                    localStorage.clear();
	                    localStorage.token = response.data.token;
	                    localStorage.username = response.data.username;
	                    localStorage.user_id = response.data.id;
	                    location.href = '/index.html';
	                })
	                .catch(...)
	        }
	    }
	})

3.多账号登录 认证

增加支持用户名与手机号均可作为登录账号

JWT扩展的登录视图,在收到用户名与密码时,也是调用Django的认证系统中提供的authenticate()来检查用户名与密码是否正确。

我们可以通过修改Django认证系统的认证后端(主要是authenticate方法)来支持登录账号既可以是用户名也可以是手机号。

修改Django认证系统的认证后端需要继承django.contrib.auth.backends.ModelBackend,并重写authenticate方法。

authenticate(self, request, username=None, password=None, **kwargs)方法的参数说明:

request 本次认证的请求独享
username 本次认证提供的用户账号
password 本次认证提供的密码
我们想要让用户既可以以用户名登录,也可以以手机号登录,那么对于authenticate方法而言,username参数即表示用户名或者手机号。

重写authenticate方法的思路:

根据username参数查找用户User对象,username参数可能是用户名,也可能是手机号

若查找到User对象,调用User对象的check_password方法检查密码是否正确


    ''' utils/users.py '''

    def get_user_by_account(username):
        # 根据用户输入的信息  判断用户输入的是手机号还是用户名
        try:
            if re.match('1[3-9]\d{9}',username):
                # 用户名满足手机号的规则
                user = Users.objects.get(mobile=username)
            else:
                user = Users.objects.get(username=username)
        except Users.DoesNotExist:
            user = None
        
        return user

    class UsernameMobileAuthModelBackend(ModelBackend):

        def authenticate(self,request,username=None,password=None,**kwargs):
            
            # 1. 先判断输入类型  再进行查询
            user = get_user_by_account(username)

            # 2. 校验
            if user is not None and user.check_password(password):

                return user
            
            return None

    class SettingsBackend(object):
        """
        Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

        Use the login name and a hash of the password. For example:

        ADMIN_LOGIN = 'admin'
        ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
        """

        def authenticate(self, request, username=None, password=None):
            # 1. 根据用户在用户名的地方输入的内容进行判断,如果用户输入的是手机号,我们就根据手机号查询用户
            # 否则就根据 用户名进行查询
            user = get_user_by_account(username)

            # 2如果用户查询出来,我们再校验密码
            if user is not None and user.check_password(password):
                return user
            return None

        def get_user(self, user_id):
            try:
                return Users.objects.get(pk=user_id)
            except Users.DoesNotExist:
                return None

需要改变认证后端的类
在配置文件中告知Django使用我们自定义的认证后端


    '''settings.py '''

    # 改变认证后端的类
    AUTHENTICATION_BACKENDS = [
        # 'utils.users.UsernameMobileAuthModelBackend',
        'utils.users.SettingsBackend'
    ]

        # 不加 这个 会报 
        # {
        #   "non_field_errors": [
        #     "无法使用提供的认证信息登录。"
        #   ]
        # }

Django REST framework JWT提供了登录获取token的视图,可以直接使用,在users应用中的urls添加路由信息

  
    ''' urls.py '''
    
    from rest_framework_jwt.views import obtain_jwt_token
  
    urlpatterns = [
        #添加登录认证
        url(r'^auths/',obtain_jwt_token),
    ]



你可能感兴趣的:(Django)