2014-03-31 48 views
12

我想创建一个REST API,并坚持用户注册:基本上我需要在注册之前有访问令牌。在视图中创建Django的rest框架权限

这是视图:

class UserViewSet(viewsets.ModelViewSet): 

    """ 
    API endpoint that allows users to be viewed or edited. 
    """ 
    queryset = User.objects.all() 
    serializer_class = UserSerializer 

    def metadata(self, request): 
     """ 
     Don't include the view description in OPTIONS responses. 
     """ 
     data = super(UserViewSet, self).metadata(request) 
     return data 

    def create(self, request): 
     serializer = self.get_serializer(data=request.DATA, files=request.FILES) 

     if serializer.is_valid(): 
      self.pre_save(serializer.object) 
      self.object = serializer.save(force_insert=True) 
      self.post_save(self.object, created=True) 
      self.object.set_password(self.object.password) 
      self.object.save() 
      headers = self.get_success_headers(serializer.data) 
      return Response(serializer.data, status=status.HTTP_201_CREATED, 
          headers=headers) 

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

这是解决方法:

@api_view(['POST']) 
@permission_classes((AllowAny,)) 
@csrf_exempt 
def create_auth(request, format=None): 
    data = JSONParser().parse(request) 
    serialized = UserSerializer(data=data) 

    if serialized.is_valid(): 
     user = User.objects.create_user(
      serialized.init_data['email'], 
      serialized.init_data['username'], 
      serialized.init_data['password'], 
     ) 
     user.groups = serialized.init_data['groups'] 

     user.save() 

     serialized_user = UserSerializer(user) 
     return Response(serialized_user.data, status=status.HTTP_201_CREATED, headers={"Access-Control-Allow-Origin": "http://127.0.0.1:8000/"}) 
    else: 
     return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST, headers={"Access-Control-Allow-Origin": "http://127.0.0.1:8000/"}) 

我的问题是:如何在UserViewSet指定为创建我不要求证书?或者指定一个自定义验证方法?我不想为整个视图集更改认证/权限类。

感谢, 阿迪

编辑 澄清:未注册用户应该被允许登记后的数据和不应该被允许别的。经过身份验证的用户可以获取用户列表并更新他们自己的配置文件...这是默认行为。这就是为什么AllowAny是而不是的一个选项。在我看来,适当的地方是创建函数,但我没有得到我应该重写。

回答

21

定制get_queryset方法:

def get_queryset(self): 
    if self.request.user.is_superuser: 
     return User.objects.all() 
    else: 
     return User.objects.filter(id=self.request.user.id) 

这样,经过认证的用户只能检索,修改或删除其自己的对象。

指定permission_classes = (AllowAny,),以便经过身份验证的用户可以创建一个新用户。

编辑:从意见

定制get_queryset方法这种方式进一步解释指:

  1. 是,未通过认证的用户可以发送GET请求来检索用户列表中,但它将为空,因为返回User.objects.filter(id = self.request.user.id)确保只返回有关经过身份验证的用户的信息。

  2. 同样的情况适用于其他方法,如果经过身份验证的用户试图删除另一个用户对象,则会显示:未找到将返回(因为它试图访问的用户不在查询集中)。

  3. 通过身份验证的用户可以对他们的用户对象执行任何他们想要的操作。

+0

感谢您的回复。在这种情况下,我将丢失所有权限检查和注册用户的内在功能,例如检索完整的用户列表。 – adi

+2

否,因为注册用户只能使用自己的对象而不使用其他对象,视图中的get_queryset方法可确保此行:return User.objects.filter(id = self.request.user.id)。 – argaen

+0

这允许未经过身份验证的用户获取用户列表...我不希望这样。我在原帖中增加了一些说明。 – adi

7

您可以利用Django REST框架定义自定义权限的能力。您可以在自定义类中指定has_permissionhas_object_permission。这会给你预期的行为,向匿名用户投掷403s,除了发布到创建端点之外。它看起来是这样的:

class IsAnonCreate(permissions.BasePermission): 
    def has_permission(self, request, view): 
     if request.method == "POST" and not request.user.is_authenticated(): 
      return True 
     elif not request.user.is_authenticated() and request.method != "POST": 
      return False 
     elif request.method in permissions.SAFE_METHODS: 
      return True 

     return False 

    def has_object_permission(self, request, view, obj): 
     if not request.user.is_authenticated(): 
      return False 
     if request.method in permissions.SAFE_METHODS: 
      return True 

     return obj.username == request.user.username 

然后,您可以添加一些自定义的处理通过验证的用户,如果你想。

然后,所有你需要做的就是允许类添加到您的ModelViewSet

class UserViewSet(viewsets.ModelViewSet): 

    """ 
    API endpoint that allows users to be viewed or edited. 
    """ 
    queryset = User.objects.all() 
    serializer_class = UserSerializer 
    permission_classes = (IsAnonCreate,) 
0

这是基于@argaen答案,它为我工作:

class UserViewSet(viewsets.ModelViewSet): 
    serializer_class = UserSerializer 
    permission_classes = (AllowAny,) 
    authentication_classes = (NoAuthentication,) 
    filter_backends = (filters.DjangoFilterBackend,) 
    filter_fields = ('id', 'email', 'name') 

    def get_queryset(self): 
     user = TokenAuthentication().authenticate(self.request) 
     if user is not None: 
      user = user[0] 
      if user.is_superuser: 
       return get_user_model().objects.all() 
      else: 
       return get_user_model().objects.filter(id=user.id) 

     return get_user_model().objects.none()