【python】-- Django 分页 、cookie、Session、CSRF

Django  分页 、cookie、Session、CSRF

一、分页

分页功能在每个网站都是必要的,下面主要介绍两种分页方式:

1、Django内置分页

from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

L = []
for i in range(999):
    L.append(i)

def index(request):
    current_page = request.GET.get('p')

    paginator = Paginator(L, 10)
    # per_page: 每页显示条目数量
    # count:    数据总个数
    # num_pages:总页数
    # page_range:总页数的索引范围,如: (1,10),(1,200)
    # page:     page对象
    try:
        posts = paginator.page(current_page)
        # has_next              是否有下一页
        # next_page_number      下一页页码
        # has_previous          是否有上一页
        # previous_page_number  上一页页码
        # object_list           分页之后的数据列表
        # number                当前页
        # paginator             paginator对象
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)
    return render(request, 'index.html', {'posts': posts})
views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul>
    {% for item in posts %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

<div class="pagination">
      <span class="step-links">
        {% if posts.has_previous %}
            <a href="?p={{ posts.previous_page_number }}">Previous</a>
        {% endif %}
          <span class="current">
            Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
          </span>
          {% if posts.has_next %}
              <a href="?p={{ posts.next_page_number }}">Next</a>
          {% endif %}
      </span>

</div>
</body>
</html>
HTML
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger


class CustomPaginator(Paginator):
    def __init__(self, current_page, max_pager_num, *args, **kwargs):
        """
        :param current_page: 当前页
        :param max_pager_num:最多显示的页码个数
        :param args:
        :param kwargs:
        :return:
        """
        self.current_page = int(current_page)
        self.max_pager_num = max_pager_num
        super(CustomPaginator, self).__init__(*args, **kwargs)

    def page_num_range(self):
        # 当前页面
        # self.current_page
        # 总页数
        # self.num_pages
        # 最多显示的页码个数
        # self.max_pager_num
        print(1)
        if self.num_pages < self.max_pager_num:
            return range(1, self.num_pages + 1)
        print(2)
        part = int(self.max_pager_num / 2)
        if self.current_page - part < 1:
            return range(1, self.max_pager_num + 1)
        print(3)
        if self.current_page + part > self.num_pages:
            return range(self.num_pages + 1 - self.max_pager_num, self.num_pages + 1)
        print(4)
        return range(self.current_page - part, self.current_page + part + 1)


L = []
for i in range(999):
    L.append(i)

def index(request):
    current_page = request.GET.get('p')
    paginator = CustomPaginator(current_page, 11, L, 10)
    # per_page: 每页显示条目数量
    # count:    数据总个数
    # num_pages:总页数
    # page_range:总页数的索引范围,如: (1,10),(1,200)
    # page:     page对象
    try:
        posts = paginator.page(current_page)
        # has_next              是否有下一页
        # next_page_number      下一页页码
        # has_previous          是否有上一页
        # previous_page_number  上一页页码
        # object_list           分页之后的数据列表
        # number                当前页
        # paginator             paginator对象
    except PageNotAnInteger:
        posts = paginator.page(1)
    except EmptyPage:
        posts = paginator.page(paginator.num_pages)

    return render(request, 'index.html', {'posts': posts})
扩展内置分页:views.py
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<ul>
    {% for item in posts %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

<div class="pagination">
<span class="step-links">
{% if posts.has_previous %}
    <a href="?p={{ posts.previous_page_number }}">Previous</a>
{% endif %}

    {% for i in posts.paginator.page_num_range %}
        <a href="?p={{ i }}">{{ i }}</a>
    {% endfor %}

    {% if posts.has_next %}
        <a href="?p={{ posts.next_page_number }}">Next</a>
    {% endif %}
</span>

<span class="current">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}.
</span>

</div>
</body>
</html>
扩展内置分页:Html

2、自定义分页

需求:

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页...)

3、设定显示多少页号

4、获取当前数据总条数

5、根据设定显示多少页号和数据总条数计算出,总页数

6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

7、在数据表中根据起始位置取值,页面上输出数据

8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

2.1、templates下的模板:

<li>{{ item }}</li>
li.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .pagination .page{
            display: inline-block;
            padding: 5px;
            background-color: cyan;
            margin: 5px;
        }
        .pagination .page.active{
            background-color: brown;
            color: white;
        }
    </style>
</head>
<body>
    <ul>
        {% for item in li %}
            {% include 'li.html' %}
        {% endfor %}
    </ul>

    <div class="pagination">
        {{ page_str }}
    </div>
</body>
</html>
user_list.html

2.2、新增utils目录,创建分页工具pagination.py文件。

from django.utils.safestring import mark_safe  # 导入mark_safe函数


class Page:
    def __init__(self, current_page, data_count, per_page_count=10, pager_num=7):
        self.current_page = current_page  # 当前页
        self.data_count = data_count      # 总页数
        self.per_page_count = per_page_count   # 每页显示数据
        self.pager_num = pager_num             # 前端展示的分页数

    @property            # 转换成静态属性
    def start(self):
        """
        定义每页展示数据的开始下标
        :return:
        """
        return (self.current_page - 1) * self.per_page_count

    @property
    def end(self):
        """
        定义每页展示数据的结束下标
        :return:
        """
        return self.current_page * self.per_page_count

    @property
    def total_count(self):
        """
        定义总页数
        :return:
        """
        v, y = divmod(self.data_count, self.per_page_count)   # 内置函数divmod 能够整除返回True,不能整除返回False
        if y:
            v += 1
        return v

    def page_str(self, base_url):
        """
        定义分页规则
        :param base_url:
        :return:
        """
        page_list = []

        if self.total_count < self.pager_num:
            start_index = 1
            end_index = self.total_count + 1
        else:
            if self.current_page <= (self.pager_num + 1) / 2:
                start_index = 1
                end_index = self.pager_num + 1
            else:
                start_index = self.current_page - (self.pager_num - 1) / 2
                end_index = self.current_page + (self.pager_num + 1) / 2
                if (self.current_page + (self.pager_num - 1) / 2) > self.total_count:
                    end_index = self.total_count + 1
                    start_index = self.total_count - self.pager_num + 1

        if self.current_page == 1:
            prev = '<a class="page" href="javascript:void(0);">上一页</a>'
        else:
            prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
        page_list.append(prev)

        for i in range(int(start_index), int(end_index)):
            if i == self.current_page:
                temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
            else:
                temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
            page_list.append(temp)

        if self.current_page == self.total_count:
            nex = '<a class="page" href="javascript:void(0);">下一页</a>'
        else:
            nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
        page_list.append(nex)

        jump = """
        <input type='text'  /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
        <script>
            function jumpTo(ths,base){
                var val = ths.previousSibling.value;
                location.href = base + val;
            }
        </script>
        """ % (base_url,)

        page_list.append(jump)
        # django从view向template传递HTML字符串的时候,django默认不渲染此HTML,原因是为了防止这段字符串里面有恶意攻击的代码(XSS)
        # mark_safe这个函数就是确认这段HTML字符串是安全的,Django会才会渲染此HTML
        page_str = mark_safe("".join(page_list))

        return page_str
pagination.py

2.3、app下的views.py:

from django.shortcuts import render
from utils import pagination

LIST = []
for i in range(500):
    LIST.append(i)


def user_list(request):
    current_page = request.GET.get('p', 1)
    current_page = int(current_page)

    # val = request.COOKIES.get('per_page_count', 10)
    # val = int(val)
    page_obj = pagination.Page(current_page, len(LIST))

    data = LIST[page_obj.start:page_obj.end]

    page_str = page_obj.page_str("/app1/user_list/")

    return render(request, 'user_list.html', {'li': data, 'page_str': page_str})
views.py

 

 

二、Cookie

Cookie 是在 HTTP 协议下,服务器或脚本可以维护客户工作站上信息的一种方式。Cookie 是由 Web 服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息,因此用户不用每次登陆都需要输入账号密码。

1、获取Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    """ 
  参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间
    """

2、设置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...) #创建对象
 
rep.set_cookie(key,value,...) #明文Cookie设置方式
rep.set_signed_cookie(key,value,salt='加密盐',...) #加密加盐Cookie设置方式
    """
    参数:
        key,              键
        value='',         值
        max_age=None,     超时时间  
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
        max_age与expires的区别:max_age用秒设置超时时间,N秒后失效,expires用datetime设置超时时间,到截至时间后失效
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    只能http协议传输,当httponly=True时,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
    """

3、前端页面操作Cookie

由于Cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作Cookie。

JavaScript操作Cookie:
#获取cookie并赋值给变量
var strCookie=document.cookie
#修改cookie中的id值
document.cookie="id=88";
#设置终止时间
###获取当前时间
var date=new Date();
var expireDays=10;
###将date设置为10天以后的时间
date.setTime(date.getTime()+expireDays*60*1000);
###将id和name两个cookie设置为10天后过期
document.cookie="id=77; name=bill; expires="+date.toUTCString();


Jquery操作Cookie:
相对于JS操作cookie,Jquery操作Cookie就简单很多
<script src='/static/js/jquery.cookie.js'></script>   #导入jquery.cookie.js文件,此文件在官网下载
$.cookie("list_pager_num", 30,{ path: '/' }); 

4、根据cookie值显示定制条数示例:

在上面自定义分页示例的基础上,根据cookie值进行定制条数数据显示。

 4.1、templates下的模板:

<li>{{ item }}</li>
li.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .pagination .page{
            display: inline-block;
            padding: 5px;
            background-color: cyan;
            margin: 5px;
        }
        .pagination .page.active{
            background-color: brown;
            color: white;
        }
    </style>
</head>
<body>
    <ul>
        {% for item in li %}
            {% include 'li.html' %}
        {% endfor %}
    </ul>

    <div>
        <select id="ps" onchange="changePageSize(this)">
            <option value="10">10</option>
            <option value="30">30</option>
            <option value="50">50</option>
            <option value="100">100</option>
        </select>
    </div>

    <div class="pagination">
        {{ page_str }}
    </div>
    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script>
        //将选择框的值设置为cookie,在Views中就可以接收cookie
        function changePageSize(ths){
            var v = $(ths).val();
            $.cookie('per_page_count',v);
            location.reload(); //页面重新加载
        }
        //将多选框的值更改为最新的值
         $(function(){
                var v = $.cookie('per_page_count');
                $('#ps').val(v);
        });

    </script>
</body>
</html>
user_list.html

4.2、utils目录下pagination.py文件

from django.utils.safestring import mark_safe  # 导入mark_safe函数


class Page:
    def __init__(self, current_page, data_count, per_page_count=10, pager_num=7):
        self.current_page = current_page  # 当前页
        self.data_count = data_count      # 总页数
        self.per_page_count = per_page_count   # 每页显示数据
        self.pager_num = pager_num             # 前端展示的分页数

    @property            # 转换成静态属性
    def start(self):
        """
        定义每页展示数据的开始下标
        :return:
        """
        return (self.current_page - 1) * self.per_page_count

    @property
    def end(self):
        """
        定义每页展示数据的结束下标
        :return:
        """
        return self.current_page * self.per_page_count

    @property
    def total_count(self):
        """
        定义总页数
        :return:
        """
        v, y = divmod(self.data_count, self.per_page_count)   # 内置函数divmod 能够整除返回True,不能整除返回False
        if y:
            v += 1
        return v

    def page_str(self, base_url):
        """
        定义分页规则
        :param base_url:
        :return:
        """
        page_list = []

        if self.total_count < self.pager_num:
            start_index = 1
            end_index = self.total_count + 1
        else:
            if self.current_page <= (self.pager_num + 1) / 2:
                start_index = 1
                end_index = self.pager_num + 1
            else:
                start_index = self.current_page - (self.pager_num - 1) / 2
                end_index = self.current_page + (self.pager_num + 1) / 2
                if (self.current_page + (self.pager_num - 1) / 2) > self.total_count:
                    end_index = self.total_count + 1
                    start_index = self.total_count - self.pager_num + 1

        if self.current_page == 1:
            prev = '<a class="page" href="javascript:void(0);">上一页</a>'
        else:
            prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,)
        page_list.append(prev)

        for i in range(int(start_index), int(end_index)):
            if i == self.current_page:
                temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i)
            else:
                temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i)
            page_list.append(temp)

        if self.current_page == self.total_count:
            nex = '<a class="page" href="javascript:void(0);">下一页</a>'
        else:
            nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,)
        page_list.append(nex)

        jump = """
        <input type='text'  /><a onclick='jumpTo(this, "%s?p=");'>GO</a>
        <script>
            function jumpTo(ths,base){
                var val = ths.previousSibling.value;
                location.href = base + val;
            }
        </script>
        """ % (base_url,)

        page_list.append(jump)
        # django从view向template传递HTML字符串的时候,django默认不渲染此HTML,原因是为了防止这段字符串里面有恶意攻击的代码(XSS)
        # mark_safe这个函数就是确认这段HTML字符串是安全的,Django会才会渲染此HTML
        page_str = mark_safe("".join(page_list))

        return page_str
pagination.py

4.3、app下的views.py:

def cookie(request):
    #
    # request.COOKIES
    # request.COOKIES['username111']
    request.COOKIES.get('username111')

    response = render(request,'index.html')
    response = redirect('/index/')
    # 设置cookie,关闭浏览器失效
    response.set_cookie('key',"value")
    # 设置cookie, N秒只有失效
    response.set_cookie('username111',"value",max_age=10)
    # 设置cookie, 截止时间失效
    import datetime
    current_date = datetime.datetime.utcnow()
    current_date = current_date + datetime.timedelta(seconds=5)
    response.set_cookie('username111',"value",expires=current_date)
    response.set_cookie('username111',"value",max_age=10)

    # request.COOKIES.get('...')
    # response.set_cookie(...)
    obj = HttpResponse('s')

    obj.set_signed_cookie('username',"kangbazi",salt="asdfasdf")
    request.get_signed_cookie('username',salt="asdfasdf")

    return response
views.py

5、cookie模拟登陆示例:

5.1、templates下模板:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/login/" method="POST">
        <input type="text" name="username" placeholder="用户名" />
        <input type="password" name="pwd" placeholder="密码" />
        <input type="submit" />
    </form>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>欢迎登录:{{ current_user }}</h1>
</body>
</html>
home

5.2、views.py:

user_info = {
    'dachengzi': {'pwd': "123123"},
    'kanbazi': {'pwd': "kkkkkkk"},
}
def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    if request.method == "POST":
        u = request.POST.get('username')
        p = request.POST.get('pwd')
        dic = user_info.get(u)
        if not dic:
            return render(request,'login.html')
        if dic['pwd'] == p:
            res = redirect('/home/')
            # res.set_cookie('username111',u,max_age=10)   #设置cookie N秒后失效 
            # import datetime
            # current_date = datetime.datetime.utcnow()
            # current_date = current_date + datetime.timedelta(seconds=5)
            # res.set_cookie('username111',u,expires=current_date)      #设置cookie 到截至时间后失效 
            res.set_cookie('username111',u)
            res.set_cookie('user_type',"asdfjalskdjf",httponly=True) # 只能http协议传输,当httponly=True时,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
            return res
        else:
            return render(request,'login.html')
views.py

6、CBV和FBV用户认证装饰器

6.1、FBV (Function Base VIew)在view(视图)中装饰器编写逻辑:

def auth(func):
    def inner(reqeust,*args,**kwargs):
        v = reqeust.COOKIES.get('username111')
        if not v:
            return redirect('/login/')
        return func(reqeust, *args,**kwargs)
    return inner

@auth
def index(reqeust):
    # 获取当前已经登录的用户
    v = reqeust.COOKIES.get('username111')
    return render(reqeust,'index.html',{'current_user': v})

6.2、CBV(Class Base Viev)在view(视图)中装饰器编写逻辑:

from django import views
from django.utils.decorators import method_decorator  #导入method_decorator函数,此函数时Django工具包中用于视图中类的装饰器函数

def auth(func):
    def inner(reqeust,*args,**kwargs):
        v = reqeust.COOKIES.get('username111')
        if not v:
            return redirect('/login/')
        return func(reqeust, *args,**kwargs)
    return inner


@method_decorator(auth,name='dispatch') #第三种装饰方法,在类上添加上装饰器
class Order(views.View):

    # @method_decorator(auth)  #第二种装饰方法,在dispatch函数上添加装饰器,因为dispatch函数会在类中所有函数执行前先行执行一遍
    # def dispatch(self, request, *args, **kwargs):
    #     return super(Order,self).dispatch(request, *args, **kwargs)

    # @method_decorator(auth)  #第一种装饰方法,在需要装饰的上添加装饰器
    def get(self,reqeust):
        v = reqeust.COOKIES.get('username111')
        return render(reqeust,'index.html',{'current_user': v})

    def post(self,reqeust):
        v = reqeust.COOKIES.get('username111')
        return render(reqeust,'index.html',{'current_user': v})

  

 

三、session

Session存储在服务器端,一般放置在服务器的内存中(为了高速存取),Sessinon在用户访问第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session,可调用request.getSession(true)强制生成Session。

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie
 1 Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
 2  
 3 a. 配置 settings.py
 4  
 5     SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
 6      
 7     SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
 8     SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
 9     SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
10     SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
11     SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
12     SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
14     SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
15  
16  
17  
18 b. 使用
19  
20     def index(request):
21         # 获取、设置、删除Session中数据
22         request.session['k1']
23         request.session.get('k1',None)
24         request.session['k1'] = 123
25         request.session.setdefault('k1',123) # 存在则不设置
26         del request.session['k1']
27  
28         # 所有 键、值、键值对
29         request.session.keys()
30         request.session.values()
31         request.session.items()
32         request.session.iterkeys()
33         request.session.itervalues()
34         request.session.iteritems()
35  
36  
37         # 用户session的随机字符串
38         request.session.session_key
39  
40         # 将所有Session失效日期小于当前日期的数据删除
41         request.session.clear_expired()
42  
43         # 检查 用户session的随机字符串 在数据库中是否
44         request.session.exists("session_key")
45  
46         # 删除当前用户的所有Session数据
47         request.session.delete("session_key")
48  
49         request.session.set_expiry(value)
50             * 如果value是个整数,session会在些秒数后失效。
51             * 如果value是个datatime或timedelta,session就会在这个时间后失效。
52             * 如果value是0,用户关闭浏览器session就会失效。
53             * 如果value是None,session会依赖全局session失效策略。
数据库Session
 1 a. 配置 settings.py
 2  
 3     SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
 4     SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
 5  
 6  
 7     SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
 8     SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
 9     SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
10     SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
11     SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
12     SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
14     SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存
15  
16  
17  
18 b. 使用
19  
20     同上
缓存Session
 1 a. 配置 settings.py
 2  
 3     SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
 4     SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
 5  
 6  
 7     SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
 8     SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
 9     SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
10     SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
11     SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
12     SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
14     SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存
15  
16 b. 使用
17  
18     同上
文件Session
1 数据库用于做持久化,缓存用于提高效率
2  
3 a. 配置 settings.py
4  
5     SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
6  
7 b. 使用
8  
9     同上
缓存+数据库Session
1 a. 配置 settings.py
2      
3     SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
4  
5 b. 使用
6  
7     同上
加密cookie Session

 更多session参考:参考一参考二

1、session免密登录+10秒超时示例:

1.1、templates 下的模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/login/" method="POST">
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登录
        <input type="submit" value="提交" />
        <input id="btn1" type="button" value="按钮" />
        <input id="btn2" type="button" value="按钮" />
    </form>

    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>欢迎登录:{{ username }}, {{ request.session.username }}</h1>
    <a href="/logout/">注销</a>
</body>
</html>
index.html

 1.2、views.py:

from django.shortcuts import render,redirect,HttpResponse

def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    elif request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        if user == 'root' and pwd == "123":
            # session中设置值  Django自动产生4步操作(1、生成随机字符串 2、写到用户浏览器cookie 3、保存在session中 4、在随机字符串对应字典中设置相应值)
            request.session['username'] = user
            request.session['is_login'] = True
            if request.POST.get('rmb', None) == '1':
                # 超时时间
                request.session.set_expiry(10)
            return redirect('/index/')
        else:
            return render(request,'login.html')

def index(request):
    # session中获取值
    if request.session.get('is_login', None):
        # {'username': request.session['username']}这个字典数据其实可以不用传递,因为request本身就包含了session值,
        # 在HTML中可以直接通过request.session.username获取用户名
        return render(request, 'index.html', {'username': request.session['username']})
    else:
        return HttpResponse('gun')

# 注销
def logout(request):
    # del request.session['username']  # 删除一个指定session用户
    request.session.clear()            # 清楚全部session用户
    return redirect('/login/')
views.py

 注、在运行Django工程示例前需要运行如下命令,生成表结构,session会默认保存在db.sqlite3数据库文件中。

python3 manage.py  makemigrations #相当于在该app的migrations目录,记录下该app下modes.py所有表结构类型的改动(普通增删改查不记录)
python3 manage.py  migrate        #将刚刚对于表结构的改动作用至数据库

 

 

四、CSRF

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

之前的Django博文,都是基于在settings.py文件中,注释掉CSRF中间件进行的。

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',
]

注:现在开始之后的博文都是去掉CSRF中间件注释进行演示。

django中设置防跨站请求伪造功能有分为全局和局部: 

全局:
  中间件 django.middleware.csrf.CsrfViewMiddleware

局部:
  from django.views.decorators.csrf import csrf_exempt,csrf_protect
  @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
  @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

1、生成csrf_token:

在Django中可以通过{% csrf_token %} 生成csrftoken来防范CSRF攻击,但是要在后续的request请求中需要带上csrftoken参数才能通过CSRF中间件防御机制,进入view视图中进行处理。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/login/" method="POST">
        <!--在Django中可通过%配合csrf_token字段可生成csrf_token字符串,会在form表单中生成一个隐藏的input框如下:
        <input type="hidden" name="csrfmiddlewaretoken" value="6CAO0tE8oqIsehJFR9VojmS307XRfK91GkxRHutKvwj5nSQgI6KDZZL4hLkAorNo">
        还会在cookie中生成{csrftoken:gQgbSGTiXtnqTeKD0jRbq6ZGsrdiWiJZQydezHIU4zY32PReRgGq6JSHJ5A15Znm}-->
        {% csrf_token %}
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登录
        <input type="submit" value="提交" />
    </form>
    </script>
</body>
</html>
生成csrftoken

 2、csrf_token 提交给后台的三种方式:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/login/" method="POST">
 9         <!--在Django中可通过%配合csrf_token字段可生成csrf_token字符串,会在form表单中生成一个隐藏的input框如下:
10         <input type="hidden" name="csrfmiddlewaretoken" value="6CAO0tE8oqIsehJFR9VojmS307XRfK91GkxRHutKvwj5nSQgI6KDZZL4hLkAorNo">
11         还会在cookie中生成{csrftoken:gQgbSGTiXtnqTeKD0jRbq6ZGsrdiWiJZQydezHIU4zY32PReRgGq6JSHJ5A15Znm}-->
12         {% csrf_token %}
13         <input type="text" name="user" />
14         <input type="text" name="pwd" />
15         <input type="checkbox" name="rmb" value="1" /> 10秒免登录
16         <input type="submit" value="提交" />                                <!-- 第一种提交csrf_token的方式 通过from表单的形式-->
17         <input id="btn1" type="button" value="按钮" />
18         <input id="btn2" type="button" value="按钮" />
19     </form>
20 
21     <script src="/static/jquery-1.12.4.js"></script>
22     <script src="/static/jquery.cookie.js"></script>
23     <script>
24       //  第二种提交csrf_token的方式   在每个ajax请求之前,将cookie中的csrftoken值添加至请求头中,ajaxSetup类似于unittest框架中的setup,在每个case运行之前运行一遍
25         $(function(){
26             $.ajaxSetup({
27                 beforeSend: function(xhr,settings){   // xhr 是XMLHttpRequest()对象,settings是Django的配置文件,settings.py每次运行都会将settings加载在内存之中
28 
29 
30                     xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
31                 }
32             });
33 
34             // <!-- 第三种提交csrf_token的方式   通过ajax形式将cookie中的csrftoken值添加至请求头中-->
35             // <!-- 但是第三种提交方式不好的地方就是需要不断的添加ajax请求,有一个提交按钮,就要新增一个ajax请求-->
36             $('#btn1').click(function () {
37                 $.ajax({
38                     url: '/login/',
39                     type:"GET",
40                     data: {'user': 'root', 'pwd': '123'},
41                     // headers: {'X-CSRFtoken': $.cookie('csrftoken')},  //X-CSRFtoken 这key是固定的不能改变
42                     success:function(arg){
43 
44                     }
45                 })
46             });
47         })
48     </script>
49 </body>
50 </html>
三种方式

 注:解释 ajax 将csrftoken值添加至请求头中,为什么对应的key是 X-CSRFtoken:

from django.conf import settings
print(settings.CSRF_HEADER_NAME)

#输出打印结果  HTTP_X_CSRFTOKEN
View Code

通过打印结果发现值是 HTTP_X_CSRFTOKEN,这个就是csrftoken请求头对应的key,HTTP是因为Django会默认将收到的每个请求都会加上HTTP的缘故。因此对应的真实key是X_CSRFTOKEN,但是如果真的就把请求头中的key写成X_CSRFTOKEN的话,后台是接收不到的,因为无法识别下划线_,因此需要将X_CSRFTOKEN写成X-CSRFTOKEN。(X-CSRFTOKEN这个作为key是可以请求的,但是为了命名规范会写成X-CSRFtoken)

3、根据请求方式决定是否提交csrftoken值

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% csrf_token %}
  
    <input type="button" onclick="Do();"  value="Do it"/>
  
    <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
    <script src="/static/plugin/jquery/jquery.cookie.js"></script>
    <script type="text/javascript">
        var csrftoken = $.cookie('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);
                }
            }
        });
        function Do(){
  
            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:'POST',
                success:function(data){
                    console.log(data);
                }
            });
  
        }
    </script>
</body>
</html>
View Code

更多CSRF参考:点击

posted @ 2018-04-10 22:17  Wilson_Blogs  阅读(309)  评论(0编辑  收藏  举报