1、注册、登陆接口-简单设计

1.1、创建用户注册表的模型类

  1. from django.db import models
  2. from django.core.paginator import Paginator
  3. # Create your models here.
  4. class NewUser(models.Model):
  5. phone = models.CharField(verbose_name="手机号",max_length=11,unique=True)
  6. email = models.EmailField(verbose_name="邮箱",max_length=50,unique=True)
  7. nick = models.CharField(verbose_name="昵称", max_length=50)
  8. password = models.CharField(verbose_name="密码",max_length=32)
  9. class Meta:
  10. verbose_name = "用户表"
  11. verbose_name_plural = verbose_name
  12. db_table = "new_user" # 映射的用户注册表名称
  13. def __str__(self):
  14. return self.nick
  1. 检查这个模型所在的app是否注册,没有注册,在settings.py配置文件中,完成注册
  2. 数据迁移:
    1. 生成迁移文件 python manage.py makemigrations
    2. 执行迁移脚本程序 python manage.py migrate
  3. 连接数据库,检查数据表是否创建成功

image.png

1.2、注册、登陆接口视图函数

  1. import hashlib
  2. import json
  3. import time
  4. import django_redis
  5. from django.forms import model_to_dict
  6. from django.http import JsonResponse
  7. from django.db.models import Q
  8. from django.shortcuts import render
  9. from . import models
  10. # Create your views here.
  11. # 注册接口
  12. def register(request):
  13. email = request.POST.get("email")
  14. nick = request.POST.get("nick")
  15. phone = request.POST.get("phone")
  16. password = request.POST.get("password")
  17. password2 = request.POST.get("password2")
  18. if email and nick and phone and password:
  19. if password != password2:
  20. return JsonResponse({"code": -1, "msg": "两次输入的密码不一致"})
  21. user_email = models.NewUser.objects.filter(email=email)
  22. user_phone = models.NewUser.objects.filter(phone=phone)
  23. if user_email.exists():
  24. return JsonResponse({"code": -1, "msg": "email exists"})
  25. if user_phone.exists():
  26. return JsonResponse({"code": -1, "msg": "phone exists"})
  27. new_password = md5(password)
  28. user = models.NewUser(email=email, nick=nick, phone=phone, password=new_password)
  29. user.save()
  30. return JsonResponse({"code": 0, "msg": "success"})
  31. else:
  32. return JsonResponse({"code": -1, "msg": "not null"})
  33. # 登陆接口
  34. def login2(request):
  35. username = request.POST.get("username") # email + password ; phone+password
  36. password = request.POST.get("password")
  37. if username and password:
  38. user = models.NewUser.objects.filter(Q(email=username) | Q(phone=username))
  39. if not user.exists():
  40. return JsonResponse({"code": -1, "msg": "user not exists"})
  41. if user.first().password != md5(password):
  42. return JsonResponse({"code": -1, "msg": "password error"})
  43. #todo:这里要校验当前用户是否已经登录,如果已经登录,那么直接返回token (用来解决重复登陆存储多个token的问题)
  44. token = md5(str(time.time()) + username)
  45. user_dict = json.dumps(model_to_dict(user.first()))
  46. r = django_redis.get_redis_connection()
  47. expire_time = 60 * 60 * 24 * 1 # session的过期时间
  48. r.set(token, user_dict, expire_time)
  49. return JsonResponse({"code": 0, "msg": "login success", "token": token})
  50. else:
  51. return JsonResponse({"code": -1, "msg": "not null"})
  52. def md5(s):
  53. s = str(s)
  54. m = hashlib.md5(s.encode())
  55. return m.hexdigest()

配置路由

  1. """thz_django URL Configuration
  2. The `urlpatterns` list routes URLs to views. For more information please see:
  3. https://docs.djangoproject.com/en/3.2/topics/http/urls/
  4. Examples:
  5. Function views
  6. 1. Add an import: from my_app import views
  7. 2. Add a URL to urlpatterns: path('', views.home, name='home')
  8. Class-based views
  9. 1. Add an import: from other_app.views import Home
  10. 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
  11. Including another URLconf
  12. 1. Import the include() function: from django.urls import include, path
  13. 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
  14. """
  15. from django.contrib import admin
  16. from django.urls import path
  17. from user_demo import views as user_demo_view
  18. urlpatterns = [
  19. path('admin/', admin.site.urls),
  20. path('user_demo/register', user_demo_view.register), # 绑定路由和视图
  21. path('user_demo/login', user_demo_view.login2),
  22. ]

验证注册接口

image.pngimage.png

验证登陆接口

image.png
bug:短时间再次登陆,再次返回一个新的token,并存储到Redis中
image.png

1.3、解决重复登陆,Redis存储多个token的问题

  1. 登陆成功,给Redis存储两条记录
    1. 一条记录记录用户名和token,例如 (15503129999:”4a546a5e7b90adf7aa16a091006931e0”)
    2. 一条记录token和用户信息,例如(”4a546a5e7b90adf7aa16a091006931e0”:用户信息)
  1. # 登陆接口
  2. def login2(request):
  3. username = request.POST.get("username") # email + password ; phone+password
  4. password = request.POST.get("password")
  5. if username and password:
  6. user = models.NewUser.objects.filter(Q(email=username) | Q(phone=username))
  7. if not user.exists():
  8. return JsonResponse({"code": -1, "msg": "user not exists"})
  9. if user.first().password != md5(password):
  10. return JsonResponse({"code": -1, "msg": "password error"})
  11. #todo:这里要校验当前用户是否已经登录,如果已经登录,那么直接返回token (用来解决重复登陆存储多个token的问题)
  12. user_key = "session_wxj:%s"%user.first().phone
  13. # 登陆时,给Redis中存储两条记录 (用户名:token)(token:用户信息)
  14. #
  15. r = django_redis.get_redis_connection()
  16. redis_token = r.get(user_key)
  17. if redis_token:# 如果Redis中有这个用户的登陆记录就返回上一次登陆获取的token
  18. return JsonResponse({"code": 0, "msg": "login success", "token": redis_token.decode()})
  19. #
  20. token = md5(str(time.time()) + username) # 如果没有登陆记录,就重新返回新的token
  21. user_dict = json.dumps(model_to_dict(user.first()))
  22. r = django_redis.get_redis_connection()
  23. expire_time = 60 * 60 * 24 * 1 # session的过期时间
  24. #
  25. r.set(user_key, token, expire_time)# 登陆时,给Redis中存储两条记录 (用户名:token)(token:用户信息)
  26. #
  27. r.set(token, user_dict, expire_time)
  28. return JsonResponse({"code": 0, "msg": "login success", "token": token})
  29. else:
  30. return JsonResponse({"code": -1, "msg": "not null"})

image.pngimage.png

2、中间件-校验token

  1. from django.middleware.common import MiddlewareMixin
  2. from django.http.response import HttpResponse, JsonResponse
  3. import django_redis
  4. import json
  5. no_login_urls = ["login", "register", "admin"] # 不需要校验token的接口相关
  6. class TokenMiddleWare(MiddlewareMixin):
  7. """
  8. 校验接口是否需要传token的中间件
  9. """
  10. def check_not_url(self, path): # /api/login
  11. for url in no_login_urls:
  12. if url in path:
  13. # 模糊匹配,URL中包含这个就不校验token
  14. return True
  15. def process_request(self, request):
  16. # 执行路由之前被调用
  17. login_tag = self.check_not_url(request.path_info)
  18. if login_tag != True:
  19. token = request.GET.get("token")
  20. if not token:
  21. return JsonResponse({"code": -1, "msg": "没有传token"})
  22. r = django_redis.get_redis_connection()
  23. login_user = r.get(token)
  24. if not login_user:
  25. return JsonResponse({"code": -1, "msg": "token已失效"})
  26. request.user = json.loads(login_user) # 给request对象里面增加user属性
  27. # token = reuqest.headers.get("token")
  28. # request.path_info /login /api/register
  29. # 1、先判断url是否需要登录,需要登录才校验token
  30. # 2、在判断是否传了token
  31. # 3、再判断在redis里面是否有token
  32. # def process_response(self,request,response):
  33. # print("process_response")
  34. # response["aaaaa"] = "111111"
  35. # return response
  36. # def process_exception(self,request,exception):
  37. # print("exception",exception)
  38. # return

注意:

别忘了在settings.py配置文件中,注册这个中间件