若依框架——从零到一:前后端分离版实战部署与核心权限系统解析
1. 若依框架初探为什么选择它第一次接触若依RuoYi框架是在三年前的一个企业级项目里。当时团队需要快速搭建一个具备完整权限管理功能的后台系统从零开发显然不现实。在对比了多个开源方案后我们发现这个基于Spring Boot和Vue的前后端分离框架简直就是为国内开发者量身定制的瑞士军刀。若依最吸引我的地方在于它开箱即用的权限系统。不同于需要自己搭建基础的Spring Security脚手架它已经帮你实现了四层权限控制菜单可见性、页面按钮操作、API接口访问、数据行级过滤可视化配置通过管理后台就能完成权限分配不需要反复修改代码多维度隔离支持按部门、角色、用户等多个维度进行权限组合记得第一次用若依生成代码时我们团队的前端小伙伴惊呼这比我们手动写CRUD快10倍确实它的代码生成器能根据数据库表结构自动生成包含前端页面和后端接口的完整功能模块。对于常见的用户管理、订单管理这类功能基本上半小时就能跑通全流程。2. 环境搭建从零启动项目2.1 准备工作清单在开始前请确保你的开发环境满足以下要求实测版本后端环境JDK 1.8推荐Amazon Corretto 11MySQL 5.7注意需要开启大小写敏感Redis 5.0Maven 3.6前端环境Node.js 14推荐LTS版本npm 6 或 yarn 1.22踩坑提示我曾遇到过MySQL 8.0默认认证方式导致连接失败的问题解决方法是在application-druid.yml中配置useSSLfalseallowPublicKeyRetrievaltrue2.2 数据库初始化创建数据库默认名ry-vue可自定义CREATE DATABASE ry-vue DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;执行初始化SQL项目sql目录下mysql -uroot -p ry-vue ry_20230210.sql mysql -uroot -p ry-vue quartz.sql2.3 后端配置要点修改ruoyi-admin/src/main/resources下的配置文件application-druid.yml# 数据源配置 spring: datasource: druid: master: url: jdbc:mysql://localhost:3306/ry-vue?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNull username: root password: yourpasswordapplication.yml# Redis配置 redis: host: 127.0.0.1 port: 6379 password: database: 0启动时如果报错Failed to configure a DataSource检查是否漏了MapperScan注解或者在启动类加上SpringBootApplication(exclude { DataSourceAutoConfiguration.class })2.4 前端启动技巧在ruoyi-ui目录下执行# 首次运行需要安装依赖 npm install --registryhttps://registry.npmmirror.com # 开发模式启动 npm run dev常见问题解决方案端口冲突修改vue.config.js中的devServer.port依赖安装失败删除node_modules后重试或使用yarnESLint报错临时关闭可在package.json中移除vue/cli-plugin-eslint3. 权限系统深度解析3.1 菜单权限控制可见性在若依中创建菜单时这几个参数最容易混淆路由地址对应Vue路由配置的path组件路径指向src/views下的具体组件文件权限字符需要与后端PreAuthorize注解保持一致实际案例创建一个订单管理菜单系统管理 → 菜单管理 → 新增填写基本信息菜单名称订单管理路由地址/order组件路径system/order/index权限配置权限字符system:order:list后端Controller添加注解PreAuthorize(ss.hasPermi(system:order:list)) GetMapping(/list) public TableDataInfo list(Order order) { // ... }3.2 按钮权限精细化控制按钮权限的实现原理是通过自定义指令v-hasPermi。在模板中这样使用el-button v-hasPermi[system:order:add] clickhandleAdd 新增订单 /el-button背后的权限校验逻辑在src/directive/permission/hasPermi.js中实现核心代码function checkPermission(el, binding) { const { value } binding const all_permission *:*:* const permissions store.getters store.getters.permissions if (value value instanceof Array) { if (value.length 0) { const permissionFlag value const hasPermissions permissions.some(permission { return all_permission permission || permissionFlag.includes(permission) }) // 无权限则移除DOM元素 if (!hasPermissions) { el.parentNode el.parentNode.removeChild(el) } } } else { throw new Error(请设置操作权限标签值) } }3.3 接口权限最后的安全防线即使前端做了按钮隐藏恶意用户仍可能直接调用API。若依通过Spring Security的注解提供双重保障PreAuthorize(ss.hasPermi(system:order:remove)) Log(title 订单管理, businessType BusinessType.DELETE) DeleteMapping(/{orderIds}) public AjaxResult remove(PathVariable Long[] orderIds) { return toAjax(orderService.deleteOrderByIds(orderIds)); }这里的ss是若依自定义的权限校验Bean核心逻辑在SysPermissionService中实现支持权限字符串校验如system:user:list角色校验如ss.hasRole(admin)逻辑运算符组合如hasPermi(a) or hasPermi(b)3.4 数据权限实现数据隔离若依的数据权限通过AOP切面实现支持五种范围全部数据权限无过滤自定数据权限自定义SQL本部门数据本部门及以下部门数据仅本人数据使用方法是在Service方法上添加注解DataScope(deptAlias d, userAlias u) public ListOrder selectOrderList(Order order) { return mapper.selectOrderList(order); }切面会动态拼接SQL条件例如当用户有本部门数据权限时实际执行的SQL会变成SELECT * FROM sys_order WHERE dept_id 当前用户部门ID4. 实战技巧与性能优化4.1 代码生成器高级用法若依的代码生成器可以通过修改ruoyi-generator模块的模板文件实现定制化修改vm/java/controller.java.vm可以改变生成的Controller风格在vm/js/api.js.vm中添加统一的错误处理逻辑通过vm/sql/sql.vm调整生成的DDL语句生成代码时有个实用技巧先在界面上配置好生成选项然后点击预览代码复制页面URL中的参数如tablessys_user以后就可以通过命令行快速生成# 生成sys_user表相关代码 curl http://localhost:8080/tool/gen/genCode/sys_user4.2 前后端联调建议接口调试使用ruoyi-admin内置的Swagger访问/swagger-ui.html或配置knife4j增强文档需添加依赖跨域处理 开发环境可以在vue.config.js中配置代理devServer: { proxy: { /prod-api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/prod-api: } } } }API封装技巧 在src/api中封装请求时建议统一错误处理import request from /utils/request export function listOrder(query) { return request({ url: /system/order/list, method: get, params: query, // 添加请求拦截器 interceptors: { requestInterceptor: config { // 可在此添加全局参数 config.headers[X-Custom-Header] value return config }, responseInterceptor: response { // 对响应数据做处理 if (response.code ! 200) { Message.error(response.msg) } return response } } }) }4.3 性能优化方案Redis缓存优化在application.yml中配置缓存过期时间spring: cache: redis: time-to-live: 1800000 # 30分钟MyBatis二级缓存 在mapper.xml中添加cache evictionLRU flushInterval60000 size512 readOnlytrue/前端懒加载 修改路由配置使用动态导入{ path: /system/order, component: () import(/views/system/order/index), name: Order, meta: { title: 订单管理, icon: tree } }打包优化 在vue.config.js中配置CDNconfigureWebpack: { externals: { vue: Vue, element-ui: ELEMENT } }