drf使用jwt做验证
之前做网站是用django自带的登录,在'django.contrib.auth.urls下有登录、注销等等Django已经提供好的。
urlpatterns = [
url(r'^login/$', views.LoginView.as_view(), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
url(r'^password_change/$', views.PasswordChangeView.as_view(), name='password_change'),
url(r'^password_change/done/$', views.PasswordChangeDoneView.as_view(), name='password_change_done'),
url(r'^password_reset/$', views.PasswordResetView.as_view(), name='password_reset'),
url(r'^password_reset/done/$', views.PasswordResetDoneView.as_view(), name='password_reset_done'),
url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
url(r'^reset/done/$', views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]
但现在做前后端分离的接口的登录就需要自己来写了。
session与JSON Web Token区别
在做学校三级项目时我们是将用户通过接口登录后,生成sessionid存到redis里 userid:sessionid。然后sessionid返回给用户,用户在之后进行需要验证身份的操作时带上sessionid和userid,后端验证这个sessionid是否与redis中的sessionid是否相同。
Token和session其实都是为了身份验证,session需要我们进行存贮,用于日后比对,而Token不用。
Token是使用算法的方式进行验证,就不需要进行往数据库里存了。
drf自带Token与JSON Web Token区别
drf 自带的token也是新建一张Token表,里面存了用户和随机值Token的对应关系,和三级项目数据库里存sessionid没什么区别,只不过drf的token要放在请求header里,普通接口的sessionid放在get或post请求中。
使用 django-rest-framework-jwt
django有现成的jwt轮子 django-rest-framework-jwt,可以拿来使用。
pip install djangorestframework-jwt
安装后设置权限认证
设置PERMISSION_CLASSES和AUTHENTICATION_CLASSES
可以在setting中全局设置进行
REST_FRAMEWORK = {
# 权限认证
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
# 身份验证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
也可以在指定的ViewSet下设置权限,比如对商品信息做权限认证,需登录后才能获得信息。
class GoodsViewSet(viewsets.ReadOnlyModelViewSet):
"""
商品列表
商品详情
"""
queryset = Goods.objects.all()
# 序列化
serializer_class = GoodsSerializer
# 分页
pagination_class = StandardResultsSetPagination
# 过滤
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_class = GoodsFilter
ordering_fields = ('add_time', )
search_fields = ('name', 'goods_desc')
#Token认证
permission_classes = (IsAuthenticated,)
添加url
from rest_framework_jwt.views import obtain_jwt_token
#...
urlpatterns = [
'',
# ...
url(r'^api-token-auth/', obtain_jwt_token),
]
此时直接访问http://127.0.0.1:8000/goods/返回
{
"detail": "身份认证信息未提供。"
}
获取token
$ curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"password123"}' http://localhost:8000/api-token-auth/
返回
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImhlaGVjYXQiLCJleHAiOjE1MzIxNTIwNzUsImVtYWlsIjoicXdlQHF3ZS5xcSJ9.3zLJFaMjY8q3VC3aUdy5N79sbWelse0sSfp5yKOaNUs"}
访问需权限的url
$ curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImhlaGVjYXQiLCJleHAiOjE1MzIxNTIwNzUsImVtYWlsIjoicXdlQHF3ZS5xcSJ9.3zLJFaMjY8q3VC3aUdy5N79sbWelse0sSfp5yKOaNUs" http://localhost:8000/goods/
修改登录验证
登录默认 使用django的ModelBackend,对用户名和密码进行验证。但我们平时登录网站时除了用户名也可以用邮箱或手机进行登录,这就需要我们自己扩展backend。
AUTHENTICATION_BACKENDS = {
'django.contrib.auth.backends.ModelBackend',
}
新建backend
users.views中新建backend,对用户名或手机进行验证
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
User = get_user_model()
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username) | Q(mobile=username))
if user.check_password(password):
return user
except Exception as e:
return None
设置认证backend
settings 中添加
AUTHENTICATION_BACKENDS = {
'users.views.CustomBackend'
}