在Flask Web应用中,登录是常用的操作之一,本片文章主要记录结合验证码,实现后台用户登录的功能。
0x01 验证码逻辑
验证码部分要注意,不小心就写出bug,首先要保证验证码的时效性,同时也得保证验证码,只能验证一次,第二次使用后就要失效。
- 验证码验证用户登录时,如果用户输入的账号或者密码错误,重新刷新验证码
- 如果只是验证码填写错误,不需要更新验证码
- 后端设置验证码的有效时间
这里总结几点关于验证码的心得
0x02 验证码存储
验证码的价值具有时效性,所以不需要永久存储,所有针对这个特征,可以使用memcached
或者redis
进行存储,这两个数据库都可以设置数据生存时间,以满足验证功码时效性问题。
0x03 后端逻辑
后端逻辑的话,就是在正常的登录逻辑上,加上验证码的逻辑。在后端验证时,需要先验证验证码是否正确,然后在进行用户名密码的验证,这点逻辑需要注意。
1 | |
2 | class LoginView(views.MethodView): |
3 | ''' |
4 | 登录视图类 |
5 | ''' |
6 | def get(self): |
7 | message = request.args.get('message') |
8 | return render_template('admin/login.html', message=message) |
9 | |
10 | def post(self): |
11 | form = LoginForm(request.form) |
12 | if form.validate(): |
13 | email = form.email.data # 邮箱或者用户名 |
14 | password = form.password.data |
15 | remember = form.remember.data |
16 | user = Admin.query.filter_by(email=email).first() or Admin.query.filter_by(username=email).first() |
17 | if user and user.check_password(password): |
18 | session[app.config.get('CMS_USER_ID')] = user.id # 保存用户登录信息 |
19 | if remember: |
20 | # 如果设置session.permanent = True,那么过期时间为31天 |
21 | session.permanent = True |
22 | user.last_login_time = datetime.datetime.now() |
23 | db.session.add(user) |
24 | db.session.commit() |
25 | return field.success(message='登陆成功!') |
26 | else: |
27 | return field.params_error(message='邮箱或者密码错误') |
28 | |
29 | else: |
30 | message = form.get_error() |
31 | return field.params_error(message=message) |
32 | |
33 | class LoginForm(BaseForm): |
34 | ''' |
35 | 登录表单验证 |
36 | ''' |
37 | email = StringField(validators=[InputRequired('请输入邮箱或者用户名')]) |
38 | password = StringField(validators=[Length(6, 20, message='请输入正确格式的密码'), InputRequired(message='请输入密码')]) |
39 | graph_captcha = StringField( |
40 | validators=[Regexp(r'\w{4}', message='请输入正确格式的图形验证码!'), InputRequired(message='请输入验证码')]) |
41 | remember = IntegerField() |
42 | |
43 | def validate_graph_captcha(self, field): #验证验证码 |
44 | from utils import cache |
45 | graph_captcha = field.data |
46 | graph_captcha_mem = cache.get(graph_captcha.lower()) |
47 | if not graph_captcha_mem: #如果验证不成功 |
48 | raise ValidationError(message='图形验证码错误!') |
49 | else: #验证成功,删除验证码,防止多次使用 |
50 | cache.delete_capt(graph_captcha.lower()) |
0x04 前端代码
前端代码中使用到模拟点击事件,当后端返回的值为信息中包含邮箱或者密码错误
或者请输入正确格式的密码
时,进行验证码的更换。
1 | $(function () { |
2 | $(".login_btn").click(function (event) { |
3 | event.preventDefault(); |
4 | var email_input = $("input[name=email]"); |
5 | var password_input = $("input[name='password']"); |
6 | var graph_captcha_input = $("input[name='graph_captcha']"); |
7 | var remember_input = $("input[name='remember']"); |
8 | |
9 | var email = email_input.val(); |
10 | var password = password_input.val(); |
11 | var remember = remember_input.checked ? 1 : 0; |
12 | var graph_captcha = graph_captcha_input.val(); |
13 | zlajax.post({ |
14 | 'url': "xxxx", //后端登录url |
15 | 'data': { |
16 | 'email': email, |
17 | 'password': password, |
18 | 'remember': remember, |
19 | 'graph_captcha': graph_captcha |
20 | }, |
21 | 'success': function (data) { |
22 | if (data['code'] == 200) { |
23 | zlalert.alertSuccessToast(data['message']); |
24 | setTimeout(function () { |
25 | window.location = 'xxx'; //登录成功,重定向后台首页 |
26 | },2000); |
27 | } else { |
28 | zlalert.alertInfoToast(data['message']); |
29 | if(data['message']=='邮箱或者密码错误' || data['message']=='请输入正确格式的密码') |
30 | { |
31 | $('#captcha-img').trigger('click'); //触发验证码更换事件 |
32 | } |
33 | } |
34 | }, |
35 | 'fail':function (error) { |
36 | zlalert.alertNetworkError(); |
37 | } |
38 | }) |
39 | }) |
40 | }); |