2.eladmin的登陆功能

后端控制登陆的Controller位置

 

 对应前端文件

 二维码

 来看看二维码是怎么实现的,点击图片触发了getCode这个函数

 

 这个getCode函数被放在初始化函数里,可以看到具体的getCodeImg函数接收了一个img和uuid,所以我们要提供给前端的也就是uuid和img

 

 

 

调用的是这个函数,而这个函数调用了request的函数来自/utils/request,具体先不管这个函数是怎么实现的,大概意思就是指定一个方法和url就可以发送请求了

 来到后端的代码

大体步骤是获取验证码实例--》去除小数情况--》缓存进redis保存,并且设置缓存时间--》返回验证码信息给前端

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        return ResponseEntity.ok(SecurityUtils.getCurrentUser());
    }

    @ApiOperation("获取验证码")
    @AnonymousGetMapping(value = "/code")//支持匿名访问的get请求
    //ResponseEntity是可以添加HttpStatus状态码的HttpEntity的扩展类。被用于RestTemplate和Controller层方法 
    public ResponseEntity<Object> getCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();//得到一个验证码实例
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();//uuid=properties对应的是jwt+独立的uui
        String captchaValue = captcha.text();
        //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型,如果二维码是小数就舍弃掉
        //ordinal():用于获取某个枚举对象的位置索引值
        if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 缓存进redis并且设置过期时间,loginProperties.getLoginCode().getExpiration()是两分钟
        redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());//jwt用到的编码方法
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);//ok表示返回状态码是200
    }

几个函数分析

getCaptcha()

 

 

 

 

 

其他的登陆功能

前端会传递给后端的参数在data里面,上面的url和method是请求

其中的uuid是验证码的唯一识别码(之前后端给前端发送验证码的时候同样也往redis里存了一份,所以后续在后端会从redis缓存中拿出来校对)

 

 后端的接收的对象里包括了这几个参数

 具体的登陆授权代码

    @ApiOperation("登录授权")
    @AnonymousPostMapping(value = "/login")//支持匿名访问的post请求
    //@Validated注解用于校验从前端传递的参数
    //@RequestBody将json格式的数据转为java对象,也就是接收前端数据
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密,使用的私钥具体在配置文件可以看到
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 从redis缓存中拿到验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码缓存
        redisUtils.del(authUser.getUuid());
        //判断验证码
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {//验证码不正确
            throw new BadRequestException("验证码错误");
        }
        //security相关的内容
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String token = tokenProvider.createToken(authentication);
        //jwtToken规范,不用去服务器端也保存一份token进行校对,只要是没有过期,就可以使用
        //获得一个登陆的主题,这里封装了所有的信息
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息,为了看目前有多少人在线
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
        }};
        if (loginProperties.isSingleLogin()) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(), token);
        }
        return ResponseEntity.ok(authInfo);
    }

RsaProperties.privateKey

 

 

 

回顾整个登陆流程:

 

进入登陆页面,自动获取验证码,每次生成的验证码都会不一样

后端生成验证码,通过(uuid,值)的键值对放入redis并且设置过期时间为,然后返回uuid,图片,url给前端

前端通过访问auth/code获取后端生成的二维码

用户填写用户名,密码,验证码,点击登陆

前端用预设的公匙进行密码数据加密,发送到后端

后端接收数据后,首先用预设的私匙进行解密,然后从redis中获取验证码,判断是否正确或者过期,然后清除验证码

如果用户的密码和验证码正确就生成token,并保存在线信息,然后返回用户信息和token给前端

posted @ 2021-08-01 12:44  一拳超人的逆袭  阅读(889)  评论(0编辑  收藏  举报