2018-8-16JWTtoken用户登录认证思路分析9502751

2018-8-16JWTtoken用户登录认证思路分析9502751

JWT token在商城中的实现

class UserView(CreateAPIView):
    serializer_class = CreateUserSerializer
  • 创建用户对象的视图函数,调用序列化器中的create方法来实现用户的保存
class CreateUserSerializer(serializers.ModelSerializer):
    """
    创建用户序列化器具
    """
    password2 = serializers.CharField(label="确认密码", required=True, allow_null=False, allow_blank=False, write_only=True)
    sms_code = serializers.CharField(label="短信验证码", required=True, allow_null=False, allow_blank=False, write_only=True)
    allow = serializers.CharField(label="同意协议", required=True, allow_null=False, allow_blank=False, write_only=True)
    token = serializers.CharField(label='登录状态token', read_only=True)  # 增加token字段

    def validate_mobile(self, value):
        if not re.match(r"^1[3456789]\d{9}$", value):
            # if not re.match(r'^1[345789]\d{9}$', value):

            raise serializers.ValidationError("手机号码错误")
        return value

    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_conn = get_redis_connection("verify_codes")
        mobile = attrs["mobile"]
        real_sms_code = redis_conn.get("sms_code_%s" % mobile)
        if real_sms_code is None:
            raise serializers.ValidationError("无效的短信验证码")
        if real_sms_code.decode() != 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)

        user.set_password(validated_data["password"])
        user.save()
        # 为用户设置jwt验证字符串
        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

    class Meta:
        model = User
        fields = ('id', 'username', 'password', 'password2', 'sms_code', 'mobile', 'allow', 'token')
        extra_kwargs = {
            'id': {"read_only": True},
            "username": {
                "max_length": 20,
                "min_length": 5,
                "error_messages": {
                    "max_length": "仅允许5-20个字符的用户名",
                    "min_length": '仅允许5-20个字符的用户名',
                }
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

  • 序列化器中对应的model模型中没有的字段进行补充
    • 增加用户名的属性为只读
    • 对用户的密码进行限制
  • 对手机号码进行验证
  • 对协议进行验证
  • 对密码和短信验证码进行验证
  • 创建用户的对象保存进到数据库中
  • 将保存的用户对象返回给前端
  • 为用户设置jwt验证字符串
  • 在返回的用户对象中增加一个额外的字段token(此token字段是中载荷为user对象)

用户登录

    from rest_framework_jwt.views import obtain_jwt_token

    url(r'^authorizations/$', obtain_jwt_token, name='authorizations'),
  • 原生的这个登录的方法中,默认返回到回来的数据是token

  • 但是在前端页面中我们需要在用户信息中展示用户的基本信息username,user对象

  • 对源码的分析如下所示:

    • 用户发送过来的登录请求是post请求
    • 默认回去post方法中执行
    • 执行的流程中会去将数据库中的token与用户发送过来的token做对比返回
    • 默认返回的数据由JWT_RESPONSE_PAYLOAD_HANDLER来决定
    • 重写JWT_RESPONSE_PAYLOAD_HANDLER并在配置文件中重新配置接可以返回我们想要的数据
  • 源代码的执行流程如下

obtain_jwt_token = ObtainJSONWebToken.as_view()



class JSONWebTokenAPIView(APIView):


    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ObtainJSONWebToken(JSONWebTokenAPIView):
    """
    API View that receives a POST with a user's username and password.

    Returns a JSON Web Token that can be used for authenticated requests.
    """
    serializer_class = JSONWebTokenSerializer
    
# 默认情况下会去api_settings中加载这个函数
jwt_response_payload_handler = api_settings.JWT_RESPONSE_PAYLOAD_HANDLER


  • 重写的代码
def jwt_response_payload_handler(token, user=None, request=None):
    """
    自定义jwt认证成功返回数据
    """
    return {
        'token': token,
        'user_id': user.id,
        'username': user.username
    }

  • 在配置文件中进行配置
# JWT
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.utils.jwt_response_payload_handler',
}
posted @ 2018-08-19 21:26  cerofang  阅读(611)  评论(0编辑  收藏  举报