“用户多次登录,账号冻结业务”功能实现(代码+详细注释)

目录

设计思路

分析

前后端交互接口

请求

响应

代码实现和详细注释

数据库设计

实体类设计

前后端交互

客户端开发

服务器开发


设计思路


分析

需求:当用户输入的用户名存在,并且密码输错 3 次以上,就触发账号冻结功能(冻结时间自定义0)。

主要分为以下步骤:

  1. 对客户端传入的账号和密码进行非空校验。(防止恶意用户通过 Postman 等工具请求非法数据)
  2. 通过客户端传入的账号获取该用户的所有数据,进行非空校验。
  3. 判断该用户输错密码是否等于 3 次(通过用户表的 state 字段的数值进行判断),若等于 3 次,首先记录下该用户开始冻结的时间(通过用户表中的 createtime 字段记录,这个字段后面会用到),创建一个线程使用 wait 进行冷冻时间等待,当冷冻时间结束,唤醒线程,将用户输错密码次数修改为 0 (state = 0)。
  4. 若输错密码小于 3 次,则校验密码是否正确,若密码错误则 state + 1,若密码正确,则 state = 0。
  5. 若输错密码大于 3 次,则计算剩余冷却时间(剩余冷却时间 = 开始冻结的时间 + 冷冻的时间 - 当前时间),这个计算不难,不好处理的时间格式(代码注释中有详细解释),最后将剩余冷却时间反馈给客户端。

前后端交互接口

请求

POST /user/login
Content-Type: application/json
{
    "username": username.val(),
    "password": password.val()
}

响应

Ps:采用同一返回数据格式处理(“code:状态码,msg:信息,data:数据”)

HTTP/1.1 200 OK
Content-Type: application/json
{
    code: 200,
    msg: "",
    data: 1
}

代码实现和详细注释


数据库设计

用户表如下:

create table userinfo(
    id int primary key auto_increment,
    username varchar(100) unique,
    password varchar(65) not null,
    photo varchar(500) default "img/default.jpg",
    createtime timestamp default current_timestamp,
    updatetime timestamp default current_timestamp,
    `state` int default 0
) default charset 'utf8mb4';

实体类设计

用户实体类如下:

@Data
public class UserInfo {

    private Integer id;
    private String username;
    private String password;
    private String photo;

    //格式化时间处理(处理到秒是为了精确冻结时间)
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "GMT+8")
    private LocalDateTime createtime;
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "GMT+8")
    private LocalDateTime updatetime;

    private Integer state;

}

前后端交互

客户端开发

js代码如下:

        function login() {
            //非空校验
            var username = jQuery("#username");
            var password = jQuery("#password");
            var inputCheckPassword = jQuery("#checkpassword");
            if (username.val() == "") {
                alert("请先输入用户名!");
                username.focus();
                return;
            }
            if (password.val() == "") {
                alert("请先输入密码!");
                password.focus();
                return;
            }
            if(inputCheckPassword.val() == "") {
                alert("请先输入验证码!");
                inputCheckPassword.focus();
                return;
            }
            //比较验证码
            if(inputCheckPassword.val() != checkPassword) {
                alert("验证码错误,请重试");
                inputCheckPassword.focus();
                return;
            }
            //ajax 登录接口
            jQuery.ajax({
                type: "POST",
                url: "/user/login",
                data: {
                    "username": username.val(),
                    "password": password.val()
                },
                success: function (result) {
                    if (result != null && result.code == 200 && result.data != null) {
                        //登录成功
                        location.href = '/myblog_list.html';
                    } else {
                        alert(result.msg);
                    }
                }
            });

服务器开发

    private Object lock = new Object();
    @RequestMapping("/login")
    public AjaxResult login(HttpServletRequest request, HttpServletResponse response, String username, String password) throws IOException, ParseException {
        //非空校验
        if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(403, "账号或密码错误,请稍后重试!");
        }
        UserInfo userInfo = userService.getUserByName(username);
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||
        !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(403, "账号不存在!");
        }
        //安全校验:当用户输入密码错误 3 次执行冻结(禁止用户登录)
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        if(userService.getStateByName(username) == 3) {
            //记录该用户开始冻结时间(格式yyyy-MM-ddThh:mm:ss)
            userInfo.setCreatetime(LocalDateTime.now());
            //修改用户开始冻结时间
            userService.updateUserInfoById(userInfo);
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        try {
                            //state + 1
                            userService.updateStateByName(username, userInfo.getState() + 1);
                            lock.wait(AppVariable.FREEZE_TIME); //这里为了演示效果,时间设置为 10s
                            //解冻:修改 state 为 0
                            userService.updateStateByName(username, 0);
                            lock.notify();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            thread.start();
            return AjaxResult.fail(403, "账号已被冻结,请 10s 后重试!");
        } else if(userService.getStateByName(username) > 3) {
            //账号错误三次以上,计算剩余时间
            //将格式话时间转化为时间戳计算
            String[] time = userInfo.getCreatetime().toString().split("T");
            Date date = simpleDateFormat.parse(time[0] + " " + time[1]);
            //计算相差秒数(以下计算都是 毫秒级别,因此最后需要除 1000 换算到秒)
            return AjaxResult.fail(403, "账号已被冻结,请 "+
                    ((date.getTime() +  AppVariable.FREEZE_TIME - System.currentTimeMillis()) / 1000) +"s 后重试");
        }
        //ps:这里注意要对加盐密码解密
        if(userInfo == null || !PasswordUtils.check(password, userInfo.getPassword())) {
            //用户名不存在或密码错误
            if(!PasswordUtils.check(password, userInfo.getPassword())) {
                //密码错误,该用户的 state 需要 +1
                userService.updateStateByName(username, userInfo.getState() + 1);
            }
            return AjaxResult.fail(403, "账号或密码错误,请稍后重试!");
        }
        //登录成功
        HttpSession session = request.getSession(true);
        session.setAttribute(AppVariable.USER_SESSION_KEY, userInfo);
        //安全校验,登录成功后将 state 状态改回 0
        userService.updateStateByName(username, 0);
        return AjaxResult.success(1);
    }

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
可以使用Python的Flask框架实现登录功能,以下是代码示例: ```python from flask import Flask, request, redirect, render_template app = Flask(__name__) # 学生账号密码存储,可以使用数据库或文件存储 students = {'123': 'password123', '456': 'password456'} # 学生账号冻结状态存储,可以使用数据库或文件存储 frozen_students = set() # 登录页面 @app.route('/', methods=['GET', 'POST']) def login(): if request.method == 'POST': student_id = request.form['student_id'] password = request.form['password'] if student_id in frozen_students: return render_template('login.html', message='该账号已被冻结,请联系管理员') elif student_id in students and password == students[student_id]: # 登录成功,跳转到主页 return redirect('/home') else: # 登录失败,记录错误次数并提示 if student_id in students: students[student_id + '_error_count'] = students.get(student_id + '_error_count', 0) + 1 if students[student_id + '_error_count'] >= 3: frozen_students.add(student_id) del students[student_id + '_error_count'] return render_template('login.html', message='密码错误次数超过三次,该账号已被冻结,请联系管理员') return render_template('login.html', message='学号或密码错误') else: return render_template('login.html') # 主页 @app.route('/home') def home(): return '欢迎进入主页' if __name__ == '__main__': app.run() ``` 在上述代码中,使用`students`字典存储学生账号和密码,使用`frozen_students`集合存储被冻结的学生账号。在登录时,先检查账号是否已被冻结,如果是则返回错误提示;如果账号未被冻结,则检查账号和密码是否匹配,如果匹配则登录成功,跳转到主页;否则记录错误次数并返回错误提示,如果错误次数超过三次则将账号添加到`frozen_students`集合中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值