第四章:用户认证系统
用户认证,说白了就是「你是谁」的问题。
我刚开始做ERP系统时,觉得认证不就是登录登出嘛,简单。结果第一个项目就被安全审计打回来三次……嗯,从那以后我再也不敢小看这块了。
今天咱们就把用户认证系统从头到尾捋一遍。从用户模型设计,到注册登录,再到JWT令牌和权限控制基础。你跟着我走一遍,以后做任何系统都能复用这套思路。
4.1 用户模型设计
用户模型是整个认证系统的地基。地基没打好,后面全是坑。
我个人习惯,用户模型最少包含这几个核心字段:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | UUID | 主键,不暴露自增ID给前端 |
| username | string(50) | 唯一,用于登录 |
| string(100) | 唯一,用于找回密码 | |
| password_hash | string(255) | 永远不存明文! |
| role | enum | admin / manager / staff |
| status | boolean | 是否激活 |
| created_at | datetime | 注册时间 |
| updated_at | datetime | 最后更新时间 |
用Django的模型来写,大概是这样的:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
ROLE_CHOICES = (
('admin', '管理员'),
('manager', '经理'),
('staff', '员工'),
)
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='staff')
phone = models.CharField(max_length=20, blank=True)
status = models.BooleanField(default=True)
class Meta:
db_table = 'sys_user'
verbose_name = '用户'
为什么继承AbstractUser?因为Django自带的认证系统已经帮我们处理了密码哈希、会话管理等麻烦事。你想想看,自己手写密码加密算法?那大概率会出问题。
4.2 注册功能实现
注册功能看着简单,但有几个细节要注意。
我建议的注册流程:
- 前端提交用户名、邮箱、密码、确认密码
- 后端校验用户名和邮箱是否唯一
- 校验密码强度(至少8位,包含字母和数字)
- 密码加密存储(用bcrypt或Django自带的PBKDF2)
- 返回成功信息,不返回密码相关字段
代码示例:
from rest_framework import serializers
from django.contrib.auth.hashers import make_password
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, min_length=8)
confirm_password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['username', 'email', 'password', 'confirm_password']
def validate(self, data):
if data['password'] != data['confirm_password']:
raise serializers.ValidationError('两次密码不一致')
# 这里可以加密码强度校验
return data
def create(self, validated_data):
validated_data.pop('confirm_password')
validated_data['password'] = make_password(validated_data['password'])
return super().create(validated_data)
4.3 登录/登出功能
登录认证,我推荐用JWT(JSON Web Token)。为什么不用Session?
说白了,Session是服务端存储状态,JWT是无状态的。在微服务架构下,JWT的优势很明显——每个服务都能独立验证令牌,不用每次都去查Session数据库。
登录流程:
- 用户提交用户名和密码
- 后端验证凭据
- 验证通过后生成JWT令牌
- 返回access_token和refresh_token
代码示例:
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.views import APIView
from rest_framework.response import Response
class LoginView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user is None:
return Response({'error': '用户名或密码错误'}, status=401)
if not user.status:
return Response({'error': '账号已被禁用'}, status=403)
refresh = RefreshToken.for_user(user)
return Response({
'access_token': str(refresh.access_token),
'refresh_token': str(refresh),
'user': {
'id': user.id,
'username': user.username,
'role': user.role
}
})
登出呢?JWT是无状态的,所以登出其实就是前端把令牌删掉。但为了更安全,我一般会在后端维护一个黑名单,把登出的令牌加进去。
4.4 JWT令牌认证
JWT的结构很简单:header.payload.signature。你想想看,就像一份带防伪标识的文件,谁都能看内容,但只有持有密钥的人才能验证真伪。
配置JWT:
# settings.py
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'AUTH_HEADER_TYPES': ('Bearer',),
'ALGORITHM': 'HS256',
}
前端每次请求时,在HTTP头里带上令牌:
Authorization: Bearer <your_access_token>
后端通过中间件验证令牌:
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticated
class SomeView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
# request.user 就是当前登录用户
return Response({'message': f'你好, {request.user.username}'})
4.5 权限控制基础
权限控制,说白了就是「你能干什么」。
在ERP系统里,权限控制分三个层次:
| 层次 | 说明 | 示例 |
|---|---|---|
| 接口权限 | 能不能访问某个API | 只有admin能删除用户 |
| 数据权限 | 能看到哪些数据 | 经理能看到本部门数据 |
| 字段权限 | 能看到哪些字段 | 普通员工看不到薪资字段 |
最简单的实现方式——基于角色的权限控制(RBAC):
from rest_framework.permissions import BasePermission
class IsAdminUser(BasePermission):
def has_permission(self, request, view):
return request.user.role == 'admin'
class IsManagerOrAdmin(BasePermission):
def has_permission(self, request, view):
return request.user.role in ['admin', 'manager']
# 使用方式
class DeleteUserView(APIView):
permission_classes = [IsAuthenticated, IsAdminUser]
def delete(self, request, user_id):
# 只有admin才能删除用户
pass
我建议在项目初期就设计好权限模型,不然后面改起来很痛苦。我曾经在一个项目里,权限控制是写在视图函数里的if-else……后来加了20多个角色,那代码简直没法看。
4.6 本章小结
好了,咱们把用户认证系统过了一遍:
- 用户模型设计——地基要打牢
- 注册功能——后端校验不能省
- 登录/登出——JWT是主流方案
- JWT令牌认证——无状态,适合微服务
- 权限控制基础——默认拒绝,显式授权
下一章咱们要进入ERP的核心模块——商品管理了。到时候你会看到,今天学的认证和权限控制,会在每个功能里用到。
嗯,今天就到这儿。有什么问题,咱们下节课见。
公众号:蓝海资料掘金营,微信deep3321