DJANGO后端开发简单员工管理系统实现登录功能中的JWT验证(零基础也可以看懂)

当我们在网站上进行登录时,服务器需要确认我们的身份,并且在我们后续的请求中知道我们是谁。JSON Web Token(JWT)是一种用于解决这个问题的简单而安全的方法。

想象一下,当我们登录成功后,服务器会给我们一个特殊的令牌(Token)。这个令牌就像是一个通行证,可以证明我们是合法的用户。我们每次向服务器发送请求时,都会带上这个令牌。这个令牌由三个部分组成:头部、载荷和签名。

头部包含了关于令牌的一些描述信息,比如令牌的类型和加密算法。

载荷是令牌的主体,里面存放着一些重要的信息,比如用户的ID、用户名和权限等。这些信息以键值对的形式呈现,并被编码成JSON格式。

签名是用来验证令牌的真实性的。服务器会使用一个密钥对头部和载荷进行加密生成签名。当服务器收到带有令牌的请求时,它会使用相同的密钥再次进行加密,并与请求中的签名进行比较,以确保令牌没有被篡改过。

这样,服务器可以根据令牌中的信息来验证用户的身份和权限。我们每次请求时都需要携带有效的令牌,服务器会检查令牌的签名,如果验证通过,就知道这是一个合法的请求。JWT的好处是它非常轻量级且易于使用。它可以在不保存用户状态的情况下进行身份验证,因为令牌本身就包含了用户的信息。它也可以跨不同的系统和平台使用,并且可以自定义载荷中的信息来满足特定需求。

总结一下,JWT是一种安全的令牌,用于验证用户身份和权限。它由头部、载荷和签名组成,服务器使用密钥验证令牌的真实性。通过在每次请求中携带令牌,服务器可以确认用户的身份并进行相应的操作。

这里只是简单介绍,想要进一步了解这些内容,建议参考此文章(这些基础知识对了解JWT很有帮助)傻傻分不清之 Cookie、Session、Token、JWT - 掘金

我使用的是django框架进行后端开发,github仓库地址为https://github.com/2024635569/company-management-system

其中虚拟环境的下载,如何使用django就不详细介绍了。

 我们主要在以下文件进行编辑,一个是app下的views.py,可以定义各种视图函数来处理不同的URL请求,一个是models.py,用于定义数据模型,通过定义数据模型,你可以创建数据库表、定义表之间的关系以及操作数据。

在 Django 中进行 JWT(JSON Web Token)验证的过程可以概括为以下几个步骤:

  1. 用户身份验证:

    • 用户在登录时提供用户名和密码。
    • Django 使用提供的用户名和密码进行身份验证,验证用户的凭据是否正确。
    • 如果身份验证成功,Django 返回一个包含用户信息的有效用户对象。
  2. JWT 生成:

    • 一旦用户身份验证成功,服务器将使用用户信息(例如用户ID)和其他所需的有效载荷数据创建一个 JWT。
    • 有效载荷是 JWT 中存储的一组自定义数据,可以包含关于用户的任意信息,例如用户角色、权限等。
    • 使用加密算法(例如HMAC或RSA)对有效载荷进行签名,以确保 JWT 的完整性和真实性。
  3. JWT 返回给客户端:

    • 生成的 JWT 将作为响应的一部分返回给客户端(通常在HTTP响应的头部或响应体中)。
    • 客户端将保存这个 JWT,通常将其存储在浏览器的本地存储或会话存储中,以备将来的请求使用。
  4. JWT 的使用:

    • 在客户端发起后续请求时,将 JWT 作为身份验证凭据一起发送给服务器。
    • 通常,JWT 将放置在请求头部的 Authorization 字段中,并以特定的格式发送(例如 "Bearer <JWT>")。
    • 服务器接收到请求后,会解析 JWT 并验证其完整性和签名。
    • 如果验证成功,服务器可以从 JWT 中提取有效载荷数据,例如用户ID和角色,以便进行后续的授权和权限验证

 开发文档中详细记录了后端要完成的工作,对于登录功能的接口设计如下

 主要用到的表(储存登录的账号与密码)为

 先在models.py中完成类的创建

class User(models.Model):
    id = models.AutoField(primary_key=True)
    emp_no = models.OneToOneField(Employee, on_delete=models.CASCADE,related_name='user_emp_no', db_index=True)
    password = models.CharField(max_length=50)
    role_id = models.ForeignKey(Role, on_delete=models.CASCADE)
   
    def check_password(self, raw_password):
        print(self.password)
        return raw_password==self.password

然后在视图函数(views.py)完成当用户登录输入账号后密码之后执行的函数(register_user)

def register_user(request):
    data = request.POST.dict()
    username = data.get("username")
    password = data.get("password")
    '''
    首先,从请求中获取表单数据,包括用户名和密码。
    '''
    try:
        user = User.objects.get(emp_no_id=username)  
    
    except User.DoesNotExist:
        response = {
            "status": 'FAIL',
            "message": '工号不存在',
        }
        return JsonResponse(response)
'''
接下来,使用用户名查询数据库,尝试获取具有指定 emp_no_id 的用户对象。如果用户不存在,返回一个包含错误信息的 JSON 响应,表示工号不存在。
'''
    #check_password定义在models.py的User类里
    if user.check_password(password) and user:
        try:
            tokens = Tokens.objects.get(emp_no_id=user.emp_no_id)
            token = tokens.token
            
        except ObjectDoesNotExist:
            payload = {
                'id': user.id,
                'emp_no_id': user.emp_no_id
            }
            token = jwt.encode(payload, get_random_secret_key(), algorithm='HS256')
            tokens = Tokens(emp_no_id=user.emp_no_id, token=token)
            tokens.save()
        
        response = {
            "status": 'SUCCESS',
            "message": '登录成功',
            "token": token
        }
    else:
        response = {
            "status": 'FAIL',
            "message": '密码错误',
        }
    return JsonResponse(response)
'''
如果用户存在,并且密码验证成功,进入登录成功的逻辑。首先尝试从数据库中获取具有相同 emp_no_id 的 Tokens 对象。如果存在,则获取该对象的 token 值。

如果不存在对应的 Tokens 对象,说明用户之前没有登录过,那么就生成一个新的 token。这里使用 PyJWT 库来生成 JWT(JSON Web Token)。将用户的 id 和 emp_no_id 添加到 JWT 的负载中,然后使用get_random_secret_key() 生成一个随机的密钥来对 JWT 进行签名,并指定算法为 HS256。接下来,创建一个新的 Tokens 对象,并将 emp_no_id 和生成的 token 保存到数据库中。

最后,返回一个 JSON 响应,包含登录成功的状态、消息和生成的 token。

如果密码验证失败,则返回一个包含密码错误信息的 JSON 响应
'''

注意,因为表中的字段有外键,外键在原本定义的字段后会自动跟着一个_id的后缀,不要忽略

这是其中一种生成token的方法,另一种方法是这段代码,利用了Django自带的功能,使用rest_framework.authtoken模块生成身份验证令牌。该模块为Django REST Framework提供了基于令牌的身份验证系统。使用此方法传入get_or_create函数的参数必须是django内置的user类的实例,不能为自定义的。

from rest_framework.authtoken.models import Token
token, _ = Token.objects.get_or_create(user=user)

接下来实现另外两个视图函数example和generate_csrf_token

@require_GET
def example(request):
    auth_header = request.META.get('HTTP_AUTHORIZATION')
    if auth_header and auth_header.startswith('Bearer '):
        token = auth_header.replace('Bearer ', '')
        token_record = Tokens.objects.filter(token=token).first()

        if token_record:
            current_time = timezone.now()
            if current_time < token_record.expires_at:
                return JsonResponse({'message': '验证成功',"status":"SUCCESS",})
            else:
                return JsonResponse({'message': 'token过期',"status":"FAIL"})
        else:
            return JsonResponse({'message': 'token不存在',"status":"FAIL"})
  • 该函数使用了 @require_GET 装饰器,表示该视图函数只接受 HTTP GET 请求。它用于验证身份验证令牌(Token)是否有效。
  • 从请求的 HTTP 头部中获取授权头信息(Authorization Header),并检查是否以 "Bearer " 开头。
  • 如果存在有效的令牌,则查询数据库中的 Tokens 模型,检查令牌是否存在且未过期。
  • 如果令牌有效且未过期,则返回包含验证成功消息的 JSON 响应。
  • 如果令牌不存在或已过期,则返回相应的 JSON 响应。
  • 这个视图函数可以用于对需要身份验证的请求进行访问控制和权限验证。
@require_http_methods(['GET'])
def generate_csrf_token(request):
    response = JsonResponse({'csrfToken': get_token(request)})
    # 将csrftoken写入cookie,保证ajax请求携带csrf_token
    response.set_cookie('csrftoken', get_token(request))
    return response
  • 该函数用于生成 CSRF 令牌(Cross-Site Request Forgery Token)并将其返回给客户端。它使用 Django 提供的 get_token(request) 方法生成 CSRF 令牌。
  • 在生成的 JSON 响应中,将 CSRF 令牌作为 csrfToken 返回。
  • 同时,将 CSRF 令牌写入浏览器的 Cookie 中,以确保后续的 AJAX 请求能够携带 CSRF 令牌。
  • 这个视图函数用于在客户端和服务器之间建立 CSRF 保护机制,以防止跨站请求伪造攻击。

整个视图函数代码就完成了,这里按照文档完成了后端的对应需求,前端代码也放在了github仓库中,某次登录后就可以看到数据库中tokens表中增添的数据

 完整视图函数代码如下

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST, require_GET
from .models import User,Tokens
from django.utils import timezone   
import jwt
from django.core.exceptions import ObjectDoesNotExist   
from django.core.management.utils import get_random_secret_key#该函数会生成适用于密码学用途的随机密钥
@csrf_exempt
@require_POST
def register_user(request):
    data = request.POST.dict()
    username = data.get("username")
    password = data.get("password")
    
    try:
        user = User.objects.get(emp_no_id=username)  
    
    except User.DoesNotExist:
        response = {
            "status": 'FAIL',
            "message": '工号不存在',
        }
        return JsonResponse(response)

    # 与数据库中的密码进行比较
    if user.check_password(password) and user:
        try:
            tokens = Tokens.objects.get(emp_no_id=user.emp_no_id)
            token = tokens.token
            
        except ObjectDoesNotExist:
            payload = {
                'id': user.id,
                'emp_no_id': user.emp_no_id
            }
            token = jwt.encode(payload, get_random_secret_key(), algorithm='HS256')
            tokens = Tokens(emp_no_id=user.emp_no_id, token=token)
            tokens.save()
        
        response = {
            "status": 'SUCCESS',
            "message": '登录成功',
            "token": token
        }
    else:
        response = {
            "status": 'FAIL',
            "message": '密码错误',
        }
    return JsonResponse(response)
    
@require_GET
def example(request):
    auth_header = request.META.get('HTTP_AUTHORIZATION')
    if auth_header and auth_header.startswith('Bearer '):
        token = auth_header.replace('Bearer ', '')
        token_record = Tokens.objects.filter(token=token).first()

        if token_record:
            current_time = timezone.now()
            if current_time < token_record.expires_at:
                return JsonResponse({'message': '验证成功',"status":"SUCCESS",})
            else:
                return JsonResponse({'message': 'token过期',"status":"FAIL"})
        else:
            return JsonResponse({'message': 'token不存在',"status":"FAIL"})

# 生成csrf_token的视图
@require_http_methods(['GET'])
def generate_csrf_token(request):
    response = JsonResponse({'csrfToken': get_token(request)})
    # 将csrftoken写入cookie,保证ajax请求携带csrf_token
    response.set_cookie('csrftoken', get_token(request))
    return response

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idMiFeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值