# springboot-simplify-dev **Repository Path**: sgy_project/springboot-simplify-dev ## Basic Information - **Project Name**: springboot-simplify-dev - **Description**: 这是一个基于springboot开发的用于简化项目开发, 您可以很方便的基于现有的代码进行二次开发 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-06-22 - **Last Updated**: 2021-12-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: 简化开发, 开发 ## README # 脚手架 springboot-simplify-dev: springboot项目的脚手架 通过使用本项目的脚手架可以快速开发而不需要自己去再次搭建工程, 本脚手架中已经包含开发中需要的基本功能, 比如全局异常处理, 统一结果返回,参数校验,统一认证系统等等功能。 # 环境依赖 1. maven ===> 3.6.3 (https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/) # 基本架构 所有的组件以及扩展都是在基础架构之上运行的 ``` com.simplifydev ├── simplifydev-ui // 前端框架 ├── simplifydev-gateway // 网关模块 ├── simplifydev-auth // 认证中心 ├── simplifydev-api // 接口模块 │ └── simplifydev-api // 系统接口 ├── component // 通用模块 │ └── component-core // 核心模块 │ └── component-datascope // 权限范围 │ └── component-datasource // 多数据源 │ └── component-log // 日志记录 │ └── component-cache // 缓存服务 │ └── component-security // 认证服务 | └── component-web // 只要是web服务就必须依赖该模块 ├── simplifydev-modules // 业务模块 │ └── simplifydev-module-system // 系统模块 │ └── simplifydev-module-file // 文件服务 | └── simplifydev-module-job // 任务服务 | └── simplifydev-module-gen // 生成代码服务 | └── simplifydev-module-start // 整合全部服务,用于单体应用,好处就是按模块开发 ├──pom.xml // 公共依赖 ``` # 组件 ## component-core ### 工具类(utils) - 文件分片 (`FileSplitter`) - 文件合并 () # 目录结构 ```text com.simplifydev ├── simplifydev-ui // 前端框架 ├── simplifydev-gateway // 网关模块 ├── simplifydev-auth // 认证中心 ├── simplifydev-api // 接口模块 │ └── simplifydev-api // 系统接口 ├── component // 通用模块 │ └── component-core // 核心模块 │ └── component-datascope // 权限范围 │ └── component-datasource // 多数据源 │ └── component-log // 日志记录 │ └── component-cache // 缓存服务 │ └── component-swagger // 系统接口 │ └── component-security // 认证服务 | └── component-web // 只要是web服务就必须依赖该模块 ├── simplifydev-modules // 业务模块 │ └── simplifydev-module-system // 系统模块 │ └── simplifydev-module-file // 文件服务 | └── simplifydev-module-job // 任务服务 | └── simplifydev-module-gen // 生成代码服务 | └── simplifydev-module-start // 整合全部服务,用于单体应用,好处就是按模块开发 ├──pom.xml // 公共依赖 ``` # 快速生成自己的工程 在实际项目中,有规定项目名称以及包名以及项目前缀,这时可以使用 component/core/CopyProject 类实现该功能 该类会拷贝一个工程,您可以在类的开头指定一些参数,然后运行主函数即可在当前工程的同级目录下生成你所需要的工程 ```java /** 工程名 */ private static final String projectName = "test"; /** 工程名简写 每个模块的前缀 */ private static final String projectNameShort = "test"; /** 包名 */ private static final String packageName = "com.test"; ``` - 工程名简写就是每个子模块的前缀 - 工程名就是项目的父级模块名称也是项目所在的根目录的文件夹名称 > 不会对xxx-ui目录下的所有文件做任何处理 (前端资源) # 详细功能说明 ## 数据权限说明 你需要在每个需要进行过滤数据的方法中添加user_id字段,这个字段表示的是创建者的用户id,内部程序会以及该user_id对数据进行过滤 数据过滤sql例子 ```sql SELECT r.role_id,r.role_name,r.create_by FROM sys_role r INNER JOIN sys_user_dept_rel ud ON r.user_id = ud.user_id WHERE -- 此处填写自定义条件 【至少一个条件,如果没有自定义条件则写1 = 1】 -- 数据范围过滤 ${params.dataScope} SELECT d.dict_id,d.dict_name,d.create_by FROM sys_dict d INNER JOIN sys_user_dept_rel ud ON d.user_id = ud.user_id WHERE -- 此处填写自定义条件 【至少一个条件,如果没有自定义条件则写1 = 1】 -- 数据范围过滤 ${params.dataScope} ``` 在需要实现数据过滤的方法上标注如下注解 ```java @DataScope(userDeptRelAlias = "ud", userAlias = "d") ``` - userDeptRelAlias:用户和部门的关联表别名对应上边sql例子的`sys_user_dept_rel ud` 中的ud - userAlias: 用户别名其实就是实体类表的别名,对应上边sql例子的 `sys_dict d` 中的d ## 签名校验 > component-signature 阿里云api接口调用方式:[调用方式 - 云安全中心 - 阿里云 (aliyun.com)](https://help.aliyun.com/document_detail/109177.html) 签名,通常用于参数签名,防止参数被非法篡改,最常见的是修改金额等重要敏感参数,sign的值一般是将所有非空参数按照升续排序然后+token+key+ts+nonce(随机数)拼接在一起,然后使用某种加密算法进行加密,这种方式的好处就是,当被劫持后,修改其中的参数值,然后再继续调用接口,虽然参数的值被修改了,但是因为攻击者并不清楚sign是如何计算出来的,所以即可是篡改参数的值,但没法修改sign的值,当服务器调用接口前会按照sign的规则重新计算出sign的值然后和接口传递的sign参数的值做比较,如果相等表示参数值没有被篡改,如果不等,表示参数被非法篡改了,则不会返回真实的响应信息。 如果是h5页面,不法分子很容易知道是如何生成签名的,因此需要动态获取秘钥key , 然后进行加密传输 默认判断接收到的时间戳 - 当前时间戳 > 120 , 也就是说2分钟后,链接失效 要求请求头中带有一下参数: - token: 令牌 - ts: 10位时间戳 (必填) - nonce: 8位随机数,数字+字母(必填) - sign: 签名 (必填,如果不使用签名,可以随便填写,只是不能为空) 签名格式 data(字典升序) + token + key(随机生成的秘钥) + ts(当前时间戳timestamp) + nonce(随机数) 思考一个问题: 如果是app可以通过加密固化处理,但是前端h5该如何处理呢,如果将参与生成签名的key,放入到页面中,会导致任意一个人访问网站后都可以按F12查看源代码,就知道key了,同时也知道生成签名的流程,因此中间者可以修改参数后自己再重新生成签名。 解决办法: 前端在调用接口前随机生成一个字符串,然后通过rsa公钥进行加密处理,将加密结果传输到后端,后端存入到redis缓存中,失效时间设置为30s具体工作流程: 1. 前端随机生成一个字符串,然后通过rsa公钥进行加密,将加密结果传输到后端,后端使用rsa私钥进行 解密,将结果存入到缓存中,失效时间为30s 2. 前端将 将所有非空参数按照升续排序然后 / body(请求体) + token + key + ts(当前时间戳) + nonce 进行拼接 md5加密 第一步 设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序), 使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA 或者 body(请求体)为字符串stringA。 特别注意以下重要规则: - 参数名ASCII码从小到大排序(字典序); - 如果参数的值为空不参与签名; - 参数名区分大小写 第二步 - 在stringA最后拼接上 + token + key + ts(当前时间戳) + nonce 得到stringSignTemp字符串, - 并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 第三步 - 将 token , sign , ts , nonce 放入到请求头中访问后端 simplifydev-test-rest: 测试rest接口 # nginx配置 ```text location /api/ { #$http_origin动态获取请求客户端请求的域 # 这个必须有,否则跨域失败 add_header 'Access-Control-Allow-Origin' $http_origin; # 可选,允许客户端的请求方法 add_header 'Access-Control-Allow-Methods' '*'; # 可选,允许客户端提交Cookie add_header 'Access-Control-Allow-Credentials' 'true'; # 可选,允许客户端访问的响应头 add_header 'Access-Control-Expose-Headers' 'api-format-tag,Cache-Control,Content-Disposition, Content-Language,level,Content-Type,Range, Expires, Last-Modified, Pragma,sign,ts,token,nonce,level,key'; # 处理预检请求 if ($request_method = 'OPTIONS') { # 必须有,指定允许跨域的方法,*代表所有 add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' '*'; # 必须有,允许客户端提交的的请求头 add_header 'Access-Control-Allow-Headers' 'api-format-tag,Origin,level,x-requested-with,Range, Content-Type, Accept, Authorization,sign,ts,token,nonce,level,key'; # 预检请求缓存时间 add_header 'Access-Control-Max-Age' 1728000; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } # SpringBoot 应用访问路径 proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host:$server_port; #这一行就是来解决问题的,将NGINX接收到请求头中的Host和端口继续往下传递 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 6000; proxy_read_timeout 6000; } ``` # 对象存储(oss的nginx代理) 如果配置使能nginx代理,则获取持久链接的前缀,就是url中的lan或者wan eg:http://127.0.0.1:9090/files/bucketName/objctName nginx相关配置如下,这个配置相当访问minio文件格式的地址转为访问oss格式的地址 配置中使用 .* 表示0个或多个任意字段,用 () 括起来,可以在location中用$1、$2等获取。 注意,使用这种方式,location内部不能包含if语句,否则proxy_pass不会生效 浏览器访问http://127.0.0.1:9090/files/ai-approve-bucket/2021/07/17/t ```tex location ~ /files/(.*?)/(.*)$ { proxy_pass https://$1.oss-cn-beijing.aliyuncs.com/$2; } ``` 如果关掉了nginx代理, 则{@link UrlTypes} 不生效,获取到的持久链接格式如下 eg: https://bucketName.oss-cn-beijing.aliyuncs.com/objectName # 实体类自动转换(mapstruct) 项目中集成了mapstruct 实体类自动转换功能 你需要在需要运行主程序的modules中pom.xml中引入如下插件 比如项目中有job、system等module, 但是这两个module是不需要单独运行的, 而是被聚合到module-start中,只要运行 module-start即可,因此这时,你需要在module-start中引入如下依赖(事实上单体应用是将所有模块都聚合到start中,因此module-start是必须引入的,但是如果是微服务项目,则在每个module中都需要引入) ```xml org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} ${java.version} ${java.version} org.mapstruct mapstruct-processor ${mapstruct.version} org.projectlombok lombok ${lombok.version} ``` 只有你需要通过maven编译整个工程或者单独编译具体的模块才能生成实现类, 只启动不编译是不会生成实现类的 > 如果需要转换的实体类发生改变, 则需要重新编译一下 # 前端 ## 上下文路径设置 上下文路径就是访问的根路径, 默认打包到网关中的前端上下文路径是 system_admin, 因此如果你部署完网关之后, 后端管理页面访问的路径应该是`http://网关ip:网关镜像对外开放的前端端口/system_admin` 部署时候, 如何修改上下文路径呢? 1. 修改`.env.production` 中的 `VUE_APP_CONTEXT_PATH` 注意末尾不能有斜杠 2. 修改 `deploy/docker/value/mod-test.yaml`中的 `ui.system_admin.context_path`