登陆的主要参数是:手机号/用户名,密码和记住我的选项
class LoginForm(forms.Form):
# 手机号 和用户名 密码
user_account = forms.CharField()
password = forms.CharField(label='密码', max_length=20, min_length=6,
error_messages={"min_length": "密码长度要大于6",
"max_length": "密码长度要小于20",
"required": "密码不能为空"}
)
# 勾选的字段
remember_me = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(LoginForm, self).__init__(*args, **kwargs)
def clean_user_account(self):
user_info = self.cleaned_data.get('user_account')
if not user_info:
raise forms.ValidationError("用户账号不能为空")
if not re.match(r'^1[3-9]\d{9}$', user_info) and (len(user_info) < 5 or len(user_info) > 20):
raise forms.ValidationError('格式不正确,请重新输入')
return user_info
def clean(self):
cleaned_data = super().clean()
user_info = cleaned_data.get('user_account')
passwd = cleaned_data.get('password')
hold_login = cleaned_data.get('remember_me')
# 2 查询数据
user_queryset = Users.objects.filter(Q(mobile=user_info) | Q(username=user_info)) # 满足一个都可以
if user_queryset:
user = user_queryset.first() # 获取第一个
if user.check_password(passwd):
if hold_login: # 如果说勾选有值
self.request.session.set_expiry(constants.USER_SESSION_EXPIRES)#只是设置过期时间
else:
self.request.session.set_expiry(None)#浏览器关闭就退出
login(self.request, user)#真正的保存到数据库中
else:
raise forms.ValidationError('密码错误,请重新输入')
else:
raise forms.ValidationError("账号不存在,请重新输入")
主要流程是:
- 用户输入登陆需要的参数
- 后端通过post请求接收,进行数据校验
- 校验通过,查询数据库的账号密码,同时检查记住我选项,若勾选了就保存session的值
- 调用login()函数保存登陆状态
延伸的问题:
1、如何保证密码的保密性?https://blog.csdn.net/qq_42039417/article/details/103309195 参考文章
首先是储存的安全,作为应用的服务端,我们的后台数据库中存储着所有用户的口令数据,怎样实现安全的存储方案就是我们所需要考虑的。
上面的代码是直接明文储存在服务区的数据库中。
可以看出,这里存储的 password 是直接从表单中获取的字符序列值,中间并没有任何加密处理的过程显然这很不安全,容易被社会工程渗透,而且一旦攻击者入侵了数据库,这些口令就都白给了,所以我们需要先对口令进行加密处理,再存储到数据库中。
摘要处理
- 一个常用的处理方法就是,使用散列算法对口令进行摘要,存储得到的散列值,这样的话,即使攻击者爆破了数据库,能获取到的也只是口令的散列值,因为散列是一个单向的过程,所以不可能逆推得到口令,当要验证口令时,只需要用相同的散列算法对用户输入的口令进行摘要,把计算得到的散列值和数据库中存储的散列值进行对比,如果一致则认证通过。
- 但这样也还不够安全,毕竟散列值的长度是固定的,说明散列值的范围是有限的。
这样必然会出现碰撞情况,即多个不同字符序列的散列值却是一样的
即便从散列值无法逆推得到完全正确的原口令,只需要找到一个跟原口令散列碰撞的字符序列,同样可以使用这个字符序列通过认证
而且,有限代表可以遍历。如果彩虹表足够大,通过查询彩虹表,不难找到一个与散列值对应的序列
所以在对口令进行摘要的过程,我们还需要加盐(salt)
撒盐
- 原序列每一位比特的变化对散列结果的影响都是千差地别的,对撒了盐的口令取摘要,即便攻击者获取了撒盐口令的散列值,他不仅需要猜测口令,还需要猜测盐值,口令破解的难度呈指数剧增
这里可以直接调用 Django 内置哈希模块 django.contrib.auth.hashers 的口令生成函数 make_password
下面是经过改进的注册视图函数
views.py
from django.contrib.auth.hashers import make_password
from django.shortcuts import render
from Blog.models import User
def register(request):
if request.session.get('is_login', None):
request.session.flush()
if request.method == 'POST':
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
re_password = request.POST.get('re_password', '').strip()
if password == re_password:
try:
User.objects.get(username=username)
except User.DoesNotExist:
encrypted_password = make_password(password, None, 'pbkdf2_sha256')
new_user = User(username=username, password=encrypted_password)
new_user.save()
return render(request, 'register.html', {'mess': '用户注册成功'})
else:
return render(request, 'register.html', {'mess': '该用户已被注册'})
else:
return render(request, 'register.html', {'mess': '两次密码输入不一致'})
return render(request, 'register.html')
make_password(password, salt=None, hasher=‘default’)
其中,第二个参数指定盐值,如果为 None 则随机生成盐值(建议随机)
这是一个设置盐值为 ‘set’ 的口令在数据库中存储的值,其使用的散列算法是 pbkdf2_sha256,在登录验证时,使用和 make_password 配套的 check_password 函数进行口令验证
views.py
from django.contrib.auth.hashers import check_password
from django.shortcuts import render, redirect
from Blog.models import User
def login(request):
if request.session.get('is_login', None): # 检查是否已登录
return redirect('/index/')
if request.method == 'POST': # 如果有提交表单
username = request.POST.get('username', '').strip()
password = request.POST.get('password', '').strip()
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return render(request, 'login.html', {'mess': '用户不存在'})
else:
if check_password(password, user.password):
request.session['is_login'] = 'T' # 标记已登录
request.session['user_id'] = user.id
request.session['username'] = username
return redirect('/index/')
else:
return render(request, 'login.html', {'mess': '密码输入错误'})
return render(request, 'login.html')
————————————————
原文链接:https://blog.csdn.net/qq_42039417/article/details/103309195
2、session和login的具体实现和作用是什么?session的信息回持久化到数据库中,并且设置了过期时间,在过期时间内,Django内置的session会保存用户的信息,最直接的方法就是用户退出浏览器,再次登陆的时候右上角的用户名还是回存在的。
login 的作用,登录的过程(django),Django内置的login函数原理
- 查询用户
- login的逻辑
先将用户的基本信息组成json,然后加密生成加密的session字符串
随机生成一串长的字符,叫做sessionid
将sessionid和session值绑定在一起保存到数据库中
将sessionid写入到cookie中
返回请求给浏览器
盐(Salt)
在密码学中,是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为“加盐”。
第三代密码
本来第二代密码设计方法已经很不错了,只要你密码设置得稍微复杂一点,就几乎没有被破解的可能性。
但是如果你的密码设置得不够复杂,被破解出来的可能性还是比较大的。
好事者收集常用的密码,然后对他们执行 MD5 或者 SHA1,原密码作为键,通过hash处理后的称为值,然后做成一个数据量非常庞大的数据字典,然后对泄露的数据库中的密码做对比,如果你的原始密码很不幸的被包含在这个数据字典中,那么花不了多长时间就能把你的原始密码匹配出来。
这个数据字典很容易收集,CSDN 泄露的那 600w 个密码,就是很好的原始素材。
其实就是hash碰撞,于是,第三代密码设计方法诞生,用户表中多了一个字段:
mysql> desc User;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| UserName | varchar(50) | NO | | | |
| Salt | char(50) | NO | | | |
| PwdHash | char(32) | NO | | | |
+----------+-------------+------+-----+---------+-------+
数据存储形式如下:
mysql> select * from User;
+----------+----------------------------+----------------------------------+
| UserName | Salt | PwdHash |
+----------+----------------------------+----------------------------------+
| lichao | 1ck12b13k1jmjxrg1h0129h2lj | 6c22ef52be70e11b6f3bcf0f672c96ce |
| akasuna | 1h029kh2lj11jmjxrg13k1c12b | 7128f587d88d6686974d6ef57c193628 |
+----------+----------------------------+----------------------------------+
Salt 可以是任意字母、数字、或是字母或数字的组合,但必须是随机产生的,每个用户的 Salt 都不一样,用户注册的时候,数据库中存入的不是明文密码,也不是简单的对明文密码进行散列,而是 MD5( 明文密码 + Salt),也就是说:
MD5('123' + '1ck12b13k1jmjxrg1h0129h2lj') = '6c22ef52be70e11b6f3bcf0f672c96ce'
MD5('456' + '1h029kh2lj11jmjxrg13k1c12b') = '7128f587d88d6686974d6ef57c193628'
当用户登陆的时候,同样用这种算法就行验证。
由于加了 Salt,即便数据库泄露了,但是由于密码都是加了 Salt 之后的散列,坏人们的数据字典已经无法直接匹配,明文密码被破解出来的概率也大大降低。
是不是加了 Salt 之后就绝对安全了呢?淡然没有!坏人们还是可以他们数据字典中的密码,加上我们泄露数据库中的 Salt,然后散列,然后再匹配。
但是由于我们的 Salt 是随机产生的,假如我们的用户数据表中有 30w 条数据,数据字典中有 600w 条数据,坏人们如果想要完全覆盖的坏,他们加上 Salt 后再散列的数据字典数据量就应该是 300000* 6000000 = 1800000000000,一万八千亿啊,干坏事的成本太高了吧。
但是如果只是想破解某个用户的密码的话,只需为这 600w 条数据加上 Salt,然后散列匹配。
可见 Salt 虽然大大提高了安全系数,但也并非绝对安全。
实际项目中,Salt 不一定要加在最前面或最后面,也可以插在中间嘛,也可以分开插入,也可以倒序,程序设计时可以灵活调整,都可以使破解的难度指数级增长。
1、密码加密后存储
简略写:
from django.contrib.auth.hashers import make_password,check_password
#存储的时候
user.password = make_password(password)
#解析的时候
booltype = check_password(password,user.password )
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)