## django csrf跨站,CBV添加装饰器,auth认证模块 ## 内容概要 csrf跨站请求伪造 csrf相关校验策略 CBV添加装饰器的多种方式 auth认证模块 BBS项目需求分析 ## 内容详情 ## csrf跨站请求伪造 钓鱼网站:模仿一个正规的网站 让用户在该网站上操作 但是操作的结果会影响到用户正常的网站账户 但是其中会有一些暗箱操作 例如:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是却交到了一个莫名其妙的账户 并不是真正的四六级官方账户 模拟钓鱼网站案例:转账案例 内部隐藏标签 如何区分真假网站页面发送的请求 正常请求 ```python 我是真正的网站 用户名: 转账账户: 转账金额: ```  钓鱼网站的暗箱操作 ```python 我是钓鱼网站 用户名: 转账账户 转账金额 ```  ## csrf校验策略 在提交数据的位置添加唯一标识 ### 1.form表单csrf策略 form表单内部添加`{% csrf_token %}` 打开配置文件csrf校验中间件 ```python 'django.middleware.csrf.CsrfViewMiddleware', ``` 在前端页面添加校验 ```python 我是真正的网站 {% csrf_token %} 用户名: 转账账户: 转账金额: ``` 有唯一标识可以通过校验正常使用功能  没有唯一标识无法通过校验  如果不是真正网站的csrf_token,自己伪造的不行  ### 2.ajax请求csrf策略 方式1:自己动手取值 较为繁琐 ```python data:{'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, $.ajax({ url: "/cookie_ajax/", type: "POST", data: { "username": "Tonny", "password": 123456, "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中 }, success: function (data) { console.log(data); } }) ``` 方式2:利用模板语法自动获取(一定要用引号引起来) ```python data:{ 'csrfmiddlewaretoken':'{{ csrf_token }}','username':'jason'}, ``` 方式3:直接引入一个js脚本即可 或者用自己写一个getCookie方法: ```python function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); ``` 每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。 ```python function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); ``` 将下面的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的) 放在一个js文件中然后再页面中引入js文件即可 ```python $('#ajax').click(function () { $.ajax({ url: '', type: 'post', data: {'username': '张三', 'transfer': '官方账户', 'money': 10000}, success:function () { alert('转账成功') } }) }) ```  ## csrf相关装饰器 整个django项目都校验csrf 但是某些视图函数/视图类不想校验 整个django项目都不校验csrf 但是某些个视图函数/视图类想要校验 ## FBV添加装饰器的方式 与正常函数添加装饰器一致 ### 取消校验csrf ```python from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_exempt def index_func1(request): if request.method == 'POST': username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage1.html') ``` ### 单个校验csrf ```python from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_protect def index_func2(request): if request.method == 'POST': username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage2.html') ``` ## CBV添加装饰器的方式 与正常情况不一样 需要注意 主要有三种方式 ### 方式1:单独生效 ```python from django.views import View from django.views.decorators.csrf import csrf_exempt, csrf_protect class MyView(View): def get(self, request): return render(request, 'indexPage1.html') @csrf_protect # 不能直接装饰 def post(self, request): username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage1.html') ```  报错,CBV不能直接添加装饰器 需要使用一个方法进行装饰 ```python from django.views import View from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator class MyView(View): def get(self, request): return render(request, 'indexPage1.html') # @csrf_protect # 不能直接装饰 @method_decorator(csrf_protect) def post(self, request): username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage1.html') ```  使用方法装饰后就可以正常使用了 ### 方式2:单独生效 ```python @method_decorator(csrf_protect, name='post') class MyView(View): def get(self, request): return render(request, 'indexPage1.html') # @csrf_protect # 不能直接装饰 def post(self, request): username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage1.html') ``` ### 方式3:整个类中生效 ```python from django.views import View from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator class MyView(View): @method_decorator(csrf_protect) # cbv拦截装饰方法 def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self, request): return render(request, 'indexPage1.html') # @csrf_protect # 不能直接装饰 def post(self, request): username = request.POST.get('username') transfer = request.POST.get('transfer') money = request.POST.get('money') print(f'{username}转账给{transfer}金额{money}元') return render(request, 'indexPage1.html') ``` ### 注意事项 有一个装饰器是特里只能有一种添加方式`csrf_exempt` 只有在dispatch方法添加才会生效 ```python from django.views import View from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator class MyView(View): @method_decorator(csrf_exempt) # 取消校验 def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) ``` ## auth认证模块 django自带一个admin路由 但是需要我们提供管理员账号和密码 如果想要使用admin后台管理 需要先创建表 然后创建管理员账号直接执行数据库迁移命令即可产生默认的auth_user表 该表就是admin管理后台默认的认证表 1.创建超级管理员 ```python python manage.py createsuperuser ```  )  基于auth_user表编写用户相关的各相功能 登录、校验用户是否登录、修改密码、注销登录等 ## auth认证相关模块及操作 ### 用户注册 ```python def login_func(request): if request.method == 'POST': username = request.POST.get('username') pwd = request.POST.get('pwd') # User.objects.create(username=username,password=pwd) # 不加密 User.objects.create_user(username=username, password=pwd) # 密码加密正常创建用户用这个 ```  ### 判断用户是否登录成功 自动校验用户名密码,并且自动创建session ```python user_obj = auth.authenticate(request, username=username, password=pwd) # 判断是否登录成功 if user_obj: print('登录成功') # 用户登录成功(返回给客户端登录的凭证,令牌,随机字符串) auth.login(request, user_obj) # 自动操作djangosession表 return render(request, 'login.html') ```  ### 判断用户是否登录 结合登录注册 ```python print(request.user) # 查看用户名 print(request.user.is_authenticated) # 查看用户是否已登录 ``` ```python def login_func(request): print(request.user) print(request.user.is_authenticated) # 判断当前用户是否以登陆 if request.method == 'POST': username = request.POST.get('username') pwd = request.POST.get('pwd') user_obj = auth.authenticate(request, username=username, password=pwd) # 判断是否登录成功 if user_obj: print('登录成功') # 用户登录成功(返回给客户端登录的凭证,令牌,随机字符串) auth.login(request, user_obj) # 自动操作djangosession表 return render(request, 'login.html') print('登录失败') return render(request, 'login.html') def home(request): return render(request, 'homePage.html', locals()) ``` ```python {% if request.user.is_authenticated %} 登录用户{{ request.user }} {% else %} 登录注册 {% endif %} ``` ### 校验装饰器 配置文件中LOGIN_URL = '/login/' ```python from django.contrib.auth.decorators import login_required @login_required(login_url='/login/') # 可以指定用户没有登录跳转的地址,每个都可自己手动指定跳转地址 def logined_func(request): print('登录后才能看的页面') return HttpResponse('登录后才能看的页面') ``` 结合登录功能 ```python def login_func(request): print(request.user) print(request.user.is_authenticated) # 判断当前用户是否以登陆 if request.method == 'POST': username = request.POST.get('username') pwd = request.POST.get('pwd') # # User.objects.create(username=username,password=pwd) # 不加密 # User.objects.create_user(username=username, password=pwd) # 密码加密正常创建用户用这个 user_obj = auth.authenticate(request, username=username, password=pwd) # 判断是否登录成功 if user_obj: print('登录成功') # 用户登录成功(返回给客户端登录的凭证,令牌,随机字符串) auth.login(request, user_obj) # 自动操作djangosession表 return redirect(request.GET.get('next')) print('登录失败') return render(request, 'login.html') ``` 使用全局配置 ```python # 使用全局配置全局 # LOGIN_URL = '/login/' @login_required def logined_func(request): print('登录后才能看的页面') return HttpResponse('登录后才能看的页面') ``` ### 校验原密码是否正确 ```python res = request.user.check_password(原密码) ``` ### 修改密码 ```python request.user.set_password(新密码) request.user.save() 在忘记原密码时使用下面方法可以进行密码修改 res_obj = User.objects.filter(username='qwe').first() # 首先拿到要修改的对象 res_obj.set_password('1234') # 修改密码 res_obj.save() # 保存密码就行了 ``` ### 退出登录 ```python auth.logout(request) ``` ## 扩展auth_user表 既想使用auth模块的功能 并且又想扩展auth_user表的字段 思路1:一对一字段关联 思路2:替换auth_user表 步骤1:模型层编写模型类继承AbstractUser ```python from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=32,verbose_name='手机号') desc = models.TextField(verbose_name='详情') ``` 步骤2:一定要在配置文件中声明替换关系 ```python AUTH_USER_MODEL = 'app01.UserInfo' ``` ### 不使用新库扩展自定义auth_user表 替换还有一个前提 就是数据库迁移没有执行过(auth相关表没有创建) 如果非要创建那么可以在写好自定义auth_user表后,删除数据库里面django创建的表,保留自己在models里面创建的表,然后重新makemigrations后,删除,migrations文件夹里面记录的,自己创建的表后,在执行migrate就创建好自己自定义添加的auth_user表了     ```python models.UserInfo.objects.create_user(username=username, password=pwd, phone='123331', desc='坤坤') ```  ) 判断登录还是使用request.user进行判断 ```python print(request.user) print(request.user.is_authenticated) # 判断当前用户是否以登陆 ``` 只有在进行模型类操作使用自己写的类,其余与django自动创建,使用方式一样 ```python user_obj = auth.authenticate(request, username=username, password=pwd) # 判断是否登录成功 if user_obj: print('登录成功') # 用户登录成功(返回给客户端登录的凭证,令牌,随机字符串) auth.login(request, user_obj) # 自动操作djangosession表 return redirect(request.GET.get('next')) print('登录失败') ``` Last modification:December 29th, 2022 at 04:27 pm © 允许规范转载 Support 如果觉得我的文章对你有用,请随意赞赏 ×Close Appreciate the author Sweeping payments
Comment here is closed