文章目录
0. 前言
Ruoyi前后端分离版:SpringBoot + Vue
官网:https://ruoyi.vip
目标
学习开源项目的目标:
- 用,减少自己的工作量
- 学习优秀开源项目的底层编程思想、设计思路,提升自己的编程能力
使用、学习开源项目的流程:
- 下载并运行
- 看懂业务流程
- 进行二次开发
功能的基本流程
- 加载Vue页面
- 请求后端
环境要求
- JDK1.8+
- MySQL8+
- Redis
- Maven
- Vue
1. 运行Ruoyi
1.1 下载
从Gitee官网复制url在IDEA中打开(后端),注意前端Vue项目ruoyi-ui
需要额外使用一个idea打开。
1.2 配置数据库
表:直接执行/sql
下的两个sql文件,在本地创建表
数据源:修改配置文件中数据源配置
1.3 配置Redis
使用Docker启动Redis
修改Redis配置
1.4 日志
需要在ruoyi-admin/src/main/resources/logback.xml
中修改日志存放位置:
1.5 启动后端
启动admin中的springboot启动类
(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙
.-------. ____ __
| _ _ \ \ \ / /
| ( ' ) | \ _. / '
|(_ o _) / _( )_ .'
| (_,_).' __ ___(_ o _)'
| |\ \ | || |(_,_)'
| | \ `' /| `-' /
| | \ / \ /
''-' `'-' `-..-'
1.6 启动前端
根据ruoyi-ui
项目中的README.md
文件进行配置安装依赖,然后启动
# 克隆项目
git clone https://gitee.com/y_project/RuoYi-Vue
# 进入项目目录
cd ruoyi-ui
# 安装依赖
npm install
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npmmirror.com
# 启动服务
npm run dev
2. 登陆功能
2.1 验证码
基本思路
简而言之:前端让后端出一道算术题,后端把题目告诉前端,并把答案放入后端的Redis中,前端计算完结果后去后端的Redis中比对答案。
每次需要登录时,会在后端自动生成验证码,如“1+1=?@2”,验证码“1+1=?”会被转成图片传到前端登陆页面,答案“2”会被存储进Redis中(@是用于分割问题和答案的标记符号)。当前端输入完账号密码和验证码后,系统会拿验证码“2”和Redis中的答案“2”进行比较,成功则再验证账号密码。Redis中的key值会被传到前端,如果有多人登陆,每个客户端可根据自己的key值查询redis中的value值(答案)。
如果通过Docker启动的Redis,可通过交互模式进入Redis容器,然后进入Redis客户端查看验证码答案。
# 通过交互模式进入Redis容器
docker exec -it 6ce bash
# 进入Redis
redis-cli
# 查看所有key
keys *
# 查看验证码答案
127.0.0.1:6379> get captcha_codes:aecd3ba23ab94614b2a7840e2625107c
"\"35\""
前端实现
请求的封装
验证码的代码实现在ruoyi-ui/src/views/login.vue
中。
基本流程概括:打开登陆页面,向后端请求验证码图片和一个uuid(Redis的key)
前端Vue和后端Springboot交互时通常使用axios
(ajax),而这里看不到axios的调用是因为进行了多次封装。如果再深入追溯,可进入getCodeImg()
方法,然后发现还有封装:
进入login.js
找到getCodeImg()
,发现了ajax的基本写法:url、请求类型、超时时间,注意至此还是在request
封装中,依旧没有看到axios。
// 获取验证码
export function getCodeImg() {
return request({
url: '/captchaImage',
headers: {
isToken: false
},
method: 'get',
timeout: 20000
})
}
接着进入ruoyi-ui/src/utils/request.js
,找到axios:
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
})
其中VUE_APP_BASE_API
定义在了配置文件.env.development
中:
# 若依管理系统/开发环境
VUE_APP_BASE_API = '/dev-api'
这样任何请求都添加前缀’/dev-api’。
统一前缀是为了区分开发环境和生产环境。
反向代理

此时注意一个点:Vue获取图片时,前端项目的端口是1024;后端项目的端口是8080。理论上对验证码的信息的请求应该是对后端发起请求,但是url中实际还是对前端1024端口请求。
原因:反向代理,url在前端请求前端,进行代理,映射到后端,如此操作是为了解决跨域问题。跨域问题在后端的解决方式是Springboot添加一个配置类;前端的解决方式是反向代理。跨域问题在前端或者后端解决都可。
反向代理的配置在ruoyi-ui/vue.config.js
中:
// webpack-dev-server 相关配置
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
disableHostCheck: true
},
上面的pathRewrite
里,会把前面的请求前缀替换为空,即’',再映射到后端的端口,即target
。如此请求url从http://localhost:1024/dev-api/captchaImage
变成了http://localhost:8080/captchaImage
。
后端实现
首先先定位到验证码功能的控制器,使用全局搜索(ctrl+shift+F)对admin项目搜索captchaImage
,找到CaptchaController
。
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
// 最终需要返回给前端的ajax结果(封装版)
AjaxResult ajax = AjaxResult.success();
// 检查是否开启验证码
boolean captchaOnOff = configService.selectCaptchaOnOff();
ajax.put("captchaOnOff", captchaOnOff);
if (!captchaOnOff)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
// 拼接一个key,用于放入redis,如“captcha_codes:aecd3ba23ab94614b2a7840e2625107c”
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);
}
// 将key和value存入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());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
这个AjaxResult
就是后端给前端返回的数据对象,通常称为VO或ResultVO或R(前端与后端交互时的统一数据模型)。
2.2 登陆
前端实现
登陆的前端实现和验证码一样,依旧使用了前端反向代理。
登陆功能的前端实现主要是由handleLogin()
方法实现的。
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, {
expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), {
expires: 30 });
Cookies.set('rememberMe', this.loginForm.rememberMe, {
expires: 30 });
} else {
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove('rememberMe')</