Django的传统开发模式中,用户提交登陆信息表单时,会携带一个{% csrf_token %}的Hidden标签提交到服务器,它会生成一段字符串,功能就是验证表单的安全性进而防止跨站请求伪造攻击。
而在DRF框架中,在BrowserAPI页面的用户登陆同样也是运用了这一机制,通过Django的LoginView函数实现CSRF的表单安全验证实现登陆。
代码示例如下:
urlpatterns = [
url(r'^api-auth/', include('rest_framework.urls')),
]
# rest_framework\urls.py
app_name = 'rest_framework'
urlpatterns = [
url(r'^login/$', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
]
目前大部分公司都用到了SPA的开发模式,所以这时候我们就不能使用LoginView函数来实现登陆验证逻辑。因为LoginView函数的功能逻辑中需要验证csrf_token的值,服务在返回的Html页面时,Django会自动在Hidden标签中注册一个csrf_code方法,然后页面再调用这个方法就会生成一串csrf_token值。在前后端分离的项目中,登陆就不需要做CSRF的验证,前端有可能是安卓、IOS来写的,前后端不在是同一个站点。说白了,当使用DRF框架的时候是根本不需要考虑CSRF验证问题的。
阅读drf的官方文档时我们可以发现,默认的配置位:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
打开这两个类的源码后,发现这两个类就是在Django的 'django.contrib.sessions.middleware.SessionMiddleware’和’django.contrib.auth.middleware.AuthenticationMiddleware’这两个中间件的基础上,把request中的user取出来了而已。下图是DRF框架的认证类核心源码:
DRF框架中这两个认证类的功能跟Django中的用户认证功能差不多一样,同样也都需要验证CSRF。对于前后端分离项目的需求来说,还是不行。不过DRF框架还给我们提供了一种用户认证的方案 TokenAuthentication (下面有请靓仔出场。。。)
第一步(配置):这种认证解决方案同样也是基于HTTP协议来完成的,多适用于客户端服务器端设置,例如本机桌面和移动客户端。要使用该TokenAuthentication方案,您需要配置要包含的身份验证类TokenAuthentication,并rest_framework.authtoken在您的INSTALLED_APPS设置中另外包含:
# settings.py
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication', # 提示出错,没有调用process_request方法
'rest_framework.authentication.TokenAuthentication',
),
}
注意:在settings.py中添加这个app后,它会帮我们在数据库中生成一张authtoken表,所以我们要确保manage.py migrate在更改设置后运行。该rest_framework.authtoken应用程序提供Django数据库迁移。
第二步(生成token值):使用TokenAuthentication时,REST框架提供了一个内置视图来提供此行为。要使用它,请将obtain_auth_token视图添加到URLconf:
#urls.py
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
]
这样配置的话,前端只需要把表单信息通过post的方式提交到这个url上,就会接收到一个响应值token。
例如 { ‘token’ : ‘9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b’ }
此时数据库中的token表也会生成对应的记录:
注意:用户在post提交表单信息的时候,drf框架后台拿到这些数据会去对应数据库做比对,比对成功后才会生成一段token的值返回给用户;如果账号密码错误的话,也会有响应的错误提示。
第三步(token验证):前两步做完之后,前端已经拿到了token的值,官方文档指出客户端在进行身份验证的时候,前端需要将token值封装在HTTP协议中的header中。token应以字符串文字“Token”为前缀,空格分隔两个字符串。
例如:Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
这样的话,我们在请求其他页面的时候,就会获取到request中的user的值:
注意:后台想要取出对应的token值,只需要在requset.auth中取出即可。
这样的话就大功告成啦,使用了DRF框架中的TokenAuthentication认证方案完成了用户登陆。这种登陆模式在前后端分离的项目中是非常常见的,比起Session机制登陆方便了许多,也不需要CSRF的验证,提高了开发效率。
最后再说一下DRF中TokenAuthentication认证方案的缺点:
1.用户向登陆时服务会创建一个token值返回给用户,同时也将这个token值保存到了服务器数据库表中,如果是一个分布式系统的话,想通过一套认证系统的token表用来验证用户登陆,就会出现问题。
2.authtoken表中存放的token值没有过期时间字段,如果token值一旦泄露,非常危险。
3.随着用户的增多,token值会占用服务器大量空间,同时也会加大数据库的查询压力,性能下降。
下一篇博客,会写到JWT(Json Web Token的登陆模式)