登录功能前端验证码
img标签的src属性可以放的值:
- 本地图片的地址
- 图片的二进制数据
- 后端url, 页面渲染完毕后会自动朝该url发送get请求
验证2和3
'''
views.py
def get_code(request):
with open('avatar/111.jpg', 'rb') as f:
data = f.read()
return HttpResponse(data)
login.html
<img src="/get_code/" alt="" width="360" height="35">
'''
存取随机生成的图片
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO
'''
pillow模块(生成图片):
Image: 生成图片
ImageDraw: 在图片上写字
ImageFont: 控制字的字体样式
io模块(内存管理):
BytesIO: 保存数据, 并且在获取时是以二进制形式获取
StringIO: 保存数据, 并且在获取时是以字符串形式获取
'''
def get_random():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
def get_code(request):
# 方式一: 以文件形式存取, 有io时间, 较慢
# img_obj = Image.new('RGB', (360, 35), get_random()) # 生成图片对象
# with open(r'avatar/xxx.png', 'wb') as f: # 需先将图片对象保存到本地
# img_obj.save(f, 'png')
# with open(r'avatar/xxx.png', 'rb') as f: # 将保存的随机生成的图片数据读取
# data = f.read()
# return HttpResponse(data) # 将保存的随机生成的图片数据发送
# 方式二: 通过io模块在内存中存取在内存中存取, 较快
img_obj = Image.new('RGB', (360, 35), get_random()) # 生成图片对象
img_draw = ImageDraw.Draw(img_obj) # 生成画笔对象
img_font = ImageFont.truetype('static/font/444.TTF', 30) # 控制字体样式的文件以.ttf结尾
code = ''
for i in range(5):
upper_str = chr(random.randint(65, 90)) # 将数字转换成ascii码对应的字符
lower_str = chr(random.randint(97, 122))
digit_str = chr(random.randint(0, 9))
temp = random.choice([upper_str, lower_str, digit_str]) # 从大写字母, 小写字母, 数字中随机选取一个
img_draw.text((i * 60 + 60, 0), temp, get_random(), img_font) # 在图片中写入随机选取的字符, 并控制字符间距
code += temp # 存储写入的字符
request.session['code'] = code # session存储验证码, 是全局的视图函数都能访问
io_obj = BytesIO() # 生成一个io对象, 相当于文件句柄
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue()) # 获取二进制图片数据
验证码点击刷新及登录功能的实现
验证码点击刷新
'''
# login.html
<script>
$('#id_img').on(
'click',
function () {
let oldPath = $(this).attr('src');
$(this).attr('src', oldPath += '?')
}
);
...
</script>
'''
登录功能实现
'''
# views.py
def login(request):
if request.method == 'POST':
back_dic = {'code': 1000, 'msg': ''}
username = request.POST.get('username')
password = request.POST.get('password')
code = request.POST.get('code')
if request.session.get('code').upper() == code.upper(): # 验证码忽略大小写
user_obj = auth.authenticate(request, username=username, password=password) # auth模块校验用户名和密码
if user_obj:
auth.login(request, user_obj) # 记录用户登录状态
back_dic['msg'] = '登录成功'
back_dic['url'] = '/home/' # 登录成功页面跳转到首页
else:
back_dic['code'] = 2000
back_dic['msg'] = '用户名或密码错误'
else:
back_dic['code'] = 3000
back_dic['msg'] = '验证码错误'
print(back_dic)
return JsonResponse(back_dic)
return render(request, 'login.html')
# login.html
<script>
...
$('#id_submit').on(
'click',
function () {
$.ajax({
url: '',
type: 'post',
data: {
'username': $('#id_username').val(),
'password': $('#id_password').val(),
'code': $('#id_code').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
success: function (data) {
if (data.code == 1000) {
window.location.href = data.url; // 前端控制页面跳转
}
else {
$('#id_error').text(data.msg);
let oldPath = $('#id_img').attr('src'); // 用户提交的信息错误时刷新验证码
$('#id_img').attr('src', oldPath += '?')
}
}
})
}
)
</script>
'''
导航条注销和修改密码两个功能的实现
'''
# views.py
@login_required # 全局配置未登录时的跳转页面: settings.py-->LOGIN_URL = '/login/'
def logout(request):
auth.logout(request) # 使用auth模块的注销功能
_url = reverse('home') # 反向解析
return redirect(_url)
@login_required
def change_password(request):
...
is_right = request.user.check_password(old_password) # 使用auth模块的单独校验密码的功能
...
return JsonResponse(back_dic)
# home.html
<button type="button" class="btn btn-primary" id="id_change_password">修改</button>
<script>
$('#id_change_password').on(
'click',
function () {
$.ajax({ // ajax提交POST请求
url: {% url 'change_password' %}, // 反向解析
type: 'post',
data: {
...
},
success: function (data) {
if (data.code == 1000) {
window.location.href = data.url; // 前端控制页面跳转
}
else {
$('#id_error').text(data.error) // 前端展示报错信息
}
}
})
}
);
$('input').on(
'focus',
function () { // input框获取焦点时清除报错信息
$('#id_error').text('')
}
)
</script>
'''