8中间件,csrf跨站请求伪造,auth模块

昨日内容回顾
 多对多三种创建方式
  1.全自动
   完全依赖于ManyToMany让django orm自动创建第三张表
   优势:不需要你创建第三张表  自动创建
   不足:第三张表扩展性
  2.纯手动
   不依赖于ManyToMany,自己创建第三张表,里面是有ForeignKey自己做两张表的关联
   优势:第三张可以新增任意字段 扩展性较高
   不足:orm查询时较为繁琐
  3.半自动(推荐使用 因为可以给你后续的项目迭代提供方便)
   依赖于ManyToMany,但是自己创建第三张表
   优势:可扩展性高
   不足:多对多字段方法不支持了(add,set,remove,clear)
 
 forms组件
  forms组件三大功能:
   1.渲染前端获取用户输入的标签
   2.校验数据
   3.展示错误信息
   注意:forms组件只能渲染获取用户输入的标签  提交按钮需要用户自己写
  
  1.基本使用
   forms组件中所有的字段默认都是必填的(默认就是required=True)
   from  django import forms
   
   class MyForm(forms.Form):
    username = forms.CharField(max_length=8)
    email = forms.EmailField()
  2.先生成一个对象
   form_obj = MyForm({'username':'jason','email':'123@qq.com'})
  3.判断数据是否合法
   form_obj.is_valid()
   # 只有数据全部满足条件才会返回True
  4.获取符合条件的数据
   form_obj.cleaned_data
  5.获取不符合条件的数据的报错原因
   form_obj.errors
  6.渲染前端页面  先生成一个空对象 并将对象传递给前端页面
   form_obj = MyForm()
  7.前端页面有三种渲染方式
   1.{{form_obj.as_p}}  # 封装程度太高 可扩展性差
   2.{{ form_obj.username.label }}{{form_obj.username}}  # 书写的内容太多了
   3. {% for form in form_obj %}  # 使用频率较高
     <p> {{form.label}}{{ form }}</p>
    {% endfor %}
  8.其他属性操作
   from  django.forms import widgets
   username = forms.CharField(max_length=8,label='用户名',required=False,
          error_message={
           'max_length':"用户名最大八位",
           'required':'用户名不能为空'
          },
          widget=widgets.TextInput(attrs={'class':'form-control'},
          validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')]
          )
         )
  9.钩子函数(Hook)
   局部钩子 (针对某一个字段做一些额外的校验)
   from app01 import models
   def clean_username(self):
    # 当前用户名是否已经被注册
    username = self.cleaned_data.get('username')
    is_user = models.User.objects.filter(username=username)
    if is_user:
     self.add_error('username','用户名已经存在')
    if '西游记' in username:
     self.add_error('username','不符合社会主义核心价值观')
    return username
   
   全局钩子(针对多个字段的校验)
   def clean(self):
    password = self.cleaned_data.get('password')
    confirm_password = self.cleaned_data.get('confirm_password')
    if not password == confirm_password:
     self.add_error('confirm_password','两次密码不一致')
    return self.cleaned_data
  10.其他字段及参数
   initial  默认值
   如果是单选的话  统一都是forms.ChocieField(
     widget=widgets.Select(),
     widget=widgets.RadioSelect(),
     widget=widgets.CheckboxSelect(),
   )
  
 
 Cookie与Session
  由于http协议是无状态
  
  cookie是保存在客户端浏览器上的
  session是保存服务端上的
  session是依赖于cookie的,所有的保存用户登录状态或者各种校验基本都需要
  依赖于cookie
  
  django操作cookie与session
   操作的cookie要利用HttpResponse对象
   obj = HttpResponse()
   obj.set_cookie('k1','v1',max_age=100)
   return obj
   
   
   request.COOKIES.get('k1')
   
   request.session['name'] = 'jason'  # 三件事
   # 1.生成一个随机字符串
   # 2.将随机字符串和要保存的数据写入django_session表中(django默认session过期时间14天)
   # 3.将随机字符串返回给客户端浏览器
   """
   服务端保存用户信息 不一定非要在django_session表中保存
   可以利用其他数据库或者换成作为session的暂存地
   """
   
   
   request.session.get('name')  # 三件事
   
   
   # 删除当前会话的所有Session数据
   request.session.delete()
     
   # 删除当前的会话数据并删除会话的Cookie。
   request.session.flush()
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    例如,django.contrib.auth.logout() 函数中就会调用它。
   # 设置会话Session和Cookie的超时时间
   request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。
  
  利用cookie写了装饰器
  
  作业用session
 
 
 今日内容
  django中间件
   django中间件是类似于是django的保安
   请求的时候需要先经过中间件才能到达django后端(urls,views,templates,models)
   响应走的时候也需要经过中间件才能到达web服务网关接口
   
   
   django默认的七个中间件
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
       ]
    
    
    class SecurityMiddleware(MiddlewareMixin):
     def __init__(self, get_response=None):
      self.sts_seconds = settings.SECURE_HSTS_SECONDS
      self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
      self.sts_preload = settings.SECURE_HSTS_PRELOAD
      self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
      self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
      self.redirect = settings.SECURE_SSL_REDIRECT
      self.redirect_host = settings.SECURE_SSL_HOST
      self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
      self.get_response = get_response
     def process_request(self, request):
      path = request.path.lstrip("/")
      if (self.redirect and not request.is_secure() and
        not any(pattern.search(path)
          for pattern in self.redirect_exempt)):
       host = self.redirect_host or request.get_host()
       return HttpResponsePermanentRedirect(
        "https://%s%s" % (host, request.get_full_path())
       )
     def process_response(self, request, response):
      if (self.sts_seconds and request.is_secure() and
        'strict-transport-security' not in response):
       sts_header = "max-age=%s" % self.sts_seconds
       if self.sts_include_subdomains:
        sts_header = sts_header + "; includeSubDomains"
       if self.sts_preload:
        sts_header = sts_header + "; preload"
       response["strict-transport-security"] = sts_header
      if self.content_type_nosniff and 'x-content-type-options' not in response:
       response["x-content-type-options"] = "nosniff"
      if self.xss_filter and 'x-xss-protection' not in response:
       response["x-xss-protection"] = "1; mode=block"
      return response
    
    class CsrfViewMiddleware(MiddlewareMixin):
      if settings.CSRF_USE_SESSIONS:
       request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE']
      else:
       response.set_cookie(
        settings.CSRF_COOKIE_NAME,
        request.META['CSRF_COOKIE'],
        max_age=settings.CSRF_COOKIE_AGE,
        domain=settings.CSRF_COOKIE_DOMAIN,
        path=settings.CSRF_COOKIE_PATH,
        secure=settings.CSRF_COOKIE_SECURE,
        httponly=settings.CSRF_COOKIE_HTTPONLY,
       )
       # Set the Vary header since content varies with the CSRF cookie.
       patch_vary_headers(response, ('Cookie',))
     def process_request(self, request):
      csrf_token = self._get_token(request)
      if csrf_token is not None:
       # Use same token next time.
       request.META['CSRF_COOKIE'] = csrf_token
     def process_view(self, request, callback, callback_args, callback_kwargs):
      if getattr(request, 'csrf_processing_done', False):
       return None
      # Wait until request.META["CSRF_COOKIE"] has been manipulated before
      # bailing out, so that get_token still works
      if getattr(callback, 'csrf_exempt', False):
       return None
      # Assume that anything not defined as 'safe' by RFC7231 needs protection
      if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
       if getattr(request, '_dont_enforce_csrf_checks', False):
        # Mechanism to turn off CSRF checks for test suite.
        # It comes after the creation of CSRF cookies, so that
        # everything else continues to work exactly the same
        # (e.g. cookies are sent, etc.), but before any
        # branches that call reject().
        return self._accept(request)
       if request.is_secure():
        # Suppose user visits http://example.com/
        # An active network attacker (man-in-the-middle, MITM) sends a
        # POST form that targets https://example.com/detonate-bomb/ and
        # submits it via JavaScript.
        #
        # The attacker will need to provide a CSRF cookie and token, but
        # that's no problem for a MITM and the session-independent
        # secret we're using. So the MITM can circumvent the CSRF
        # protection. This is true for any HTTP connection, but anyone
        # using HTTPS expects better! For this reason, for
        # https://example.com/ we need additional protection that treats
        # http://example.com/ as completely untrusted. Under HTTPS,
        # Barth et al. found that the Referer header is missing for
        # same-domain requests in only about 0.2% of cases or less, so
        # we can use strict Referer checking.
        referer = force_text(
         request.META.get('HTTP_REFERER'),
         strings_only=True,
         errors='replace'
        )
        if referer is None:
         return self._reject(request, REASON_NO_REFERER)
        referer = urlparse(referer)
        # Make sure we have a valid URL for Referer.
        if '' in (referer.scheme, referer.netloc):
         return self._reject(request, REASON_MALFORMED_REFERER)
        # Ensure that our Referer is also secure.
        if referer.scheme != 'https':
         return self._reject(request, REASON_INSECURE_REFERER)
        # If there isn't a CSRF_COOKIE_DOMAIN, require an exact match
        # match on host:port. If not, obey the cookie rules (or those
        # for the session cookie, if CSRF_USE_SESSIONS).
        good_referer = (
         settings.SESSION_COOKIE_DOMAIN
         if settings.CSRF_USE_SESSIONS
         else settings.CSRF_COOKIE_DOMAIN
        )
        if good_referer is not None:
         server_port = request.get_port()
         if server_port not in ('443', '80'):
          good_referer = '%s:%s' % (good_referer, server_port)
        else:
         # request.get_host() includes the port.
         good_referer = request.get_host()
        # Here we generate a list of all acceptable HTTP referers,
        # including the current host since that has been validated
        # upstream.
        good_hosts = list(settings.CSRF_TRUSTED_ORIGINS)
        good_hosts.append(good_referer)
        if not any(is_same_domain(referer.netloc, host) for host in good_hosts):
         reason = REASON_BAD_REFERER % referer.geturl()
         return self._reject(request, reason)
       csrf_token = request.META.get('CSRF_COOKIE')
       if csrf_token is None:
        # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
        # and in this way we can avoid all CSRF attacks, including login
        # CSRF.
        return self._reject(request, REASON_NO_CSRF_COOKIE)
       # Check non-cookie token for match.
       request_csrf_token = ""
       if request.method == "POST":
        try:
         request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
        except IOError:
         # Handle a broken connection before we've completed reading
         # the POST data. process_view shouldn't raise any
         # exceptions, so we'll ignore and serve the user a 403
         # (assuming they're still listening, which they probably
         # aren't because of the error).
         pass
       if request_csrf_token == "":
        # Fall back to X-CSRFToken, to make things easier for AJAX,
        # and possible for PUT/DELETE.
        request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
       request_csrf_token = _sanitize_token(request_csrf_token)
       if not _compare_salted_tokens(request_csrf_token, csrf_token):
        return self._reject(request, REASON_BAD_TOKEN)
      return self._accept(request)
     def process_response(self, request, response):
      if not getattr(request, 'csrf_cookie_needs_reset', False):
       if getattr(response, 'csrf_cookie_set', False):
        return response
      if not request.META.get("CSRF_COOKIE_USED", False):
       return response
      # Set the CSRF cookie even if it's already set, so we renew
      # the expiry timer.
      self._set_token(request, response)
      response.csrf_cookie_set = True
      return response
    
        
    class AuthenticationMiddleware(MiddlewareMixin):
     def process_request(self, request):
      assert hasattr(request, 'session'), (
       "The Django authentication middleware requires session middleware "
       "to be installed. Edit your MIDDLEWARE%s setting to insert "
       "'django.contrib.sessions.middleware.SessionMiddleware' before "
       "'django.contrib.auth.middleware.AuthenticationMiddleware'."
      ) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
      request.user = SimpleLazyObject(lambda: get_user(request))
    
    
    
    django中间件中有五个用户可以自定义的方法
    
     django中间件可以用来做什么(***********************)
      1.网站全局的身份校验,访问频率限制,权限校验...只要是涉及到全局的校验你都可以在中间件中完成
      2.django的中间件是所有web框架中 做的最好的
      
    
     需要我们掌握的方法有
      1.process_request()方法
       规律
        1.请求来的时候 会经过每个中间件里面的process_request方法(从上往下)
        2.如果方法里面直接返回了HttpResponse对象 那么会直接返回 不再往下执行
         基于该特点就可以做访问频率限制,身份校验,权限校验
         
      2.process_response()方法
       规律
        1.必须将response形参返回 因为这个形参指代的就是要返回给前端的数据
        2.响应走的时候 会依次经过每一个中间件里面的process_response方法(从下往上)
        
     需要了解的方法
      3.process_view()
       1.在路由匹配成功执行视图函数之前 触发
       走完process_request方法就执行process_view(),然后执行process_response()方法
       
      4.process_exception()
       1.当你的视图函数报错时  就会自动执行
       
      5.process_template_response()
       1.当你返回的HttpResponse对象中必须包含render属性才会触发
       def index(request):
        print('我是index视图函数')
        def render():
         return HttpResponse('什么鬼玩意')
        obj = HttpResponse('index')
        obj.render = render
        return obj
    总结:你在书写中间件的时候 只要形参中有repsonse 你就顺手将其返回 这个reponse就是要给前端的消息
    
    
    如何自定义我们自己的中间件,研究这上面五个方法都有哪些特点********
      1.如果你想让你写的中间件生效 就必须要先继承MiddlewareMixin
      2.在注册自定义中间件的时候 一定要确保路径不要写错
    
     案例:
      from django.shortcuts import HttpResponse
      from django.utils.deprecation import MiddlewareMixin
      class MyMdd(MiddlewareMixin):
       def process_request(self,request):
        print('我是第一个中间件process_request')
       def process_response(self,request,response):
        print('我是第一个中间件里面的process_response')
        return response
           def process_view(self,request,view_func,view_args,view_kwargs):
         print(view_func)
         print(view_args)
         print(view_kwargs)
         print('我是第一个中间件里的process_view')
        def process_exception(self,request, exception): 当你的视图函数报错时  就会自动执行
         print('我是第一个中间件里面的process_exception')    
        
      class MyMdd1(MiddlewareMixin):
       def process_request(self,request):
        print('我是第二个中间件process_request')
        #当中间件里面的process_request方法返回了一个HttpResponse对象
        # 那么就不会再继续向下执行,而是直接跳到同级别的procsee_response直接往回走
        return HttpResponse('嘿嘿嘿')
       def process_response(self,request,response):
        print('我是第二个中间件里面的process_response')
        return response
      class MyMdd2(MiddlewareMixin):
       def process_request(self,request):
        print('我是第三个中间件process_request')
       def process_response(self,request, response):# 括号里面别忘记response
        print('我是第三个中间件里面的process_response')
        return response    # 此时的response就是视图函数里返回的Http
        
       
  
  csrf跨站请求伪造
    钓鱼网站
    通过制作一个跟正儿八经的网站一模一样的页面,骗取用户输入信息 转账交易
    从而做手脚
     转账交易的请求确确实实是发给了中国银行,账户的钱也是确确实实少了
     唯一不一样的地方在于收款人账户不对
    内部原理
     在让用户输入对方账户的那个input上面做手脚
     给这个input不设置name属性,在内部隐藏一个实现写好的name和value属性的input框
     这个value的值 就是钓鱼网站受益人账号
     
    防止钓鱼网站的思路
     网站会给返回给用户的form表单页面 偷偷塞一个随机字符串
     请求到来的时候 会先比对随机字符串是否一致  如果不一致  直接拒绝(403)
     例如用户在登录中国银行的时候,会产生一个唯一的随机的字符串,在下一次用户登录的时候将携带这个wo
     能识别的唯一的字符串与我储存的字符串进行一一比对,比对上说明客户登录的就是我网站的用户,比对不上就是钓鱼网站
     
    该随机字符串有以下特点
     1.同一个浏览器每一次访问都不一样
     2.不同浏览器绝对不会重复
     
  1.form表单发送post请求的时候  需要你做得仅仅书写一句话
   {% csrf_token %}*******
   在你返回给用户的form表单中,会偷偷的塞一对键值对,塞一组信息,当用户再次发送请求时,我会偷偷的先
   拿到那对键值,进行比对,如果比对上,说明你是我本网站用户,接下来在进行用户操作,然后在进行校验
   即比对成功,才会让你输入用户名,每一次的token都不一样,钓鱼网站token和正规网站token不一样,正规的网站它会记录自己的token,
   钓鱼网站的token如果和正规网站token一样,但是这个token不是正规网站给你的,所以无法访问
  2.ajax发送post请求 如何避免csrf校验
   1.现在页面上写{% csrf_token %},利用标签查找  获取到该input键值信息
    {'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
    $('[name=csrfmiddlewaretoken]').val()     #固定用法
    
   2.直接书写'{{ csrf_token }}'
    {'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
    {{ csrf_token }}
   
   3.你可以将该获取随机键值对的方法 写到一个js文件中,之后只需要导入该文件即可
    新建一个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);
     }
      }
    });
  
     
  
  1.当你网站全局都需要校验csrf的时候 有几个不需要校验该如何处理
  2.当你网站全局不校验csrf的时候 有几个需要校验又该如何处理
   from django.utils.decorators import method_decorator 
   from django.views.decorators.csrf import csrf_exempt,csrf_protect
   # 这两个装饰器在给CBV装饰的时候 有一定的区别
   如果是csrf_protect 那么有三种方式
    # 第一种方式
    # @method_decorator(csrf_protect,name='post')  # 有效的
    class MyView(View):
     # 第三种方式
     # @method_decorator(csrf_protect)
     def dispatch(self, request, *args, **kwargs):
      res = super().dispatch(request, *args, **kwargs)
      return res
     def get(self,request):
      return HttpResponse('get')
     # 第二种方式
     # @method_decorator(csrf_protect)  # 有效的
     def post(self,request):
      return HttpResponse('post')
     
   如果是csrf_exempt 只有两种(只能给dispatch装)   特例
   @method_decorator(csrf_exempt,name='dispatch')  # 第二种可以不校验的方式
   class MyView(View):
    # @method_decorator(csrf_exempt)  # 第一种可以不校验的方式
    def dispatch(self, request, *args, **kwargs):
     res = super().dispatch(request, *args, **kwargs)
     return res
    def get(self,request):
     return HttpResponse('get')
    def post(self,request):
     return HttpResponse('post')
  
  总结 装饰器中只有csrf_exempt是特例,其他的装饰器在给CBV装饰的时候 都可以有三种方式
  
  课后作业:
   将昨天的写的登陆认证装饰器 加载CBV上
   
  auth模块
   如果你想用auth模块   那么你就用全套
   
   
   
   跟用户相关的功能模块
    用户的注册 登陆 验证 修改密码 ...
   
   执行数据库迁移命令之后  会生成很多表  其中的auth_user是一张用户相关的表格
   添加数据
    createsuperuser  创建超级用户 这个超级用户就可以拥有登陆django admin后台管理的权限
  
  auth模块的功能
   查询用户
    from django.contrib import auth
    user_obj = auth.authenticate(username=username,password=password)  # 必须要用 因为数据库中的密码字段是密文的 而你获取的用户输入的是明文
   记录用户状态
    auth.login(request,user_obj)  # 将用户状态记录到session中
   判断用户是否登录
    print(request.user.is_authenticated)  # 判断用户是否登录  如果是你们用户会返回False
   用户登录之后 获取用户对象
    print(request.user)  # 如果没有执行auth.login那么拿到的是匿名用户
   校验用户是否登录
    from django.contrib.auth.decorators import  login_required
    @login_required(login_url='/xxx/')  # 局部配置
    def index(request):
     pass
    
    # 全局配置  settings文件中
    LOGIN_URL = '/xxx/'
   验证密码是否正确
    request.user.check_password(old_password)
   修改密码 
    request.user.set_password(new_password)
    request.user.save()  # 修改密码的时候 一定要save保存 否则无法生效
   退出登陆
    auth.logout(request)  # request.session.flush()
   注册用户
     # User.objects.create(username =username,password=password)  # 创建用户名的时候 千万不要再使用create 了
     # User.objects.create_user(username =username,password=password)  # 创建普通用户
     User.objects.create_superuser(username =username,password=password,email='123@qq.com')  # 创建超级用户  邮箱必填
  
  
  自定义auth_user表
   from django.contrib.auth.models import AbstractUser
   # Create your models here.
   # 第一种 使用一对一关系  不考虑
 
   # 第二种方式   使用类的继承
   class Userinfo(AbstractUser):
    # 千万不要跟原来表中的字段重复 只能创新
    phone = models.BigIntegerField()
    avatar = models.CharField(max_length=32)
   
   # 一定要在配置文件中 告诉django
   # 告诉django  orm不再使用auth默认的表  而是使用你自定义的表
   AUTH_USER_MODEL = 'app01.Userinfo'  # '应用名.类名'
  
  
  1.执行数据库迁移命令
   所有的auth模块功能 全部都基于你创建的表
   而不再使用auth_user
   
   
  
  settings功能插拔式源码
   参考django 配置文件中的 中间件等功能模块
   
原文地址:https://www.cnblogs.com/Fzhiyuan/p/11587871.html