ruoyi源码学习-3 登录后端接口

1,977 阅读2分钟

前言

经过之前两期,我们理解了ruoyi前端登录页面的流程。这期,我们开始分析后端对应的流程。主要分析获取验证码接口captchaImage和登录接口login

若依后端结构

首先我们看一下ruoyi后端的模块组成。

  • admin——web服务入口
  • common——通用工具
  • framework——核心模块
  • generator——代码生成
  • quartz——定时任务
  • system——系统模块

ruoyi后端中是将controller文件统一放入admin模块中。

验证码接口

对应文件位置为ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java

主要流程为:

  1. 生成随机的uuid
  2. 生成随机的验证码图片及结果
  3. 将uuid和验证码结果存入redis
  4. 将验证码图片放入返回值中
@GetMapping("/captchaImage")
// AjaxResult统一返回值
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
    // 保存验证码信息
    // 使用加密的强伪随机数生成器生成该 UUID
    String uuid = IdUtils.simpleUUID();
    String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
​
    String capStr = null, code = null;
    BufferedImage image = null;
​
    // 生成验证码
    // application.yml中配置ruoyi.captchaType
    // 数学模式
    if ("math".equals(captchaType))
    {
        String capText = captchaProducerMath.createText();
        // 表达式
        capStr = capText.substring(0, capText.lastIndexOf("@"));
        // 对应结果
        code = capText.substring(capText.lastIndexOf("@") + 1);
        // 根据表达式生成图片BufferedImage
        image = captchaProducerMath.createImage(capStr);
    }
    // 文本模式
    else if ("char".equals(captchaType))
    {
        // 生成的表达式和结果相同
        capStr = code = captchaProducer.createText();
        image = captchaProducer.createImage(capStr);
    }
    // 将uuid和验证码键值存入redis
    redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
    // 转换流信息写出
    FastByteArrayOutputStream os = new FastByteArrayOutputStream();
    try
    {
        ImageIO.write(image, "jpg", os);
    }
    catch (IOException e)
    {
        return AjaxResult.error(e.getMessage());
    }
​
    AjaxResult ajax = AjaxResult.success();
    ajax.put("uuid", uuid);
    // 转base64格式
    ajax.put("img", Base64.encode(os.toByteArray()));
    return ajax;
}
  1. ruoyi中默认的uuid生成为Version 4,有概率重复www.zhihu.com/question/34…
  2. ruoyi采用ThreadLocalRandom生成随机数,避免多线程竞争争夺
  3. ruoyi中提供了更安全的随机码生成,但会降低性能

登陆接口

对应文件位置为:ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java

@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
{
    AjaxResult ajax = AjaxResult.success();
    // 生成令牌
    String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
            loginBody.getUuid());
    ajax.put(Constants.TOKEN, token);
    return ajax;
}

上段代码可谈的不多,即生成token然后返回。

主要来看一下token的生成过程。这里为了方便大家查看,我去除了异常处理的代码。

public String login(String username, String password, String code, String uuid)
{
    String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
    // 从redis中获取uuid对应验证码
    String captcha = redisCache.getCacheObject(verifyKey);
    redisCache.deleteObject(verifyKey);
    // captcha为空异常
    // code不等于captcha异常
  
    // 用户验证
    Authentication authentication = null;
   // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
  // 调用org.springframework.security验证
     authentication = authenticationManager
    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
    // 用户不存在或密码错误异常
    // 其他异常
  // 异步记录登录信息
  AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
    // 获取用户信息和权限
    LoginUser loginUser = (LoginUser) authentication.getPrincipal();
    // 修改用户最后登录ip和时间
    recordLoginInfo(loginUser.getUser());
    // 生成token
    return tokenService.createToken(loginUser);
}

下期计划

用户登录后,默认进入/index页面。我们查看src/views/index.vue,发现多是静态内容。但进入此页面时,产生了两个请求getInfogetRouters。这两个请求是在第2篇—路由拦截跳转中准入限制router.beforeEach调用的。下期便首先分析getInfo.

  • 分析获取用户信息