虽然本博客讲述的是ruoyi后端代码的调用,但是查看前端代码可以帮助理解,所以在这篇博客中会先以前端代码为引入,然后讲述后端的实现。
目录
1.验证码
我们先拉起ruoyi,打开检查工具,查看请求发现我们刚打开页面,就有两条请求。
那什么是需要打开登陆页面就需要向后端请求的呢?通过分析是验证码的请求。
1.1前端
我们在前端找到关于验证码请求这块的内容,发现前端从后端的API中获得验证码的图片信息然后将其渲染到前端页面。
1.2后端
那我们来看后端验证码这块的代码:
// 使用GET请求映射到"/captchaImage"路径的控制器方法,用于获取验证码图片
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException {
// 创建一个成功的AjaxResult对象
AjaxResult ajax = AjaxResult.success();
// 查询是否启用验证码功能
boolean captchaOnOff = configService.selectCaptchaOnOff();
// 在响应中添加"captchaOnOff"字段,表示是否启用验证码
ajax.put("captchaOnOff", captchaOnOff);
// 如果未启用验证码功能,直接返回AjaxResult
if (!captchaOnOff) {
return ajax;
}
// 生成验证码
String uuid = IdUtils.simpleUUID(); // 生成一个UUID,通常用于验证码验证
String verifyKey = Constants.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);
}
// 将验证码存储到Redis缓存,并设置过期时间
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 将验证码图片转换为Base64编码,以便在前端显示
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", os);
} catch (IOException e) {
return AjaxResult.error(e.getMessage());
}
// 将UUID、验证码图片的Base64编码存储在AjaxResult中并返回
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray());
return ajax;
}
这两个方法协同工作,允许系统根据配置文件中的参数来控制验证码功能的开关状态,以便在需要时启用或禁用验证码验证。方法selectConfigByKey
负责从缓存或数据库中获取具体的配置值,而方法selectCaptchaOnOff
判断验证码是否开启。
2.登陆
当用户成功输入用户名、密码以及验证码以后,点击登陆按钮。
2.1前端
前端需要异步向后端发送请求,将用户名、密码和验证码用post向后端请求。
将传过来的数据封装到data中,然后return表示的是带着data数据对url进行post请求。
然后我们就发现用户在登陆后多了一个token
2.2后端
我们在后端接口中可以发现loginBody中获得的就是前端写入的内容。
public String login(String username, String password, String code, String uuid)
{
// 检查验证码开关状态
boolean captchaOnOff = configService.selectCaptchaOnOff();
// 如果验证码开关开启,执行验证码验证
if (captchaOnOff)
{
validateCaptcha(username, code, uuid);
}
// 用户验证
Authentication authentication = null;
try
{
// 调用authenticationManager进行用户认证,通常会调用UserDetailsServiceImpl.loadUserByUsername方法
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
catch (Exception e)
{
// 处理用户验证异常,通常会捕获BadCredentialsException异常表示密码错误
if (e instanceof BadCredentialsException)
{
// 记录登录失败信息并抛出用户密码不匹配异常
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
// 记录登录失败信息并抛出ServiceException异常
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
// 记录登录成功信息
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
// 从Authentication对象中获取登录用户信息
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 记录登录信息
recordLoginInfo(loginUser.getUserId());
// 生成并返回登录用户的token
return tokenService.createToken(loginUser);
}
当验证码以及用户的账号密码全部验证通过,就登陆成功啦!