当前位置: 首页 > news >正文

python调用api做用户登录认证_Python构建RESTful网络服务[Django篇:用户接入控制,认证与权限]...

系列文章介绍

前置步骤

前置步骤见专栏系列文章或项目代码Step-1:RESTful与Django

Step-2:创建项目和应用

Step-3:使用原生Django编写API

Step-4:序列化与反序列化

Step-5:基于DRF的视图类的视图

Step-6:基于DRF的视图集的视图

Step-7:用户接入控制

内容提要DRF提供的三种认证方式

编写用户注册视图

编写用户登录视图

编写用户模型序列化器

使用API登录

用登录了的用户发起请求

到此为止我们编写的API,都是完全开放的,也就是说任何人都可以对API发起请求,服务器也会返回相应的响应。这一节,我们将对API的接入进行一定的限定,要求注册用户才能使用API。

认证方式

Django REST Framework 提供了三种认证方式:rest_framework.authentication 模块。 BaseAuthentication及其子类提供了以用户名与密码进行认证的方式。如果我们要使用这种方式,我们要确保我们是在非生产环境或处于HTTPS协议。

SessionAuthentication: 使用Django的session认证框架。

TokenAuthentication: 提供用于认证的简单token。每个用户有一个token,发起请求时,必须在headers中携带token信息。token在headers中形如{Authorization: Token your-token}。

本文采集第三种方式进行认证。希望在以后的文章中,能讨论其它认证方式及Json-Web-Token(JWT)认证。

配置DRF用户接入控制应用

我们新建一个应用,用来管理用户接入。

$ python manage.py startapp useraccesscontrol

在项目的settings.py中,添加如下代码,进行应用定义和DRF配置:

# in settings.py

... ...

# 定义应用

REST_FRAMEWORK_APPS = [

'rest_framework', # DRF

'rest_framework.authtoken', # 认证应用

]

CUSTOM_APPS = [

'polls', # 投票应用

'useraccesscontrol', # 用户接入控制;

]

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

]\

+ REST_FRAMEWORK_APPS\

+ CUSTOM_APPS

... ...

# 配置DRF

REST_FRAMEWORK = {

# 配置全局认证方式:使用token或session

'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.TokenAuthentication',

'rest_framework.authentication.SessionAuthentication',

),

# 配置全局权限限制方式:是否经过了认证

'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',

),

}

编写用户注册视图

用户注册比较简单,思路无非是处理一个post请求,接收用户名与密码,将用户信息写入User模型对应的表中即可。在目前的权限配置中,未登录的用户,所有视图都无法访问。没登录就不能注册,显然是不合理的,所以注册视图应该应该从全局权限控制中豁免出去。另外,由于我们使用的是token认证,所以在创建用户时,要为创建的用户分配一个token,所以保存User实例时,还要同时保存一个与该user相关的Token实例。以上就是编写用户注册视图的思路。

另外要注意的是,任何时候用户密码都不应该能被取到,我们把这一点放到序列化器中处理。

编写视图

在应用目录下,新建一个apiview.py,编写如下代码:

# in useraccesscontrol/apiview.py

# 导入Django原生的User模型

from django.contrib.auth.models import User

from rest_framework import generics

from .serializers import UserSerializer # 这个序列化器暂时还没有编写

# 编写创建用户的视图

class UserCreate(generics.CreateAPIView):

name = 'user_create'

# 配置要使用的认证类,可以用如下方式配置(类属性方式)

# authentication_classes = ()

# 也可以用如下方式配置(实例方法)

def get_authenticators(self):

return () # 返回一个空的元组,表是从全局认证方式中豁免

# 配置要使用的权限为,可以使用类属性,也可以使用方法

# permission_classes = ()

def get_permissions(self):

return () # 返回一个空的元组,表是从全局认证方式中豁免

def get_serializer_class(self):

return UserSerializer # 这个序列化器暂时还没有编写

代码解释见代码中的注释。

我们还可以编写拉取所有用户信息的视图(List, Retrieve),读者可以自行编写。

编写序列化器

接下来我们来编写序列化器,新建 serializers.py,编写如下代码:

# in useraccesscontrol/serializers.py

from rest_framework import serializers

from rest_framework.authtoken.models import Token # 导入DRF为我们提供的Token模型

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):

"""用户相关视图的序列化器"""

class Meta:

model = User # 选定模型

fields = ('pk', 'username', 'email', 'password') # 选定字段

extra_kwargs = {

'password': {'write_only': True},

} # 不返回密码字段

def create(self, validated_data):

# 重写ModelSerializer的create()方法以保存User实例

user = User(

email=validated_data['email'],

username=validated_data['username'],

)

user.set_password(validated_data['password']) # 使用user.set_password()对密码编码,而不使用原始字符作为密码

user.save() # 保存User的实例

Token.objects.create(user=user) # 保存User实例后,在Token模型(数据库表)中,也对应保存一个Token实例作为用户的token

return user

现在我们完成了注册视图和相关序列化器的编写,要完成这个API,我们还要配置url分发。在此之前,我们把登录视图也编写完。

编写用户登录视图

有了用户注册API,我们还须要编写登录API。

处理用户登录的思路是,客户端发送一个post请求,视图从请求中取出用户名和密码,对用户名和密码进行校验;如果校验成功,则取出用户对应的token,作为响应返回,否则返回校验不成功的信息和状态码。

我们继续编写useraccesscontrol/apiview.py。代码解释见注释。

# in useraccesscontrol/apiview.py

from django.contrib.auth import authenticate

from django.contrib.auth.models import User

from rest_framework import generics, status

from rest_framework.views import APIView

from rest_framework.response import Response

from .serializers import UserSerializer

class UserCreate(generics.CreateAPIView):

# 略,前文已经编写过了

pass

class UserList(generics.ListAPIView):

# 略,一般来说不须要拉取用户列表,但读者可以编写它

pass

class UserRetrieve(generics.RetrieveAPIView):

# 略,不须要拉取用户信息,但读者可以编写它

pass

class LoginView(APIView):

name = 'login'

# 对用户登录进行权限管理,使用这个API不须要是已认证过的,所以从全局权限限制中豁免

# permission_classes = ()

def get_permissions(self):

return ()

def post(self, request):

username = request.data.get('username')

password = request.data.get('password')

user = authenticate(username=username, password=password) # 对用户名和密码进行认证,如果认证成功,则返回这个用户的实例

if user:

# 认证成功,从Token表中返回该用户的token

return Response(

data={

'token': user.auth_token.key,

}

)

else:

# 认证失败,则返回相关信息和状态码

return Response(

data={

'error': '认证失败,请确认账号和密码是否正确',

},

status=status.HTTP_400_BAD_REQUEST,

)

配置url

在pollsapi/urls.py对url进行分发。

# in pollsapi/urls.py

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('api-polls/', include('polls.urls')),

path('api-user-access-control/', include('useraccesscontrol.urls')),

]

在useraccesscontrol/urls.py中对url进行分发。

# in useraccesscontrol/urls.py

from django.urls import path

from rest_framework.urlpatterns import format_suffix_patterns

from .apiview import UserCreate, UserList, UserRetrieve, LoginView # UserList

urlpatterns = format_suffix_patterns([

path('users/', UserCreate.as_view(), name=UserCreate.name),

# path('users-list/', UserList.as_view(), name=UserList.name), # 如果没有编写相应视图,就不要分发了

# path('users-list//', UserRetrieve.as_view(), name=UserRetrieve.name), # 如果没有编写相应视图,就不要分发了

path('login/', LoginView.as_view(), name=LoginView.name),

])

使用API进行用户注册和登录

最后,不要忘记做数据迁移(migrate)。完成数据迁移后,可以测试api进行注册和登录。登录后,尝试获取要认证权限才能取得的数据。注意:用此前的用户进行登录是不能成功的,因为创建这些用户时,并没有生成相应的Token,所以要使用新注册的用户进行登录。

# in python interactive console

>>> import requests

>>> import json

>>> r = requests.post('http://127.0.0.1:8000/user-access-control/login/', data={'username': '小破球', 'password': 'somepassword'})

>>> r

# 以前创建的用户没有Token

>>> r = requests.post('http://127.0.0.1:8000/user-access-control/login/', data={'username': '宋江', 'password': 'runrun524299'})

>>> r

# 新创建的用户的能正常登录

>>> r.json() # 能获取到token

{'token': '5bf361eb356cbf58ab1d7fa0eb8e5d63a6929586'}

>>> polls = requests.get('http://127.0.0.1:8000/api-polls/polls/1/') # 没有使用token请求一个投票项

>>> polls

# 不使用token没法正确获取数据

>>> headers = {'Authorization': 'Token {your_token}'.format(your_token=r.json().get('token'))} # 将token作为headers中的一个元素,注意这个写法是固定的

>>> polls = requests.get('http://127.0.0.1:8000/api-polls/polls/1/', headers=headers) # 使用含有token信息的headers发起请求

>>> polls

# 状态码为200

>>> print(json.dumps(polls.json(), indent=2, ensure_ascii=0)) # 获取的信息正确

{

"id": 1,

"question": "去明尼苏达州大学要注意什么?",

"choices": [

{

"id": 1,

"votes": [],

"choice_text": "持正行远:用中国传统价值观打造商业帝国",

"poll": 1

},

... ...

{

"id": 3,

"votes": [],

"choice_text": "道路千万条,安全第一条。开车不规范,亲人两行泪",

"poll": 1

}

],

"pub_date": "2019-02-15T08:26:15.380191Z",

"created_by": 4

}

补遗:对polls/apiview.py与apiviewsets.py的整理

前文中,拷贝了polls/apiview.py,在其基础上编写了polls/apiviewsets.py。在新的进度中,将使用视图集的视图都放在了polls/apiviewsets.py,其它的API视图放在了polls/apiview.py,删除了重复的代码。读者大可不必这么做。

项目代码

目前为止的项目代码可见于

注意:本项目中,使用的数据是PostgreSQL,但Django默认的是SQLite3,读者下载使用代码时,应当根据自己环境的实际情况在settings.py中配置数据库。 PostgreSQL的安装使用可以见系统文章。

# in settings.py

# Database

# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

# 使用SQLite3

# DATABASES = {

# 'default': {

# 'ENGINE': 'django.db.backends.sqlite3',

# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

# }

# }

# 使用PostgreSQL

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.postgresql',

'NAME': 'django_rest',

'USER': 'webdev',

'PASSWORD': '5411',

'HOST': '127.0.0.1',

'PORT': '5432'

}

}

Step-7:生成API文档

Step-last:后记

系列文章风格

系列文章会以低零基础、手把手、逐行解释、连续完整的风格进行写作。 低零基础:降低文章阅读门槛,使接触Python Web开发时间较短的读者也能有所收获。本人本职是从事数据开发与数据挖掘,所以对低零基础深有体会。 手把手:一些基础操作,也会说明。如本文中,包括安装库等操作也会进行说明。 逐行解释:对代码进行解释,以白居易写诗风格为目标(传说白居易会把自己的诗解释给街头妇人,直到连不懂文化的妇人也能明白,完成创作)。 连续完整:连续是指,文章是成系列的,上文下文之间是有着联系的,项目是连续的。代码托管也体现了这一点,不同的文章,对应不同的git标签或分支,也体现了不同的进度。完整是指,项目是完整的,文章也是完整的。文章可以当作博文来读,也可以当作教程来读。

文章列表Python构建RESTful网络服务[Django篇:基于函数视图的API]:https://zhuanlan.zhihu.com/p/55562891

Python构建RESTful网络服务[Django篇:使用PostgreSQL替代SQLite](选读):https://zhuanlan.zhihu.com/p/55903530

Python构建RESTful网络服务[Django篇:基于类视图的API]:https://zhuanlan.zhihu.com/p/57024322

Python构建RESTful网络服务[Django篇:基于视图集的API]:https://zhuanlan.zhihu.com/p/57791697

Python构建RESTful网络服务[Django篇:用户接入控制,认证与权限]:https://zhuanlan.zhihu.com/p/58426061

参考文献

Hillar G C. Building RESTful Python Web Services[J]. Birmingham, UK: Packt Publishing Ltd, 2016.

相关文章:

  • pythonocc安装_PythonOCC开发-如何搭建开发环境和一个创建圆台例子
  • python怎么找到视频教程_哪里能找到 Python 视频教程地址?
  • mybatis嵌套子查询_InfluxDB常见问题和解答 - 如何在InfluxDB中实现嵌套子查询
  • select子查询返回 值_从零学会SQL:复杂查询,D4
  • python concat axis_Python NumPy中sum()函数详解 axis与keepdims图解
  • python echarts mysql_Django中从mysql数据库中获取数据传到echarts方式
  • skywalking原理_链路追踪 SkyWalking 源码分析——Collector Naming Server 命名服务
  • python print 调试_python 调试: print / assert / logging / pdb
  • 信息系统项目管理师论文_高级软考信息系统项目管理师考试技巧之论文摘要
  • imp oracle reschema_Oracle数据库逻辑备份之exp/imp(一)
  • aspnet是前端还是后端_谁能解释一下“前端开发”与“.NET”有什么区别和联系...
  • oracle rank 语法_Oracle用于排名的函数
  • extjs string类型转date_Extjs 时间格式的转换
  • jmeter如何定位网络延时_JMeter如何模拟不同的网络速度
  • docker mariadb集群_Docker Swarm 部署Mysql/Mariadb高可用主从复制集群
  • [ JavaScript ] 数据结构与算法 —— 链表
  • [笔记] php常见简单功能及函数
  • C# 免费离线人脸识别 2.0 Demo
  • co模块的前端实现
  • HashMap ConcurrentHashMap
  • JavaScript设计模式与开发实践系列之策略模式
  • JS实现简单的MVC模式开发小游戏
  • Linux CTF 逆向入门
  • nodejs调试方法
  • vue-router的history模式发布配置
  • 规范化安全开发 KOA 手脚架
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 好的网址,关于.net 4.0 ,vs 2010
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 马上搞懂 GeoJSON
  • 模型微调
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 数组的操作
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 温故知新之javascript面向对象
  • 译自由幺半群
  • 7行Python代码的人脸识别
  • 第二十章:异步和文件I/O.(二十三)
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • #QT(QCharts绘制曲线)
  • ${ }的特别功能
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (生成器)yield与(迭代器)generator
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (一)基于IDEA的JAVA基础10
  • (转)创业的注意事项
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • ***检测工具之RKHunter AIDE
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .net core 6 集成和使用 mongodb