zoukankan      html  css  js  c++  java
  • 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 配置文件中的 中间件等功能模块
       
  • 相关阅读:
    PLL详解
    CSI-2 协议
    C语言volatile关键字的用法
    联合体(union)的使用方法及其本质
    Linux驱动之LED驱动编写
    insmod 和第一个驱动
    黑电平校正BLC
    什么是HDR?
    使用starUML画顺序图
    UML 资料整理
  • 原文地址:https://www.cnblogs.com/Fzhiyuan/p/11587871.html
Copyright © 2011-2022 走看看