昨天本来想测试下权限验证到底访问了几次数据库,为了图方便,就单纯测试了一个GET获取用户列表的接口。结果测来测去,从SQL语句来看,似乎只是做了用户认证,压根没有权限的审查。
Query 1: SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
Query 2: SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 24 LIMIT 21
Query 3: SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user`
我想了下,是不是可能在这个接口之前或者某个地方,毕竟我对于这种网页后端服务开发的调试有点拿不准。然而加了个中间件测试下,结果还是这个样子,就是查询了三次。
Gemini最初说是因为用户超级管理员的问题。然而我新建了一个无权限的用户,居然发现还是能访问这个GET接口。我靠,这把我干懵了,意思是只要是个人随便建个用户,就能看到系统内所有的用户信息呗?
这时候Gemini还嘴硬,绕来绕去。我去问了GPT,GPT指出可能是DRF的DjangoModelPermissions没有考虑view的相关权限控制,因为Django 2.1之前似乎没有这个权限的控制,DRF也就没考虑。
我又去看了下,发现丫的还真没有……源码里还温馨提醒了“ # Override this if you need to also provide ‘view’ permissions”,简直绝了。
class DjangoModelPermissions(BasePermission):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that
provide a `.queryset` attribute.
"""
# Map methods into required permission codes.
# Override this if you need to also provide 'view' permissions,
# or if you want to provide custom permission codes.
perms_map = {
'GET': [], # 就这儿,是空的……
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
……
我们新建个类改一下,这样就好了:
class DjangoModelAddViewPermissions(DjangoModelPermissions):
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
这样实际就是四次查询了,没有权限的也会拒绝访问:
[SQL 查询次数] /permission/users/: 4
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 0.000
SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 24 LIMIT 21 0.000
SELECT `django_content_type`.`app_label` AS `content_type__app_label`, `auth_permission`.`codename` AS `codename` FROM `auth_permission` INNER JOIN `auth_user_user_perm
issions` ON (`auth_permission`.`id` = `auth_user_user_permissions`.`permission_id`) INNER JOIN `django_content_type` ON (`auth_permission`.`content_type_id` = `django_content_type`.`id`) WHERE `auth_user_user_permissions`.`user_id` = 24 0.001
SELECT `django_content_type`.`app_label` AS `content_type__app_label`, `auth_permission`.`codename` AS `codename` FROM `auth_permission` INNER JOIN `auth_group_permissi
ons` ON (`auth_permission`.`id` = `auth_group_permissions`.`permission_id`) INNER JOIN `django_content_type` ON (`auth_permission`.`content_type_id` = `django_content_t
ype`.`id`) WHERE `auth_group_permissions`.`group_id` IN (SELECT U0.`id` FROM `auth_group` U0 INNER JOIN `auth_user_groups` U1 ON (U0.`id` = U1.`group_id`) WHERE U1.`user_id` = 24) 0.000
Forbidden: /permission/users/
Good!