[TOC]javascript
csrf:Cross Site Request Forgery protectioncss
# start.py import notify notify.send_all('小宝贝们!快要放假啦,大家都去哪里玩呀?') # settings.py NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', 'notify.wechat.WeChat' ] # notify文件夹下(start.py和.settings.py和notify文件夹在同一目录下) # __init__.py # coding:utf8 import settings import importlib def send_all(content): for path in settings.NOTIFY_LIST: file_path,cls_name = path.rsplit('.',1) file_name = importlib.import_module(file_path) cls = getattr(file_name,cls_name) obj = cls() obj.send(content) # email.py # coding:utf8 class Email: def __init__(self): pass def send(self,content): print('邮件通知:%s'%content) # msg.py # coding:utf8 class Msg: def __init__(self): pass def send(self,content): print('短信通知:%s'%content) # wechat.py # coding:utf8 class WeChat: def __init__(self): pass def send(self,content): print('微信通知:%s'%content)
钓鱼网站 你本身写一个跟中国银行正规网站如出一辙的页面 用户输入用户名 密码 对方帐户 转帐金额提交 请求确实是朝中国银行的接口发送的 钱也扣了 可是对方帐户变了 变成了钓鱼网站本身提早设置好的帐户 如何实现 你在写form表单的时候 让用户填写的对方帐户input并无name属性 而是你本身在内部偷偷隐藏了一个具备name属性的input框 而且value值是你本身的帐户 而后将该标签隐藏了 模拟该现象的产生 建立两个django项目
ps:html
def transfer(request): if request.method =='POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s给%s转帐了%s元钱!'%(username,target_user,money)) return render(request,'transfer.html')
<!--正经网站--> <body> <p>这是legal_site</p> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>target_account:<input type="text" name="target_user"></p> <p>money:<input type="text" name ="money"></p> <input type="submit"> </form> </body> <!--钓鱼网站--> <body> <p>这是fake_site</p> <!--不指定路径,默认往当前网站提交,因此这里要伪形成往真正的网站站点提交,真网站后台获取的是钓鱼网站隐藏的用户名的input框指定的value属性值(默认值,就是骗子帐户),那么以后转帐的处理功能可定就是往这个骗子帐户转钱了--> <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <!--吧用户转给真正目标用户的input框的name属性删除了,本身再多加一个input框添加上name属性,并设上value(默认值)属性(骗子的帐户名),并把该标签隐藏,后端获取name属性的username,就是骗子设置value属性的骗子帐户--> <p>target_account:<input type="text" > <input type="text" name="target_user" value="zhang" style="display:none"> </p> <p>money:<input type="text" name ="money"></p> <input type="submit"> </form> </body>
如何解决该问题: 只处理本网站发送的post请求 如何识别如何判断当前请求是不是本网张发出的 解决: 网站在返回给用户一个form表单的时候 会自动在该表单隐藏一个input框 这个框的value是一个随机字符串 可是网站可以记住给每个浏览器发送的随机字符串,并与本身后台保存的随机字符串比较,二者一致则经过,不然就返回不存在(403),再也不对该请求作处理;
在写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_user"></p> <p>money:<input type="text" name="money"></p> <input type="submit"> </form>
下图是网站在form表单隐藏的随机字符串,那么属性就是settings文件中的django自带的七个中间件中的一个,之后用form表单发送post请求时,表单内写上{% csrf_token %},就不须要注释settings中的中间件django.middleware.csrf.CsrfViewMiddleware
了。前端
ajax方式2:java
较为繁琐python
先在页面任意的位置上书写{% csrf_token %},而后在发送ajax请求的时候 经过标签查找获取随机字符串添加到data自定义对象便可。jquery
<!--ajax方式1:--> {% csrf_token %} <button id="d1">发送ajax</button> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'zhang','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, success:function (data) { } }) }) </script>
ajax方式2:ajax
较为简单数据库
在ajax方式1的基础上,将ajax内部参数data改为以下方式,这样就不须要在任意位置写{{% csrf_token %}}了, 由于data中的模板语法{{csrf_token}}就至关于一个变量,帮咱们生成一个随机字符串。django
<!--ajax方式2:--> data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
ajax方式3:
最通用的一种方式
官网提供的文件,直接兴建js文件拷贝代码 导入便可 你不须要作任何的csrf相关的代码书写
第一步:先在settings文件中配置static路径:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static') ]
第二部:创建static文件夹,自内部建一个js文件,将以下代码拷进去;
// setup.js文件 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); } } });
第三步:在html页面上经过导入该文件便可自动帮咱们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件以前,须要先导入jQuery,由于这个配置文件内的内容是基于jQuery来实现的)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <button id="d1">发送ajax</button> <!--导入该js文件--> <script src="/static/setup.js"></script> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'zhang'}, success:function (data) { alert(data) } }) }) </script> </body> </html>
scrf详见===>Djagno官方文档中关于CSRF的内容](https://docs.djangoproject.com/en/1.11/ref/csrf/)
将settings中csrf中间件注释掉,表明全部的视图函数在浏览器的请求都不校验;
注释掉csrf中间件的状况下,若是想对某个post请求的视图函数校验,能够加装饰器@csrf_protect
;
没注释掉csrf中间件的状况下,若是朝某个视图函数发送post请求,排除校验,能够加装饰器@csrf_exempt
这两个装饰器跟settings中的csrf中间配置没有关系,它内部源码自动调用csrf。
from django.shortcuts import render,HttpResponse from django.views.decorators.csrf import csrf_exempt,csrf_protect # @csrf_exempt # 不校验 csrf def index(request): return HttpResponse('index') @csrf_protect # 校验 def login(request): return HttpResponse('login')
csrf_exempt和csrf_protect装饰器在CBV上的不一样:
csrf_exempt:这个装饰器只能给dispatch装才能生效
csrf_protect:全均可以,跟普通的装饰器装饰CBV一致
ps:csrf_exempt
# @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法 @method_decorator(csrf_exempt,name='dispatch') # 意思是给其类内部名为dispatch的函数装装饰器,支持该方法。 class MyIndex(views.View): # @method_decorator(csrf_exempt) # 能够 def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return render(request,'transfer.html') # @method_decorator(csrf_exempt,name='post') # csrf_exempt不支持该方法 def post(self,request): return HttpResponse('OK')
ps:csrf_protect
# @method_decorator(csrf_protect,name='post') # 能够 class MyIndex(views.View): @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return render(request,'transfer.html') # @method_decorator(csrf_protect) # 能够 def post(self,request): return HttpResponse('OK')
django有两个配置文件 一个是暴露给用户能够配置的
一个是内部全局的(用户配置了就用用户的 用户没有配就用本身的)
from django.conf import global_settings from django.conf import settings obj.name = 'zhang' # 全局 obj.name = 'li' # 局部 先加载全局配置 给对象设置 而后在加载局部配置 再给对象设置 一旦有重复的项 后者覆盖前者
class Settings(object): def __init__(self, settings_module): # 项目名:settings # update this dict from global settings (but only for ALL_CAPS settings) for setting in dir(global_settings): # 获取django全局配置文件中全部的变量名 if setting.isupper(): # 只有大写的才能经过 setattr(self, setting, getattr(global_settings, setting)) # self就是settings # store the settings module in case someone later cares self.SETTINGS_MODULE = settings_module mod = importlib.import_module(self.SETTINGS_MODULE) # from 项目名 import settings 导入暴露给用户的配置文件 # mod就是模块settings tuple_settings = ( "INSTALLED_APPS", "TEMPLATE_DIRS", "LOCALE_PATHS", ) self._explicit_settings = set() for setting in dir(mod): # 获取暴露给用户的配置文件中全部的变量名 if setting.isupper(): # 变量名必须是大写 setting_value = getattr(mod, setting) # 利用反射获取大写变量名对应的值 if (setting in tuple_settings and not isinstance(setting_value, (list, tuple))): raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting) setattr(self, setting, setting_value) # self._explicit_settings.add(setting) if not self.SECRET_KEY: raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. zoneinfo_root = '/usr/share/zoneinfo' if (os.path.exists(zoneinfo_root) and not os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))): raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE) # Move the time zone info into os.environ. See ticket #2315 for why # we don't do this unconditionally (breaks Windows). os.environ['TZ'] = self.TIME_ZONE time.tzset() def is_overridden(self, setting): return setting in self._explicit_settings def __repr__(self): return '<%(cls)s "%(settings_module)s">' % { 'cls': self.__class__.__name__, 'settings_module': self.SETTINGS_MODULE, }
Auth模块使Django自带的用户认证模块,它内置了强大的用户认证系统--auth,默认使用auth-user表来存储用户数据;这样开发人员就不用手写django-ORM部分的登陆、注册,用户认证、注销、修改密码等功能,以及建立user表的麻烦。
首先,执行数据库迁移命令,找到auth-user这张表,内部有:password、last_login、is_superuser first_name last_name email is_staff is_active date_joined username 字段。
而后建立按超级用户:
python3 manage.py createsuperuser # cmd执行该命令
须要输入建立超级用户的用户名和密码以及再次建立密码
1.建立用户
User.objects.create(username=username,password=password) # 不可用 密码不是加密的 User.objects.create_user(username=username,password=password) # 建立普通用户 密码自动加密 # 建立超级用户 须要邮箱数据,密码自动加密 User.objects.create_superuser(username=username,password=password,email='123@qq.com')
from django.contrib.auth.models import User def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # User.objects.create(username=username,password=password) # User.objects.create_user(username=username,password=password) User.objects.create_superuser(username=username,password=password,email='111@qq.com') return HttpResponse('注册成功!!') return render(request,'register.html')
2.校验用户名和密码是否正确
from django.contrib import auth # 必须传用户名和密码两个参数缺一不能 user_obj = auth.authenticate(request,username=username,password=password)
3.保存用户登陆状态
auth.login(request,user_obj) # 只要这句话执行了 后面在任意位置 只要你能拿到request你就能够经过request.user获取到当前登陆的用户对象
def login_dunc(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 校验当前用户名和密码是否正确 # models.User.objects.filter(username=username,password=password) # res = User.objects.filter(username=username,password=password) # 密码没法校验,前端传的是明文,后端存的是密文 # print(res) res = auth.authenticate(request,username=username,password=password) # 自动加密密码 而后去数据库校验 # print(res) # 数据对象 # print(res.username) # print(res.password) # 密码是密文 if res: # 保存用户登陆状态 # request.session['user'] = 'zhang' auth.login(request,res) """ 只要执行了这一句话 以后你能够在任意位置 经过request.user获取到当前登陆用户对象 """ return HttpResponse('ok') return render(request,'login_dunc.html')
4.判断当前用户是否登陆
request.user.is_authenticated()
def get_user(request): print(request.user) # AnonymousUser 匿名用户 print(type(request.user)) # <class'django.utils.functional'> print(request.user.is_authenticated()) # 判断当前用户是否登陆,返回布尔值 return HttpResponse('ok')
5.校验原密码是否正确
request.user.check_password(old_password)
6.修改密码
request.user.set_password(new_password) request.user.save() # 修改密码必定要保存
# @login_required(login_url='/lgg/') # 局部配置,如没有登陆,让用户直接跳到登陆页面 @login_required def check_password(request): if request.method == "POST": old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') # 校验老密码是否正确 is_right = request.user.check_password(old_password) if is_right: request.user.set_password(new_password) request.user.save() # 修改密码必定要保存 user_obj = request.user return render(request,'change_password.html',locals())
7.注销
auth.logout(request)
@login_required def logout(request): # request.session.flush() auth.logout(request) return HttpResponse('注销了')
8.校验用户是否登陆装饰器
局部配置
from django.contrib.auth.decorators import login_required @login_required(login_url='/login/') # 局部配置 def index(request): pass
全局配置
settings配置文件中 直接配置 LOGIN_URL = '/login/'
@login_required def index(request): pass # 若是全局配置了 局部也配置 以局部的为准
如何扩展auth_user表字段?
方式1 利用一对一外键字段关系
class UserDetail(models.Model): phone = models.BigIntegerField() user = models.OneToOneField(to='User')
方式2:
利用继承关系
首先先去配置文件settings中配置 AUTH_USER_MODEL = 'app01.Userinfo' # 应用名.表名 以后全部的auth模块功能全都以你写的表为准,因为继承了AbstractUser,会将auth_user那张表与本身建的这张表整合到一块儿,auth_user自己所拥有的功能,userinfo也能照常使用,使表的操做性更强了。
# 表字段写好以后,记得数据库迁移 from django.contrib.auth.models import AbstractUser class Userinfo(AbstractUser): phone = models.Bi gIntegerField() register_time = models.DateField(auto_now_add=True)
conf文件夹、lib文件夹、start.py处于同级
# conf文件夹 # settings.py NAME = '暴露给用户自定义配置'
#lib>conf文件夹 # __init__.py文件 import importlib from lib.conf import global_settings import os class Settings(object): def __init__(self): # print(111111) for name in dir(global_settings): if name.isupper(): setattr(self,name,getattr(global_settings,name)) # 获取暴露给用户的配置文件字符串路径 module_path = os.environ.get('xxx') md = importlib.import_module(module_path) # md = settings for name in dir(md): if name.isupper(): k = name v = getattr(md,name) setattr(self,k,v) settings = Settings() # global_settings.py文件 NAME = '项目默认的配置文件'
# start.py文件 import os import sys BASE_DIR = os.path.dirname(__file__) sys.path.append(BASE_DIR) if __name__ == '__main__': os.environ.setdefault('xxx','conf.settings') from lib.conf import settings print(settings.NAME) # settings就是类Settings的对象(单例)