Django用户组及登录、注册、注销、JWT

1. 用户模型创建

通常来说既然用了django了,不会完全舍弃它的用户模型吧,区别就是继承类的深度。

1.1 User

直接用django自带的User模型,如果你能够把需求砍到这种层度,没毛病。

1.2 AbstractUser

这一层帮你创建好了一些字段:

password, last_login, username, first_name, last_name, email, is_staff, is_active, date_joined

其中is_staff是能访问admin的权限。
还有几个基础方法。
其实继承这层一般就可以了,再新建几列诸如手机号,头像之类的,美滋滋。

1.3 AbstractBaseUser

更深一层的继承
只包含
password, last_login
但是封装了password的加密生成,一些状态的判断……
这是能继承的最基础的类了,如果还想更深的话,可能只能重写了

最后别忘了重新指定一下用户模型
AUTH_USER_MODEL = 'account.Account'

2. 用户模型获取
2.1 获取用户
from django.contrib.auth import get_user_model

User = get_user_model()
获取
User.objects.get/filter
创建
User.objects.create(username="xx")
设置密码
user = User.objects.get(xx)
user.set_password("xxxxx")
2.2 分组
from django.contrib.auth.models import Group
Group.objects.create(name="试用用户组")
group = Group.objects.get(name="试用用户组")
组内所有用户:
users = group.user_set.all()
用户加入组:
user = User.objects.get(xx)
user.groups.add(demo_group)

我这里的接口使用了django-restframework,接下来的操作先封装好一些类

from rest_framework.views import APIView
from rest_framework import permissions
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class BaseAPIView(APIView):
    permission_classes = (permissions.AllowAny, )
    authentication_classes = (JSONWebTokenAuthentication, )
3. 注册
3.1 表单

目前第一版暂不需要密码,通过手机短信注册。

from rest_framework import serializers

class RegisterSerializer(serializers.Serializer):
    mobile_phone = serializers.IntegerField(allow_null=False)
    verification_code = serializers.IntegerField()
    name = serializers.CharField()
    company = serializers.CharField()
3.2 控制层

写一下整体逻辑


class Register(APIView):
    serializer_class = RegisterSerializer

    def post(self, request, format=None):
        serializer = RegisterSerializer(data=request.data)
        if not serializer.is_valid():
            return render_no_match_v2(serializer.errors)

        mobile_phone = serializer.data["mobile_phone"]
        ...
        item = generate_Register_item(mobile_phone, verification_code, name, company)
        return render_api_item_v2({"item": item})
3.3 服务层

整体的流程大概如图:

register.jpg

这边我们进行的是途中的api2,api1我没有写在上面,是一个请求短信服务商发送短信通知的接口。
需要注意的地方有一处:服务器端要保存下来请求的验证码与api2里用户输入的验证码匹配
当然还要考虑覆盖(比如说用户请求了两次),以及这个存储的过期时间
综上,感觉用Redis是比较合适的。
不过,Emmmm,人手不足,怕出了问题的时候没人有时间去维护,这里先用django的cache了cache.set(key, value, timeout=30*60)
注:区别在于cache会随着django的重启而清空
接下来是注册(api2)的service层:

def generate_Register_item(mobile_phone, verification_code, name, company):
    vc = cache.get(mobile_phone)
    if vc and verification_code != vc:
        return "验证码错误"
    User = get_user_model()
    if User.objects.filter(telephone=mobile_phone):
        return "该手机已注册"
    user = User.objects.create(telephone=mobile_phone, username=mobile_phone)
    user.real_name = name
    user.company = company
    user.save()
    cache.delete(mobile_phone)
    return "注册成功"
4. 登录
4.1 表单

同样,没有设置密码,采用短信服务登录

4.2 控制层

基本如上

4.3 服务层

django是定制了login方法的
from django.contrib.auth import login
接受三个参数login(request, user, backend=None)
本质上应该是往session里写入了一个hash值来保存登录状态。
这里需要注意的是登录前是需要验证用户名密码的。
密码在数据库中通常不是明文存储,而是加盐再经过一个不可逆的hash操作存入数据库。
所以这里的验证本质上应该是把用户输入的明文密码再经过这样的加盐加密,然后对比两个字符串是否相同(没深入了解过,个人的理解),不过这里django也有封装的方法

from django.contrib.auth import authenticate
authenticate(username=xx, password=xx)

不过本次项目里因为暂不考虑密码,所以不需要这一步骤,直接短信验证后login
但是会报错,因为正常的步骤是先authenticate,再login,其中authenticate方法会给user加了一个属性backend
随便写个user测试一下得到这个backend值,赋给user

user.backend='django.contrib.auth.backends.ModelBackend'
5. 注销

这个就不写了,跟login一样,django还有一个logout方法
logout(request)

6. JWT

老样子,使用一个工具之前先搞明白它的作用,以及它和其它类似工具的区别

6.1 作用

JWT全称:Json Web Token,一种登录状态的令牌验证机制。

6.2 构成

JWT分为三段,用"."来分隔。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Im1hbmJ1ZyIsIm9yaWdfaWF0IjoxNTE2MzQ5ODYwLCJlbWFpbCI6IiIsImV4cCI6MTUxNjQzNjI2MCwidXNlcl9pZCI6N30.FNOGMZ_c6pD7q6ebhjcblPcQYyVelrV5-XJHLcX_ZQU

第一部分:头部(header)

{
  'typ': 'JWT',
  'alg': 'HS256'
}
第二部分:载荷(payload)
第三部分:签证(signature)
其中JWT要在服务端设置好秘钥,签证时候和base64后的header,payload共同生成signature
6.3 对比,差异

说到验证登录状态一般都会想到Session,我们先来捋一下一般判断登录状态的流程。

  1. Cookie
    理论上来讲,用户信息可以存到Cookie里的,如果无论你还是服务方都觉得这个安全问题无所谓的话 -.-!
  2. Session
    应该是主流的方法吧,记得前年刚工作时各种面试都会问到这个。大概就是每次登录时服务端会在数据库里(或者Redis之类的?)存下一条记录,然后把session返回给浏览器,浏览器保存下来,接下来的操作会拿这个session_id向服务器验证。
    至于Session可能隐藏的问题一个是搞负载均衡时如果没把Session存到一台服务器上可能会不断重新登录(Emmmmm这个都没考虑到还是别配多服务器了),再一个就是如果流量很大,对存储的服务器压力过大(T.T入行尚浅,还没经历过什么大型的项目,这种情况下可能会把Redis也撘成集群?)
  3. JWT
    至于JWT,其实跟Session挺像的,琢磨了好久有什么区别,后来理解大概就是服务端不用存储吧。
6.4 验证流程
  1. 客户端用用户名和密码请求服务端,成功后返回token
  2. 客户端存下token,每次请求时放到头部authorization里
  3. 服务器收到客户端请求时先验证token
6.5 具体实现代码

同样,本次项目采用django-restframework
关于具体的代码,先弄清总共分为三个模块:生成验证刷新
其实这个库可以直接用

from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token

如果有什么特殊的需求那么解决办法其实也很简单,比如说一层层依次点开obtain_jwt_token,找一个合适的层次的类继承它。

举个栗子:
现在要为网站设计子域名,比如说产品环境叫dev-data.xxx.xxx,预览版叫dev-preview.xxx.xxx,要为用户设计个权限控制

那么完全可以先一层层往上找
obtain_jwt_token >>> ObtainJSONWebToken >>> JSONWebTokenAPIView
停,重写JSONWebTokenAPIView的post方法,加一层用户组的判断就ok了

嗯,最后注意一点,别忘了以后的每个api里都加上这个token的验证,可以重新定义个基类

from rest_framework.views import APIView
from rest_framework import permissions
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


class BaseAPIView(APIView):
    permission_classes = (permissions.AllowAny, )
    authentication_classes = (JSONWebTokenAuthentication, )
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容