Flask后台登录

在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
});