若依源码分析

一.登录

1.1 生成验证码

基本思路

  • 后端生成一个表达式,7+4=?@11

  • 7+4=?转成图片,传到前端进行展示

  • 将结果11存入redis

  • 前端代码实现:

    请求后端地址:http://localhost/dev-api/captchaImage,通过反向代理解决前后端跨域问题,将请求路径变为:http://localhost:8080/captchaImage

  • 后端代码实现:

    java 复制代码
    /**
     * 生成验证码
     */
    @GetMapping("/captchaImage")
    public AjaxResult getCode(HttpServletResponse response) throws IOException
    {
        AjaxResult ajax = AjaxResult.success();
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        ajax.put("captchaEnabled", captchaEnabled);
        if (!captchaEnabled)
        {
            return ajax;
        }
    
        // 保存验证码信息
        String uuid = IdUtils.simpleUUID();
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
    
        String capStr = null, code = null;
        BufferedImage image = null;
    
        // 生成验证码
        String captchaType = RuoYiConfig.getCaptchaType();
        if ("math".equals(captchaType))
        {
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            image = captchaProducerMath.createImage(capStr);
        }
        else if ("char".equals(captchaType))
        {
            capStr = code = captchaProducer.createText();
            image = captchaProducer.createImage(capStr);
        }
    
        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());
        }
    
        ajax.put("uuid", uuid);
        ajax.put("img", Base64.encode(os.toByteArray()));
        return ajax;
    }

1.2 登录

  • 后端

    • 1.校验验证码
    • 2.校验用户名和密码
    • 3.生成ToKen
    java 复制代码
    /**
     * 登录方法
     * 
     * @param loginBody 登录信息
     * @return 结果
     */
    @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;
    }

    使用异步任务管理器,结合线程池,实现了异步的操作日志记录,和业务逻辑实现异步解耦合.

1.3 获取用户信息

java 复制代码
/**
 * 获取用户信息
 * 
 * @return 用户信息
 */
@GetMapping("getInfo")
public AjaxResult getInfo()
{
    SysUser user = SecurityUtils.getLoginUser().getUser();
    // 角色集合
    Set<String> roles = permissionService.getRolePermission(user); // 超级管理员
    // 权限集合
    Set<String> permissions = permissionService.getMenuPermission(user); // *:*:*
    AjaxResult ajax = AjaxResult.success();
    ajax.put("user", user);
    ajax.put("roles", roles);
    ajax.put("permissions", permissions);
    return ajax;
}

获取当前用户的角色和权限信息,存储到Vuex中

1.4 获取路由信息

根据当前用户的权限信息获取动态路由,最终完成动态菜单的展示

java 复制代码
/**
 * 获取路由信息
 * 
 * @return 路由信息
 */
@GetMapping("getRouters")
public AjaxResult getRouters()
{
    Long userId = SecurityUtils.getUserId();
    List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
    return AjaxResult.success(menuService.buildMenus(menus));
}

二.首页加载

当用户登录成功以后我们可以根据路由看出,默认跳转到路由/viees/index.vue页面

三.用户管理

3.1 查询

流程:加载vue页面 -> 请求后台数据

  • 获取用户信息后端

    java 复制代码
    /**
     * 获取用户列表
     */
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysUser user)
    {
        startPage(); // 做分页
        List<SysUser> list = userService.selectUserList(user); // 查询数据
        return getDataTable(list); // 返回结果
    }
  • 获取树状图后端

    java 复制代码
    /**
     * 获取部门树列表
     */
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/deptTree")
    public AjaxResult deptTree(SysDept dept)
    {
        return success(deptService.selectDeptTreeList(dept));
    }

3.2 添加用户

  • 前端
js 复制代码
/** 新增按钮操作 */
handleAdd() {
    this.reset();// 清空表单
    getUser().then(response => {
        this.postOptions = response.posts;// 岗位
        this.roleOptions = response.roles;// 角色
        this.open = true;
        this.title = "添加用户";
        this.form.password = this.initPassword;
    });
},
    /** 提交按钮 */
    submitForm: function() {
        this.$refs["form"].validate(valid => {
            if (valid) {
                if (this.form.userId != undefined) {
                    updateUser(this.form).then(response => {
                        this.$modal.msgSuccess("修改成功");
                        this.open = false;
                        this.getList();
                    });
                } else {
                    addUser(this.form).then(response => {
                        this.$modal.msgSuccess("新增成功");
                        this.open = false;
                        this.getList();
                    });
                }
            }
        });
    },
  • 后端
java 复制代码
/**
 * 根据用户编号获取详细信息
 */
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
{
    userService.checkUserDataScope(userId);
    AjaxResult ajax = AjaxResult.success();
    List<SysRole> roles = roleService.selectRoleAll();
    ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
    ajax.put("posts", postService.selectPostAll());
    if (StringUtils.isNotNull(userId))
    {
        SysUser sysUser = userService.selectUserById(userId);
        ajax.put(AjaxResult.DATA_TAG, sysUser);
        ajax.put("postIds", postService.selectPostListByUserId(userId));
        ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
    }
    return ajax;
}

/**
 * 新增用户
 */
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user)
{
    if (!userService.checkUserNameUnique(user))
    {
        return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
    }
    else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user))
    {
        return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
    }
    else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user))
    {
        return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
    }
    user.setCreateBy(getUsername());
    user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
    return toAjax(userService.insertUser(user));
}

修改和删除查看源码步骤相同,这里省略

四.异步任务管理器

这里选取一段代码作示例:com.ruoyi.framework.web.service.SysLoginService

java 复制代码
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));

通过异步任务管理器记录登录日志

  • AsyncManager.me():获取一个AsyncManager对象
  • 执行execute方法,传入的是一个Task对象,实现了Runnable接口表示是一个任务,由线程Thread去执行
java 复制代码
/**
 * 记录登录信息
 * 
 * @param username 用户名
 * @param status 状态
 * @param message 消息
 * @param args 列表
 * @return 任务task
 */
public static TimerTask recordLogininfor(final String username, final String status, final String message,
                                         final Object... args)
{
    final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
    final String ip = IpUtils.getIpAddr();
    return new TimerTask()
    {
        @Override
        public void run()
        {
            String address = AddressUtils.getRealAddressByIP(ip);
            StringBuilder s = new StringBuilder();
            s.append(LogUtils.getBlock(ip));
            s.append(address);
            s.append(LogUtils.getBlock(username));
            s.append(LogUtils.getBlock(status));
            s.append(LogUtils.getBlock(message));
            // 打印信息到日志
            sys_user_logger.info(s.toString(), args);
            // 获取客户端操作系统
            String os = userAgent.getOperatingSystem().getName();
            // 获取客户端浏览器
            String browser = userAgent.getBrowser().getName();
            // 封装对象
            SysLogininfor logininfor = new SysLogininfor();
            logininfor.setUserName(username);
            logininfor.setIpaddr(ip);
            logininfor.setLoginLocation(address);
            logininfor.setBrowser(browser);
            logininfor.setOs(os);
            logininfor.setMsg(message);
            // 日志状态
            if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
            {
                logininfor.setStatus(Constants.SUCCESS);
            }
            else if (Constants.LOGIN_FAIL.equals(status))
            {
                logininfor.setStatus(Constants.FAIL);
            }
            // 插入数据
            SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
        }
    };
}

封装了登录用户的信息,执行添加操作,这里不会执行,而是将任务交给线程对象来执行

异步任务管理器,内部定义了一个线程池,然后根据业务创建添加日志的任务,交给线程池来处理,这样做到日志和业务的抽象,解耦合,日志全部统一处理.

五.代码自动生成

5.1 创建数据表

sql 复制代码
use ry_vue;
create table test_user
(
    id       int primary key auto_increment,
    name     varchar(11),
    password varchar(11)
);

5.2 系统工具-->代码生成

5.2.1 导入数据表
5.2.2 编辑数据表
5.2.3 点击生成代码
5.2.4 解压压缩包
5.2.5 导入代码,重启项目
  • 后端
  • 前端
  • 数据库
相关推荐
胡耀超10 天前
解读若依微服务架构图:架构总览、核心模块解析、消息与任务处理、数据存储与缓存、监控与日志
微服务·云原生·架构·若依
想要打 Acm 的小周同学呀12 天前
若依框架--数据字典设计使用和前后端代码分析
java·vue3·数据字典·若依
胡耀超14 天前
解读若依框架中的 @Xss 注解
xss·若依
胡耀超14 天前
解读若依框架中的`@Excel` 和 `@Excels` 注解
java·excel·若依
bjzhang7519 天前
若依使用 Undertow 替代 Tomcat 容器
tomcat·若依·undertow
bjzhang751 个月前
若依整合 Gitee 登录
gitee·若依
代码拯救不了世界1 个月前
若依框架中的上传图片后如何实现回显到页面的
java·图片上传·若依
bjzhang751 个月前
若依微服务如何获取用户登录信息
若依
bjzhang751 个月前
若依启动项目时配置为 HTTPS 协议
https·若依
duansamve1 个月前
如何快速搭建若依管理系统?
若依