一.登录
1.1 生成验证码
基本思路
-
后端生成一个表达式,
7+4=?@11
-
7+4=?
转成图片,传到前端进行展示 -
将结果
11
存入redis -
前端代码实现:
请求后端地址:http://localhost/dev-api/captchaImage,通过反向代理解决前后端跨域问题,将请求路径变为:http://localhost:8080/captchaImage
-
后端代码实现:
/*** 生成验证码*/ @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
/*** 登录方法* * @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 获取用户信息
/*** 获取用户信息* * @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 获取路由信息
根据当前用户的权限信息获取动态路由,最终完成动态菜单的展示
/*** 获取路由信息* * @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页面 -> 请求后台数据
-
获取用户信息后端
/*** 获取用户列表*/ @PreAuthorize("@ss.hasPermi('system:user:list')") @GetMapping("/list") public TableDataInfo list(SysUser user) {startPage(); // 做分页List<SysUser> list = userService.selectUserList(user); // 查询数据return getDataTable(list); // 返回结果 }
-
获取树状图后端
/*** 获取部门树列表*/ @PreAuthorize("@ss.hasPermi('system:user:list')") @GetMapping("/deptTree") public AjaxResult deptTree(SysDept dept) {return success(deptService.selectDeptTreeList(dept)); }
3.2 添加用户
- 前端
/** 新增按钮操作 */
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();});}}});},
- 后端
/*** 根据用户编号获取详细信息*/
@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
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
通过异步任务管理器记录登录日志
AsyncManager.me()
:获取一个AsyncManager对象- 执行
execute
方法,传入的是一个Task对象,实现了Runnable接口
表示是一个任务,由线程Thread去执行
/*** 记录登录信息* * @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(){@Overridepublic 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 创建数据表
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 导入代码,重启项目
- 后端
- 前端
- 数据库