DRF chạy 2 lớp trước view: Authentication xác định bạn là ai (set request.user), Permission xác định bạn có được làm hành động này không.
# settings.py — default cho toàn project
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
}
# Override per view
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]Custom permission là class chỉ cần 1-2 method:
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author_id == request.user.idDRF gọi has_permission() cho list/create, và has_object_permission() cho retrieve/update/delete một cách tự động.
Hai chỗ dễ vấp: DEFAULT_PERMISSION_CLASSES áp dụng cho mọi viewset không khai báo riêng, nên đặt IsAuthenticated làm default an toàn hơn AllowAny rất nhiều. Và has_object_permission chỉ chạy khi bạn gọi self.get_object() — nếu truy cập obj qua custom method, phải tự gọi self.check_object_permissions(request, obj), không thì lỗi bảo mật lặng lẽ trôi qua.
DRF runs two layers before the view: Authentication decides who you are (sets request.user), Permission decides whether you can perform this action.
# settings.py — project-wide defaults
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
}
# Per-view override
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]A custom permission is a class with one or two methods:
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author_id == request.user.idDRF calls has_permission() (for list/create) and has_object_permission() (for retrieve/update/delete) automatically.
Pitfall: Every entry in DEFAULT_PERMISSION_CLASSES applies to every viewset that does not declare its own — defaulting to IsAuthenticated is safer than AllowAny. Also, has_object_permission only fires when you call self.get_object(); access through a custom method requires manual self.check_object_permissions(request, obj).