查查知识网

系统登录界面设计(登录界面设计理念)

发布者:高俊东
导读目录一、登录流程设计二、代码详细设计1. 获取验证码1) 表达式型验证码2) 特殊字符型验证码3) 中文验证码2. 登录验证密码和验证码3. 注销随着互联网技术的更新迭代,越来越多的应用采用手机验证码

目录


一、登录流程设计


二、代码详细设计


1. 获取验证码


1) 表达式型验证码


2) 特殊字符型验证码


3) 中文验证码


2. 登录验证密码和验证码


3. 注销



随着互联网技术的更新迭代,越来越多的应用采用手机验证码的方式登录,更快捷、安全。


现在仍有很多应用采用验证码的模式设计登录系统,不需要手机号注册,也能有效地提升系统的安全性,即使你的账号用户名和密码泄露出去了,但是如果不获取验证码并输入正确验证码的情况下,还是无法进入到系统,也能有效地抵御一些恶意攻击的手段,因为破解验证码相当于多加了一道门槛。


本文就图片验证码登录方式实现详细说明, 其中图片验证码主要有运算表达式型验证码、特殊字符验证码等。


一、登录流程设计


1. 请求进入到系统。


2. 判断该请求是否携带token。前端获取到请求, 如果有token,那么尝试携带token发请求给后端请求用户信息。


3. 判断token是否有效。前端get到用户信息,进入系统,get不到用户信息表示token已经失效,需要进入到重新申请token的流程。


4. 请求验证码。没有token 那么需要进入到生成验证码步骤,进入到登录页面时,前端给后端发一个http请求:
http://localhost:8080/picture。


5. 生成验证码。后端接收到请求后,处理请求, 以下三步相当于并列执行:


1) 使用验证码工具类生成验证码表达式,并生成表达式的结果,将表达式以base64的形式返回给前端。


2) 将表达式的结果存入到本地缓存里并设置失效时间,该结果即为前端所填的验证码值,后续为login匹配做准备, 此处可以使用redis缓存。


3) 生成32位uuid返回给前端,同时后端可以记录请求的ID。


6. 验证验证码。前端发起login请求,携带用户名、密码、验证码和uuid(获取验证码时返回的uuid),后端先比较验证码,此处为什么可以先比较验证码?


理由有二:


1) 获取验证码时,给某个请求标记了一个uuid, 通过比较uuid和code能确认唯一用户。


2) 验证码是存在内存里,速度也快,缓存的作用体现出来了,验证码比较通过后再比较用户名和密码。


3) 用户名和密码通过后,返回给前端login成功的status,后端并将token写入到session里,同时需要给token设置失效时间,这样前端就能从cookie里根据key拿到服务器session里的token了。


7. 注销登录。前端发起注销登录请求, 清除当前用户的token缓存信息,后端同时清除掉session, 前端重新发起请求,刷新登录页面的验证码。


用processon 画了一个登录、验证、注销的流程图:


系统登录界面设计(登录界面设计理念)


二、代码详细设计


借助开源的图形验证码工具箱easy-captcha,引入pom依赖:



            com.github.whvcse
            easy-captcha
            1.6.2


1. 获取验证码


1) 表达式型验证码


系统登录界面设计(登录界面设计理念)


存入缓存的格式 : key: uuid, value: verCode 。


    @GetMapping("/picture")
    @ResponseBody
    public Response getPicture() {
        ArithmeticCaptcha arithmeticCaptcha = new ArithmeticCaptcha(150, 50);
        // 设置表达式的运算位数
        arithmeticCaptcha .setLen(2);
       // 运算表达式:1+1=?,运算结果存放在.text()方法的返回值里。
        String verCode = arithmeticCaptcha.text().toLowerCase();
        String uuid = UUIDUtil.simpleUUID();
        log.info("当前uuid:{},验证码:{}", uid, verCode);
        // 单位:秒/s
        LFUCache.put(uid, verCode, 60);
        return Response.ok(MapUtilJson.build("uuid", uid).build("image", arithmeticCaptcha .tobase64()));
    }


返回给前端json格式长这样:


{
    "code": "0",
    "msg": "操作成功",
    "data": {
        "uuid": "db9f05fb0a1640c0b2cfe0a58e15308e",
        "image": "data:image/png;base64,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"
    },
    "requestId": "92d8bdb5-5609-470f-ac4b-91161954a3f9",
    "success": true
}


2) 特殊字符型验证码


系统登录界面设计(登录界面设计理念)


特殊符号验证码采用SpecCaptcha类实现。


SpecCaptcha captcha = new SpecCaptcha(150, 50, 5);


3) 中文验证码


中文验证码还是闪动的,如下的例子其实是3个字母。


系统登录界面设计(登录界面设计理念)


 ChineseGifCaptcha captcha = new ChineseGifCaptcha(150, 50, 4);


2. 登录验证密码和验证码


后端定义/login接口,验证验证码、用户名和密码,通过后将用户信息设置到session里, key 为currentUser。


    @PostMapping("/login")
    @ApiOperation(value = "登录")
    public Response login(@Validated @RequestBody LoginDto loginDto) {
        // 比较验证码
        String verCode = LFUCache.get(loginDto.getUuid());
        if (!StrUtil.equalsIgnoreCase(verCode , loginDto.getCaptchaCode())) {
            return Response.er("500", "验证码不正确或已失效!");
        }
        // 比较用户名  
        String pwd= ScecrtUtil.sha256(loginDto.getPassword());
        SysUser sysUser = sysUserService.queryUser(loginDto.getUsername(),pwd);
        if (sysUser == null) {
            return Response.er("501", "用户名或密码不正确");
        }
        // 如果有权限,那么可获取用户权限,并设置到session里。
        return Response.ok(SessionUtil.setUserInfo(sysUser));
    }


session 的id 格式为: "token: login:session: 1: 随机数", 其中1为用户id,生成token的形式采用对 "token: login:session: 1 : 随机整数(10000范围以内的4位数)" 可逆的方式加密, 这样能保证每次登录请求生成的token 是不同的; 当前端从cookie里把token拿出来后,根据当前用户id 去比较session里的token, 如果token 匹配成功,那么能继续后续访问,如果匹配失败,那么需要重新生成token并写入到session和cookie里。


1) session.set("token: login:session: 1: 9999","0ad7ed338e364cbe806e0b0a6ea3d181")


2) cookie.set("token","0ad7ed338e364cbe806e0b0a6ea3d181");


3. 注销


前端会携带token 放在requestHeader里发起注销请求: /logout。请求头为:


token = 0ad7ed338e364cbe806e0b0a6ea3d181


1) 在后端根据cookieName的值'token'来删除cookie即可。


2) 根据 token 的value 解密获取到指定的用户id,然后根据用户id删除掉对应session的value值。


3) 如果token 存缓存里,那么需要从缓存里移除掉。