功能配置设计
实现类似于的django中settings.py中的中间件的字符串, 注释某个字符串, 使得相应的功能失效
importlib
模块
# 可以利用 特定格式的字符串路径 导入模块 import importlib path_str = 'lib.test' mod = importlib.import_module(path_str) # from lib import test
''' 文件结构 notify __init__.py email.py msg.py wechat.py settings.py start.py ''' # __init__.py import settings import importlib def send_all(content): for path_str in settings.NOTIFY_LIST: module_path, class_name = path_str.rsplit('.', maxsplit=1) # 拿到文件对象 mod = importlib.import_module(module_path) # from notify import email/msg/wechat # 利用反射获取文件对象中的类 cls = getattr(mod, class_name) # 实例化对象 obj = cls(content) # 发送通知 (鸭子类型思想, 不管你是什么对象, 都有send方法) obj.send() # email.py class Email(object): def __init__(self, content): self.content = content def send(self): print(f'邮箱通知:{self.content}') # msg.py class Msg(object): def __init__(self, content): self.content = content def send(self): print(f'短信通知:{self.content}') # wechat.py class WeChat(object): def __init__(self, content): self.content = content def send(self): print(f'微信通知:{self.content}') # settings.py NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', 'notify.wechat.WeChat', ] # start.py from notify import * if __name__ == '__main__': send_all('非常高兴的通知大家, 本周六加班') ''' 邮箱通知:非常高兴的通知大家, 本周六加班 短信通知:非常高兴的通知大家, 本周六加班 微信通知:非常高兴的通知大家, 本周六加班 '''
跨站请求伪造CSRF
什么是CSRF
什么是跨站请求伪造呢? 举一个简单的钓鱼网站的例子
我们对照着某银行的页面写一个一模一样的页面, 然后将用户 "钓" 到我们这个页面上进行转账操作
用户输入用户名, 密码, 对方账户, 以及转账金额, 提交
然后我们利用事先写好的代码将"对方账户"改成我们自己的账户
然后再将请求提交给某银行原页面对应的接口
这样用户就默默的把钱转到我们账户上了...
- 就是仿照着某个网站页面对后端提交数据的形式, 对其后端提交请求
如果实现CSRF
- 用户输入"对方账户" 的input标签, 不设置 name 属性, 因此后端就获取不到对应的数据
- 利用一个隐藏的input标签, 设置 name 属性, 默认值是我们自己的账户
- 这样后端就只能获取到隐藏input标签携带的值了
{#向真实网站接口提交请求#} form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username <input type="text" name="username"></p> {#用户实际需要输入的标签,没有name属性#} <p>target_account<input type="text"></p> {#隐藏的标签, 有name属性和默认值#} <p><input type="text" value="bigb" name="target_account" style="display: none"></p> <p>money<input type="text" name="money"></p> <input type="submit"> </form>
如何避免CSRF
解决思路: 只处理自己网页发送的post请求--->如何确定post请求来自自己的网页
--->在返回html页面中的form表单中添加一个隐藏的input框, 携带一个随机字符串
--->后端中间件通过这个随机字符串来判断是否是本网站发送的请求
form表单
{#直接在form表单中添加 {% csrf_token %} 即可#} <form action="" method="post"> {% csrf_token %} <p>username <input type="text" name="username"></p> <p>target_account<input type="text" name="target_account"></p> <p>money<input type="text" name="money"></p> <input type="submit"> </form> {#然后我们在前端检查代码, 会发现多了个隐藏的input标签#} <input type="hidden" name="csrfmiddlewaretoken" value="eYT1yNAroJidl3qjDSLSe3U2o0OTQXjD8PcSb2crNDTQijAGjRHX7ual6Y9bLg4D">
- ajax
{#方式一: 将这个随机字符串添加到ajax向后端提交的data当中即可#} {% csrf_token %} <script> $('#d1').click(function () { $.ajax({ url: '', type: 'post', //方式一: 利用标签的name属性查找标签并获取对应的随机字符串 data:{'username':'bigb', 'csrfmiddlewaretoken':$('input[name='csrfmiddlewaretoken']').val()} //方式二: 'csrfmiddlewaretoken':'{{ csrf_token }}' data:{'username':'bigb', 'csrfmiddlewaretoken':'{{ csrf_token }}'} success: function (data) { } }) })</script>
{#方式二: 将如下代码放入一个静态js文件中, 再在html文件引入即可#} //获取 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'); //设置 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); } } });
CSRF相关的两个装饰器
导入:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
csrf_exempt
: 该视图函数对应的页面不做csrf校验csrf_protect
: 该视图函数对应的页面进行csrf校验
from django.views.decorators.csrf import csrf_exempt, csrf_protect @csrf_exempt # 不校验 def index(request): return HttpResponse('index') @csrf_protect # 校验 def index(request): return HttpResponse('index')
- 在CBV上加装这两个装器
- csrf_exempt只能给dispatch方法装才能生效
- csrf_protect怎么装都可以
from django.views.decorators.csrf import csrf_exempt, csrf_protect class MyIndex(views.View): @method_decorator(csrf_exempt) # csrf_exempt只能给dispatch方法装才能生效 def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get(self, request): renturn render(request, 'index.html') @method_decorator(csrf_protect) # csrf_protect三种方法都可以 def post(self, request): return HttpResponse('index')
auth模块
常用方法
- 创建管理员用户
#命令行输入 : python manage.py makesuperuser
- 常用方法
# 1.创建用户(注册) # 导入模块 from django.contrib.auth.models import User # 创建普通用户 creatr_user User.objects.create_user(username=username, password=password) # 创建管理员用户 creater_superuser User.objects.create_superuser(username=username, password=password, email=email) # 2.校验用户名和密码是否正确(登录) # 导入模块 from django.contrib import auth # 必须传入username和password, 返回用户对象 user_obj = auth.authenticate(request, username=username, password=password) if user_obj: return HttpResponse('登录成功!') else: return HttpResponse('密码或用户错误!') # 3.保存用户状态(session) # 执行该代码后, 后面就可以通过 request.user 获取当前登录的用户对象 auth.login(request, user_obj) # 4.判断用户是否登录 request.user.is_authenticated() # 5.校验原密码是否正确(修改密码时需要先输入原密码) request.user.check_password(old_password) # 6.修改密码 request.user.set_password(new_password) # 7.注销 auth.logout(request) # 8. 登录认证装饰器 # 导入模块 from django.contrib.auth.decorators import login_required # 局部配置跳转地址 @login_required(login_url='/login/') def index(request): return HttpRespone('index') # 全局配置配置跳转地址 # 在settings.py中配置 LOGIN_URL = /'login'/ @login_required def index(request): return HttpRespone('index')
扩展auth_user表中的字段
# 1.利用一对一表关系扩展字段 class UserDetail(models.Model): phone = models.CharField(max_length=11) addr = models.CharField(max_length=255) user = models.OneToOneField(to='User') # 2.利用面向对象的继承新建一个表, 替换掉原来的User表 from django.contrib.auth.models import AbstractUser # 继承了AbstracterUser中所有的字段 class UserInfo(AbstractUser): # 扩展字段 phone = models.CharField(max_length=11) addr = models.CharField(max_length=255) # 需要在settigs.py中将默认表指定为UserInfo AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名