cookie与session组件
目录
cookie与session组件
一 会话跟踪技术
1 什么是会话跟踪技术
1) 在Web中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。 
2) 在一个会话的多个请求中共享数据,这就是会话跟踪技术。
3) 例如在一个会话中的请求如下:
	-请求银行主页;
    -请求登录(请求参数是用户名和密码);
	-请求转账(请求参数与转账相关的数据); 
	-请求信用卡还款(请求参数与还款相关的数据)。
    在这上会话中当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是相对张三的转账和还款!这就说明我们必须在一个会话过程中有共享数据的能力。
2 会话路径技术使用Cookie或session完成
	-我们知道HTTP协议是无状态协议,即每个请求都是独立的!无法记录前一次请求的状态。但HTTP协议中可以使用Cookie来完成会话跟踪!在Web开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
3 cookie,session,token
1) cookie: 	客户端浏览器上的键值对
2) session: 存在服务端的键值对
3) token: 	加密的键值对,如果放在客户端浏览器上,它就叫cookie,  【可以理解为服务端签发的加密字符串】

二 cookie
1 django中cookie的使用
# 0) 会话跟踪,会话保持
# 1) cookie规范
	-记住:当前网站在浏览器上cookie个数和大小有限制
 	-Cookie大小上限为4KB; 
 	-一个服务器最多在客户端浏览器上保存20个Cookie; 
 	-一个浏览器最多保存300个Cookie;
# 2) django中操作cookie
	-增:obj.set_cookie('key','value')
    -删: obj.delete_cookie('key')  # 设置过期
    -查: request.COOKIES.get('key')
    -改: obj.set_cookie('key','value1')
# 3) 带签名的cookie(加盐,加密)
	-增:obj.set_signed_cookie('name','lqz','123')
    -删: obj.delete_cookie('name')  # 设置过期
    -查: request.get_signed_cookie('name',salt='123')
    -改: obj.set_signed_cookie('name','lqz','123')
2 cookie版登陆校验
2.1 路由
\urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls', namespace='app01')),
]
app01\urls.py
from django.urls import path
from app01 import views
app_name = 'app01'  # 当主路由使用了名称空间的时候,需要设置app_name
urlpatterns = [
    path('login/', views.login, name='app01_login'),
    path('index/', views.index, name='app01_index'),
    path('order/', views.order, name='app01_order'),
    path('logout/', views.logout, name='app01_logout'),
    path('userinfo/', views.userinfo, name='app01_userinfo'),
]
2.2 视图函数
from django.shortcuts import render, HttpResponse, redirect, reverse
from app01 import models
## 登录认证装饰器
def login_auth(func):
    def inner(request, *args, **kwargs):
        # 登录校验
        name = request.COOKIES.get('name')
        if name:
            res = func(request, *args, **kwargs)
            return res
        else:
            path = request.get_full_path()
            return redirect('/app01/login/?returnUrl=%s' % (path))
    return inner
## cookie 版登录
def login(request):
    url_index = reverse('app01:app01_index')
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        userinfo = models.Uesr.objects.filter(name=name).values('name', 'password').first()
        if userinfo:
            if userinfo.get('name') == name and userinfo.get('password') == password:
                # 写入cookie, 登录成功,重定向,【有之前页面到登陆前页面,没有则去首页】
                path = request.GET.get('returnUrl')
                if path:
                    obj = redirect(path)
                else:
                    obj = redirect(url_index)
                obj.set_cookie('name', name)
                return obj
            else:
                return HttpResponse('用户名或密码错误')
        else:
            return HttpResponse('用户名或密码错误')
# def order(request):
#     url_login = reverse('app01:app01_login')
#     name = request.COOKIES.get('name')
#     if name:
#         return render(request, 'order.html')
#     else:
#         return redirect(url_login)
## 装饰器版本(只要加了装饰器,一旦进入这个视图函数,就表明登录成了)
@login_auth
def order(request):
    return render(request, 'order.html')
def logout(request):
    obj = HttpResponse('退出登录成功')
    obj.delete_cookie('name')
    return obj
@login_auth
def userinfo(request):
    return render(request, 'userinfo.html')
@login_auth
def index(request):
    url_login = reverse('app01:app01_login')
    print(url_login)
    return render(request, 'index.html')
2.3 模板
login.html
<form action="" method="post">
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit" value="提交"></p>
</form>
order.html
<button><a href="{% url 'app01:app01_logout' %}">退出登录</a></button>
index.html
<h1>index页面</h1>
userinfo.html
<h1>用户信息userinfo</h1>
三 session
1 session由来
	Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
	问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
	我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“"账号密码"”等等。
	# 总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
	另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
2 django中session的使用
1) 存在于服务端的键值对
2) 同一个浏览器不允许登录多个账户【因为一个浏览器只有一个session_id】,不同浏览器可以登录同一个账户
3) session的使用(必须迁移数据)
	-增:request.session['name']=lqz
    -查:request.session['name']
    -改:request.session['name']=egon
    -删:del request.session['name']
    -设置过期时间:request.session.set_expiry(10)
    
4) session的其它使用
	-request.session.setdefault('k1',123)  # 有的话不改,没有新增
    -request.session.get('name',None)  # 取值,不会报错
    
    -request.session.keys()
	-request.session.values()
    -request.session.items()
    -request.session.session_key # 获取那个随机字符串,django_session表中session_key字段
	
    -request.session.clear_expired() # 清除过期的session
    -request.session.exists("session_key") # 判断这个随机字符串(session_key字段),有没有数据
    -request.session.delete() # 删除所有的值,django_session表中删除当前登录者的这条记录
    -request.session.flush()  # 把cookie和数据库都删除清空
#####  session的使用
# django框架默认,把session信息存到数据库中了,django_session表
# 先迁移数据库【比如默认的sqlite数据库,如果事先迁移了表,将django_session表建出来,就不会报错】
def session_set(request):
    # request.session是个字典
    # 设置session,干了如下几件事
    '''
        # 一个浏览器,一个随机字符串
        1 生成一个随机字符串asdfasdf,把随机字符串和name=lxx数据加密存到django_session表中,
        2 会把这个随机字符串放到cookie中
            obj.set_cookie('sessionid',asdfasdf)
    '''
    request.session['name'] = 'lxx'
    request.session['age'] = '188'
    return HttpResponse('session_set 成功')
# 【每次更新数据,session_data会改变,只要不删除session_id, session_id在有效期内就不会改变】
def session_set2(request):
    request.session['name'] = 'alxx'
    request.session['xx'] = '777'
    return HttpResponse('session_set 成功2')
def session_get(request):
    print(request.session.get('name'))  # lxx
    print(request.session.keys())  # dict_keys(['name', 'xx', 'age'])
    print(request.session.values())  # dict_values(['lxx', 'xxxxxxx', '188'])
    print(request.session.items())  # dict_items([('name', 'lxx'), ('xx', 'xxxxxxx'), ('age', '188')])
    print(request.session.session_key)  # j6wgdaaipgxdy3i15wlzwd77trrkybuu
    return HttpResponse('得到了 session_key')
3 session原理解析


# 1) 第一次请求:一个浏览器,一个随机字符串
    -1 浏览器的首次请求发过来,-->> request.session['name'] = 'lxx'
    -2 在请求返回客户端的时候,【在中间件环节】,生成一个随机字符串asdfasdf【即浏览器中的session_id】,把随机字符串【session_id字段的值】和name=lxx数据加密【session_data的值】存到django_session表中
    -3 到了浏览器端,会把这个随机字符串放到cookie中-->>  obj.set_cookie('sessionid',asdfasdf)
# 2) 后续请求【查数据】的时候:
    -1 浏览器请求带着随机字符串【session_id】,去服务端请求数据 request.session.get('name')
    -2 浏览器请求在【session中间件】环节,中间件根据随机字符串【session_id】去数据库查,查到数据之后【身份认证成功,免登录】,把数据解密,放到请求request.session.get('name')中,于是乎,我们就拿到了数据。 
# 3) 后续请求【写数据】的时候:
    -1 浏览器请求发到服务端,request.session['name'] = 'alxx'
    -2 请求返回的时候,在【session中间件】环节,由于数据库中有session_id,所以只是把数据部分"**替换和加密**",放到数据库中,session_id不变,以后再请求访问的时候,还是正常的查数据操作。
4 django的session原理流程[全]

5 session版登录认证
5.1 路由
\urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls', namespace='app01')),
    path('app02/', include('app02.urls', namespace='app02')),
]
app01\urls.py
from django.urls import path
from app01 import views
app_name = 'app02'  # 当主路由使用了名称空间的时候,需要设置app_name
urlpatterns = [
    path('login/', views.login, name='app02_login'),
    path('index/', views.index, name='app02_index'),
    path('order/', views.order, name='app02_order'),
    path('logout/', views.logout, name='app02_logout'),
    path('userinfo/', views.userinfo, name='app02_userinfo'),
]
5.2 视图函数
from django.shortcuts import render, HttpResponse, redirect, reverse
# Create your views here.
from app01 import models
# 登录认证装饰器
def login_auth(func):
    # url_login = reverse('app02:app02_login')
    def innder(request, *args, **kwargs):
        # 登录校验
        name = request.session.get('name')
        if name:
            res = func(request, *args, **kwargs)
            return res
        else:
            path = request.get_full_path()
            # ss = '%s?returnUrl=%s' % (url_login, path)
            # return redirect(ss)
            return redirect('/app02/login/?returnUrl=%s' % path)
    return innder
def login(request):
    url_index = reverse('app02:app02_index')
    # print(type(url_index), "url_index = reverse('app02:app02_index')")
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        print(name, password)
        userinfo = models.User.objects.filter(name=name).values('name', 'password').first()
        if userinfo:
            if userinfo.get('name') == name and userinfo.get('password') == password:
                path = request.GET.get('returnUrl')
                if path:
                    obj = redirect(path)
                else:
                    obj = redirect(url_index)
                request.session['name'] = name
                return obj
            else:
                return HttpResponse('y用户名或密码错误')
        else:
            return HttpResponse('y用户名或密码错误')
@login_auth
def logout(request):
    del request.session['name']
    return HttpResponse('退出登录成功')
@login_auth
def order(request):
    return render(request, 'order.html')
@login_auth
def userinfo(request):
    return render(request, 'userinfo.html')
def index(request):
    return render(request, 'index.html')
5.3 模板
login.html
<form action="" method="post">
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="password"></p>
    <p><input type="submit" value="提交"></p>
</form>
order.html
<button><a href="{% url 'app01:app01_logout' %}">退出登录</a></button>
index.html
<h1>index页面</h1>
userinfo.html
<h1>用户信息userinfo</h1>
6 django中session的配置
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False)
# key
# value
# max_age:传个数字,以秒计,过期时间,有默认值 (6天后过期:60*60*24*5)
---了解
# expires:传时间对象,date=datetime.timedelta()
# path:默认 /【访问根路径时显示】 表示当前域下的所有路径  http://127.0.0.1:8000/lxx/dfd/————>> 路径就是/lxx/dfd/
# domain:在哪个*域*下有效
# secure:是否Https传输cookie
# httponly:cookie只支持http传输
# # session支持存放在多个位置,默认存放于数据库中
1). 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
2). 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
3). 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 
4). 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
5). 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
其他公用设置项:
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)***记住
---了解
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
四 cbv加装饰器
urlpatterns = [
    ## cbv加装饰器
    path('userinfo2/', views.Userinfo.as_view())
]
from django.views import View
from django.utils.decorators import method_decorator
# 使用登录认证装饰器
# 用法一
# @method_decorator(login_auth,name='get')
# @method_decorator(login_auth,name='post')
class UserInfo(View):
    # 用法二
    @method_decorator(login_auth)
    def get(self, request, *args, **kwargs):
        return HttpResponse('userinfo get')
    @method_decorator(login_auth)
    def post(selfself, request, *args, **kwargs):
        return render(request, 'userinfo.html')
        
# 总结:两种用法
	-加在类上:@method_decorator(login_auth,name='get')
    -加在方法上:@method_decorator(login_auth)
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号