09.drf认证
一.token 认证
https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
- 1.1 settings 配置
# install
'rest_framework.authtoken',
#
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
- 1.2 migrate
python manage.py migrate
- 1.3 配置 url
from rest_framework.authtoken import views
urlpatterns += [
path('api-token-auth/', views.obtain_auth_token)
]
# drf 文档原本内容如下
from rest_framework.authtoken import views
urlpatterns += [
url(r'^api-token-auth/', views.obtain_auth_token)
]
- 1.4 curl 获取token
curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456##"}' http://127.0.0.1:8000/api-token-auth/
{"token":"4dd5411643aeb990ffab58118695cbdb98b253b6"}
全局配置需要认证才能访问
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.DjangoModelPermissions',
'utils.permissions.Permissions',
# 'rest_framework.permissions.AllowAny',
)
- 1.5 携带token访问
curl -X GET http://127.0.0.1:8000/idcs/ -H 'Authorization: Token 4dd5411643aeb990ffab58118695cbdb98b253b6'|python -m json.tool
- 1.6 返回
{
"total": 104,
"next": "http://127.0.0.1:8000/idcs/?pagerCount=2",
"previous": null,
"data": [{
"id": 1,
"name": "亚太机房",
###########
{
"detail": "Authentication credentials were not provided."
}
# 没有配置 token 认证类无法使用 token 登录
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
- 1.7 视图中配置
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,)
二. drf JWT 认证
https://github.com/jpadilla/django-rest-framework-jwt
https://jpadilla.github.io/django-rest-framework-jwt/
- 2.1 安装
pip install djangorestframework-jwt
- 2.2 settings
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
- 2.3 url
from rest_framework_jwt.views import obtain_jwt_token
#...
urlpatterns = [
'',
# ...
url(r'^api-token-auth/', obtain_jwt_token),
]
- 2.4 获取 token`
#
curl -X POST -d "username=admin&password=123456##" http://localhost:8000/api-token-auth/
# json格式
curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456##"}' http://localhost:8000/api-token-auth/
- 2.5 返回 token
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo"}
- 2.6 携带 token 访问
curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo" http://localhost:8000/idcs/
- 2.7返回
# 错误 token 返回:
{"detail":"Error decoding signature."}
# 正确 token 返回
jenvid@jenvidVM:~/01web/ops/vueAdmin-template$ curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkwMjkyMTgzLCJlbWFpbCI6ImFkbWluQGJtLmNvbSJ9.LWKjgwjixwc0_5Ri9DE2hjsOAz7VuS3nFJTfMw_Txdo" http://localhost:8000/idcs/ |python -m json.tool
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1324 100 1324 0 0 107k 0 --:--:-- --:--:-- --:--:-- 107k
{
"data": [
{
"address": "\u795e\u821f\u8def999\u53f7",
"email": "xxx@com.cn",
三. 修改过期时间,默认是 300s
- 3.1 settings
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
}
- 3.2 过期返回
{
"detail": "Signature has expired."
}
四.自定义认证,用户 手机号认证
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
import datetime
#有效期限
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), #也可以设置seconds=20
'JWT_AUTH_HEADER_PREFIX': 'JWT', #JWT跟前端保持一致,比如“token”这里设置成JWT
}
# users.views.py
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, 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
五. python jwt
- 5.1 自定义认证类
/user/extensions/auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.response import Response
from rest_framework.exceptions import AuthenticationFailed
from jwt import exceptions
import jwt
from django.conf import settings
class JwtQueryParamsAuthentication(BaseAuthentication):
def authenticate(self, request):
# 获取token
# 1.切割
# 2.解密第二段,判断是否过期
# 3.验证第三段合法性
token = request.query_params.get('token') #
SALT = settings.SECRET_KEY
try:
payload = jwt.decode(token, SALT, True) # True 使用集成的时间等字段校验
except exceptions.ExpiredSignatureError:
msg = 'token已失效'
raise AuthenticationFailed({'code':400,'error':msg})
except jwt.DecodeError:
msg = 'token认证失败'
raise AuthenticationFailed({'code': 400, 'error': msg})
except jwt.InvalidTokenError:
msg = '非法的token'
raise AuthenticationFailed({'code': 400, 'error': msg})
# 3种返回值
# 1.抛出异常后,后面的代码都不会执行了
# 2.return 元组(1,2) 认证通过, 在views中调用 request.user = 第一个元素, request.auth = 第二个值
# 3.None, 等待下一次认证,不用管
# if not payload:
# return Response({'code': 2001, 'error': msg})
print('payload: ', payload)
return (payload,token) # 1=request.user
- 5.2 视图函数生成 token
class JwtLoginView(APIView):
def post(self,request,*args,**kwargs):
user = request.data.get('username')
pwd = request.data.get('password')
user_object = UserInfo.objects.filter(username=user,password=pwd).first()
if not user_object:
return Response({'code':1000,'error':'用户名密码错误'})
# 通过jwt生成token
# 构造header,不写表示使用默认
headers = {
'typ': 'JWT',
'alg': 'HS256'
}
# 构造payload
payload = {
'user_id': user_object.id, # 自定义用户ID
'username': user_object.username, # 自定义用户名
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=1) # 超时时间
}
token = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8')
return Response({'code':1001,'data':token})
- 5.3 视图post中使用 jwt 认证
from user.extensions.auth import JwtQueryParamsAuthentication
from user.utils.jwt_auth import create_token
class ProLoginView(APIView):
authentication_classes = [] # 为空表示不应用认证
def post(self,request,*args,**kwargs):
user = request.data.get('username')
pwd = request.data.get('password')
user_object = UserInfo.objects.filter(username=user,password=pwd).first()
if not user_object:
return Response({'code':1000,'error':'用户名密码错误'})
payload = {
"id":user_object.id,
"username":user_object.username
}
token = create_token(payload)
return Response({'code':1001,'data':token})
- 5.4 视图get使用jwt认证
class ProOrderView(APIView):
# authentication_classes = [JwtQueryParamsAuthentication,] # 加入 settings 全局配置
def get(self, request, *args, **kwargs):
print(request.user) # 拿到payload的所有信息
return Response('订单列表')
- 5.5 settings 全局配置自定义 jwt 认证
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES" : ['user.extensions.auth.JwtQueryParamsAuthentication']
}
六.drf token base64解密 增加返回token
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from utils.base_password import decode_password
import base64
def decode_password(en_password):
byte_password = base64.b64decode(en_password)
password = str(byte_password, encoding='utf8')
return password
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
en_password = request.data['password']
print(en_password,type(en_password))
password = decode_password(en_password)
print('password:',password)
request.data['password'] = password
serializer = self.serializer_class(data=request.data,
context={'request': request})
print('drf接收登录数据', serializer)
print(request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
data = {
'token': token.key,
}
return Response(data)