django 实现状态保持
1,状态保持使用DPF框架基础之上的一种扩展包来完成,不同于session与cookie 更加安全,也兼容了分布式集群
#在虚拟环境下安装 pip install djangorestframework-jwt
2,配置文件
REST_FRAMEWORK = { # 使用第三方扩展 djangorestframework-jwt 代替cookie 进行身份验证! 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), # 异常处理 'EXCEPTION_HANDLER': 'meiduo_mall.utils.exceptions.exception_handler', } JWT_AUTH = { # djangorestframework 扩展设置过期时间! 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 指向重写的类,增加了额外的功能(增加返回前端字段的字段类型!) 'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler', } # 自定义认证后端!可以增加邮箱登录,或者手机号登录,用户名登录等! # 前端 value 值会传参给 authenticate AUTHENTICATION_BACKENDS = [ 'users.utils.UsernameMobileAuthBackend', ]
3,使用扩展包(参考文档!)
#使用数字表明了扩展包的使用! class CreateUserSerializer(serializers.ModelSerializer): '''只需要验证密码手机号与是否同意即可!''' # 1,增加一个返回字段,用来返回记录token的值! token = serializers.CharField(label='登录状态token', read_only=True) # 增加token字段 password2 = serializers.CharField(label='同意', write_only=True) sms_code = serializers.CharField(label='手机验证码', max_length=6, min_length=6, write_only=True) allow = serializers.CharField(label='是否允许', write_only=True) class Meta(): # 为上述字段增加字段验证的功能! model = User # 2,指定模型类的那些字段生成! fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token') extra_kwargs = { 'username': { 'min_length': 5, 'max_length': 20, 'error_messages': { 'min_length': '仅允许5-20个字符的用户名', 'max_length': '仅允许5-20个字符的用户名', } }, 'password': { 'write_only': True, 'min_length': 8, 'max_length': 20, 'error_messages': { 'min_length': '仅允许8-20个字符的密码', 'max_length': '仅允许8-20个字符的密码', } } } # 手机号验证! 每次验证必须返回数据。 def validate_mobile(self, data): if not re.match(r'^1[3-9]\d{9}$', data): raise serializers.ValidationError('手机号不匹配') return data # 是否同意验证! def validate_allow(self, value): """检验用户是否同意协议""" if value != 'true': raise serializers.ValidationError('请同意用户协议') return value def validate(self, attrs): if attrs['password'] != attrs['password2']: raise serializers.ValidationError('两次密码不一致') redis_code = get_redis_connection('verify_codes') # 存储到redis之中是byte类型! real_sms_code = redis_code.get("sms_%s" % attrs['mobile']).decode() if real_sms_code is None: raise serializers.ValidationError('无效的短信验证码') if real_sms_code != attrs['sms_code']: raise serializers.ValidationError('请输入正确的手机验证码') return attrs # 创建用户! def create(self, validated_data): # 移除数据库不存在的属性! del validated_data['password2'] del validated_data['sms_code'] del validated_data['allow'] # 返回用户对象 user = super().create(validated_data) # 3,调用django的认证系统加密密码 user.set_password(validated_data['password']) user.save() # 设置 token 用来保持用户user状态的登录! jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) user.token = token return user
4,增加扩展功能!
import re from django.contrib.auth.backends import ModelBackend # 自定义返回前端数据! user 是指用户对象! token 是指!加密的随机值! from users.models import User def jwt_response_payload_handler(token, user=None, request=None): """ 自定义jwt认证成功返回数据 """ return { 'token': token, 'user_id': user.id, 'username': user.username } '''自定义认证后端,提供手机登录!''' class UsernameMobileAuthBackend(ModelBackend): # 接受前端传递参数!,并定义参数类型校验! def authenticate(self, request, username=None, password=None, **kwargs): try: if re.match(r'^1[3-9]\d{9}$', username): user = User.objects.get(mobile=username) else: user = User.objects.get(username=username) except: user = None if user is not None and user.check_password(password): return user