如何让浏览器知道用户的登录状态
htpp协议是无状态的,服务器不知道访问者是谁
cookie弥补了http无状态的不足,让服务器直到来的人是谁
当用户登录成功时,服务器应该将用户的登录状态存储
cookie和session(记录用户的登录状态) --- cookie在浏览器中,session在服务器中
在session记录登录用户的基本信息
在服务器中产生cookie,记录一串随机数然后存储在本地,当浏览器再次访问服务器时,浏览器会带上cookie,服务器使用session中记录同样的一串随机数存储在服务器,
当用户登录成功时,服务器判断浏览器中的cookie中的随机数在服务器的session中是否存在,如果存在,获得session中用户的基本信息。
cookie安全性较低,容易被窃取,session安全性较高,所以cookie中一般只存储一串随机数,用户的信息存储在session中
cookie和session的操作
# 跳转/app/index页面,并且在返回index页面时绑定cookie
res = HttpResponseRedirect(reverse('app:index'))
设置cook值 --- set_cookie(key, value, max_age, expires) - key值,value值,秒,分 -- 后两个参数是存活时间
res.set_cookie('session_id','sdfsdffsdf123',max_age=10)
注意:return后应该是res,不能单独写
设置cookie中的值,并且设置session中的值
数据的账户和密码存储在session中,加密保存
登录的时候,进行验证用户的用户名和密码是否正确,如果正确才向cookie中存入信息
request.session['login'] = True
request.session['username'] = '张三'
request.session['password'] = '123456'
生成一个键是sessionid的键,值是随机字符串的cookie
生成一个session_key -- 键,session_date -- 用户信息,expire_date -- 存活时间的数据表,通过request.session可以获得数据对象
删除cookie中的key值
1.delete_cookie(key)
res.delete_cookie('session_id')
2、将存活时间设置为0
res.set_cookie(key, value, max_age=0)
获取cookie值和session值
获取网页的cookie值其实就是获取数据库中session_key的值
注意:cookie是存储在第一次登陆时使用的浏览器上,使用其他浏览器以后cookie就不在存在
session_key = request.session.session_key --- 获取session_key
username = request.session.get('username') --- 获取设置的session的信息
password = request.session.get('password')
删除sessiond的整条记录 --- 将数据表(django_session)的数据删除
request.session.delete(session_key) --- session_key是唯一的
request.session.delete() --- 本质上request.session是获取了session对象,所以可以通过delete()进行删除
del request.session['username'] --- 删除session中的username
注意:删除用户的信息并不会影响到数据表的存在
存活时间
cookie的过程:
当用户访问浏览器时,服务器生成cookie,并存储在本地,当用户再次访问浏览器的时候,就能从浏览器中查看到在服务器中设置的cookie信息 -- 需要注意的是:如果设置了cookie存活时间,当用户第一次访问浏览器时,计时已经开始,不是第二次访问能看到cookie信息才开始计时。
在pycharm中新建项目
进入终端,cd回到存放项目的文件夹中 --- pycharm中的环境和window中一样(可以使用dir查看文件夹内容)
使用djagno-admin startproject 项目名 --- 创建项目
重新选择虚拟环境
设置run --- renserver ip:端口
新项目的配置
1、在站点的__init__中导入pymysql
import pymysql
pymysql.install_as_MySQLdb()
2、在settings中配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day1',
'USER': 'root',
'PASSWORD': '123456',
'HOST': 'localhost',
'PORT': '3306'
}
}
3、在将应用名添加到站点settings.py的INSTALLED_APPS中
4、根据需求创建应用 --- 在每个应用下新建一个urls.py文件,并在站点的urls中使用include将站点urls和应用的urls进行连接,并进行别名,然后配置新建的urls
5、创建templates文件夹 --- 并在站点中的settings.py中设置路径,存模板,也就是html
6、创建static文件夹并设置路径 --- 存放静态文件
urls路由别名
站点中的urls别名使用namespace='名字'
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'app',include('app.urls', namespace='app'))
]
在应用的urls中别名使用name='名字'
urlpatterns = [
url(r'index/',views.index, name='index'),
url(r'setCookie', views.setCookie),
]
然后在views中跳转页面时:可以使用不适用路径跳转,使用reverse方法
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect
# HttpResponseRedirect和redirect一样的
def setCookie(request):
if request.method == 'GET':
return HttpResponseRedirect(reverse('app:index'))
csrf攻击和防范
csrf攻击说明:
1.用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3.用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
csrf的攻击之所以会成功是因为服务器端身份验证机制可以通过Cookie保证一个请求是来自于某个用户的浏览器,但无法保证该请求是用户允许的。因此,预防csrf攻击简单可行的方法就是在客户端网页上添加随机数,在服务器端进行随机数验证,以确保该请求是用户允许的。Django也是通过这个方法来防御csrf攻击的。
django中防御csrf攻击:
原理
在用户访问django的可信站点时,django反馈给用户的表单中有一个隐含字段csrftoken,这个值是在服务器端随机生成的,每一次提交表单都会生成不同的值。当用户提交django的表单时,服务器校验这个表单的csrftoken是否和自己保存的一致,来判断用户的合法性。当用户被csrf攻击从其他站点发送精心编制的攻击请求时,由于其他站点不可能知道隐藏的csrftoken字段的信息这样在服务器端就会校验失败,攻击被成功防御,这样就能避免被 CSRF 攻击。
在客户端页面上添加csrftoken,服务器端进行验证,服务器端验证的工作通过'django.middleware.csrf.CsrfViewMiddleware'这个中间层来完成。在django当中防御csrf攻击的方式有两种, 1.在表单当中附加csrftoken 2.通过request请求中添加X-CSRFToken请求头。注意:Django默认对所有的POST请求都进行csrftoken验证,若验证失败则403错误侍候。
csrf防范:
1、首先,最基本的原则是:GET 请求不要用有副作用。也就是说任何处理 GET 2、请求的代码对资源的访问都一定要是“只读“的。
3、要启用 django.middleware.csrf.CsrfViewMiddleware 这个中间件。
4、再次,在所有的 POST 表单元素时,需要加上一个 {% csrf_token %} tag。
5、在渲染模块时,使用 render。render会处理 csrf_token 这个 tag,从而自动为表单添加一个名为 csrfmiddlewaretoken 的 input。
通过form提交
在form表单里面需要添加{%csrf_token%}
这样当你查看页面源码的时候,可以看到form中有一个input是隐藏的
<form action="" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='0232bM2TQIRhlznMxFcyl8oPIe8drKJSMYsF7A7trU5605NgHqdqF8lMYILzF2DH'
总结原理:当用户访问login页面的时候,会生成一个csrf的随机字符串,,并且cookie中也存放了这个随机字符串,当用户再次提交数据的时候会带着这个随机字符串提交,如果没有这个随机字符串则无法提交成功
用户访问页面,服务器创建随机数,并保存然后返回给页面
当用户再次从浏览器发送请求的时候,就会带着随机数到后端,后端进行验证页面传过来的随机数是否和之前保存的随机数一致,如果一致,再进行判断浏览器中的cookie中的sessionid在服务端中是否能找到,如果找到了服务端在能肯定是本人的行为。
随机数只能使用一次,每次去访问页面值都不一样
用户登录、注册和注销
通过python manage.py createsuperuser创建新用户
通过request.POST.get('表单的name属性') --- 通过表单post提交的的name属性获得对应的values值
<input type="text" name="username" placeholder="账号" class="login_txtbx"/>
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 验证用户名和密码是否正确
# 和数据库的auth_user表对应的模型是User
# authenticate 鉴定
# 如果鉴定成功呢,返回用户实例,不成功返回空
user = auth.authenticate(request, username=username, password=password)
if user:
# 登录
auth.login(request, user)
# 登录成功,返回首页
return HttpResponseRedirect(reverse('backweb:index'))
else:
return HttpResponseRedirect(reverse('backweb:login'))
使用django的auth去验证用户名和密码是否正确
auth.authenticate() --- 鉴定用户名和密码,如果鉴定成功返回用户(User的一个对象),不成功返回空
auth.login() --- 登录
auth.logout() --- 退出
退出:
def logout(request):
if request.method == 'GET':
# 退出
auth.logout(request)
return HttpResponseRedirect(reverse('backweb:login'))
<!-- 在页面中获得点击退出后的退出页面的地址 -->
<li><a href="{% url 'backweb:logout' %}" class="quit_icon">安全退出</a></li>
或者
<li><a href="/backweb/logout" class="quit_icon">安全退出</a></li>
当用户退出登录时,我们还能直接登录index界面,这是不合理的,需要进行设置,让用户在未登录状态时,不能直接访问index页面,在这里可以使用from django.contrib.auth.decorators import login_required方法
用login_requied(英文意思:登录需要)将需要是登录状态才能访问的页面包装起来
url(r'index/',login_required(views.index), name='index'),
当用户再次访问时,就会跳转到一个位未知的网站,如果想要指定这个网站,可以在settings.py中进行设置,具体设置如下:
# 没有登录,跳转此地址
LOGIN_URL = '/backweb/login/'
自定义用户登录注册退出
应用的urls中:
from django.conf.urls import url
from backweb import views
urlpatterns = [
url(r'^my_login/',views.my_login,name='my_login'),
url(r'^my_register/',views.my_register,name='register'),
url(r'^my_logout/',views.my_logout,name='my_logout'),
]
对应的views中:
注册 --------
思路:首先获取到用户输入的用户名和密码,验证其用户名是否被注册,如果被注册,重新返回用户注册页面,如果用户名通过,判断其两次输入的密码是否一致,如果一致就创建用户数据,否则重新跳转注册页面
实例:
def my_register(request):
if request.method == 'GET':
return render(request,'backweb/register.html')
if request.method == 'POST':
username = request.POST.get('username')
password1 = request.POST.get('password1')
password2 = request.POST.get('password2')
# 先验证用户是否注册过
user = User.objects.filter(username=username).exists()
if user:
error = '用户名已注册'
return render(request, 'backweb/register.html',{'error' : error})
else:
if password1 == password2:
User.objects.create(username=username,password=password1)
return HttpResponseRedirect(reverse('backweb:my_login'))
else:
error = '两次密码不一致'
return render(request,'backweb/register.html', {'error': error})
登录----------
思路:
获取到用户输入的账户名和密码,判断用户是否存在,如果存在就判断用户对应的密码是否正确,否则跳转到登录页面,输入正确则要保存用户的状态。
保存状态的思路:在cookie中和在服务器中都要设值。可以通过随机函数生成随机字符,时间函数生成cookie存活时间。
例子:
import random
def my_login(request):
if request.method == 'GET':
return render(request,'backweb/login.html')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = User.objects.filter(username=username).first()
if user:
if password == user.password:
# 第一步,在cookie中设值
res = HttpResponseRedirect(reverse('backweb:index'))
s = 'sdfdsjgfdsifusjdifjsdlkfjlkdsfjdlksjd123123874'
session_id = ''
for i in range(20):
session_id += random.choice(s)
out_time = datetime.now() + timedelta(days=1)
res.set_cookie('session_id', session_id, expires=out_time)
"""
在服务器中存值 --- 存过期时间和sessoin_id值,
如果时间过期就将数据库中的session_id值置空,判断用户登录时浏览器的session_id值和数据库的session_id值是否匹配
"""
user.session_id = session_id
user.out_time = out_time
user.save()
return res
else:
error = '密码错误'
return render(request,'backweb/login.html', {'error':error})
else:
error = '用户名不存在'
return render(request,'backweb/login.html', {'error': error})
退出-----
用户退出的实质也就是删除服务器的ticket值并且删除cookie中的ticket值
def my_logout(request):
if request.method == 'GET':
pass
# 删除服务器端的sessoin_id值
user = request.user
user.session_id = ''
user.save()
# 删除cookie中的session_id
res = HttpResponseRedirect(reverse('backweb:my_login'))
res.delete_cookie('session_id')
return res
HttpResponse、render,、redirect
1.HttpResponse
它是作用是内部传入一个字符串参数,然后发给浏览器。
2、render
render方法可接收三个参数,一是request参数,二是待渲染的html模板文件,三是保存具体数据的字典参数。
它的作用就是将数据填充进模板文件,最后把模板中渲染的html页面返回给浏览器。
3、redirect
接受一个URL参数,表示让浏览器跳转去指定的URL.
if user:
# 登录
auth.login(request, user)
return HttpResponseRedirect(reverse('backweb:index'))
# 或者 return HttpResponseRedirect('/backweb/index/')
# 将渲染好的模板返回给浏览器
def index(request):
if request.method == 'GET':
return render(request, 'backweb/index.html')
#渲染变量到模板中,最后将结果返回给浏览器
def selStuen(request):
if request.method == 'GET':
stus = Student.objects.all()
return render(request, 'students.html',
{'stus': stus})