🎉 Initial commit.

This commit is contained in:
smallchill 2018-12-24 11:58:45 +08:00
commit f173f25535
206 changed files with 22720 additions and 0 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.java]
indent_style = tab
[*.{json,yml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# maven #
target
logs
# windows #
Thumbs.db
# Mac #
.DS_Store
# eclipse #
.settings
.project
.classpath
.log
*.class
# idea #
.idea
*.iml
# Package Files #
*.jar
*.war
*.ear
/target

64
LICENSE Normal file
View File

@ -0,0 +1,64 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License.
“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.
An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.
A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”.
The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.
The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:
a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:
a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license document.
c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.
e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

59
README.md Normal file
View File

@ -0,0 +1,59 @@
## 简介
blade-tool 是如梦技术团队作品, 是一个基于 Spring Boot 2 & Spring Cloud Finchley 封装组合大量组件用于快速构建中大型API、RESTful API项目的核心包。
注意事项
* 注册中心为 Consul
* 基于 SpringBoot2.x 版本 以及 SpringCloud Finchley 版本
技术选型&文档
* Spring Boot[查看Spring Boot学习&使用指南](http://www.jianshu.com/p/1a9fd8936bd8)
* Spring Cloud[查看Spring Cloud学习&使用指南](https://springcloud.cc/)
* Mybatis-Plus[查看官方文档](https://baomidou.gitee.io/mybatis-plus-doc/#/quick-start)
* JsonWebToken[查看官方文档](https://jwt.io/)
## 鸣谢
* 如梦技术([DreamLu](https://www.dreamlu.net/)
* pigx[Pig Microservice](https://www.pig4cloud.com/zh-cn/)
* avue[avue](https://avue.top/)
* gitee.ltd[gitee.ltd](https://gitee.ltd/)
* 鲸宵(<a href="https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/jx.png" target="_blank">鲸宵</a>
## 开源协议
LGPL[GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html)
LGPL是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。LGPL允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。
但是如果修改LGPL协议的代码或者衍生则所有修改的代码涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用但不适合希望以LGPL协议代码为基础通过修改和衍生的方式做二次开发的商业软件采用。
## 用户权益
* 允许以引入不改源码的形式用于学习、毕设、公司项目、私活等
* 特殊情况修改代码,但仍然想闭源需经过作者同意
## 禁止事项
* 直接将本项目挂淘宝等商业平台出售。
* 非界面代码60%以上相识度的二次开源,二次开源需先联系本人。
注意若禁止条款被发现有权追讨19999的授权费。
## 业务系统开源协议为Apache License界面一览即将开源敬请期待
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-k8s.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-traefik.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-traefik-health.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-harbor.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-consul.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-consul-nodes1.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-consul-nodes2.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-admin1.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-admin2.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-swagger1.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/springblade-swagger2.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu-edit.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-menu-icon.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-role.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-user.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-dict.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-locale-cn.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-locale-us.png "业务系统")
![业务系统](https://raw.githubusercontent.com/chillzhuang/blade-tool/master/pic/sword-log.png "业务系统")

122
blade-core-boot/pom.xml Normal file
View File

@ -0,0 +1,122 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-boot</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Spring-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<artifactId>tomcat-jdbc</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- Blade -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-log</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-swagger</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.bootstrapui.version}</version>
</dependency>
<!--MyBatis-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-mybatis</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>
<!-- Ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.5</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- PostgreSql -->
<!--<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>-->
</dependencies>
</project>

View File

@ -0,0 +1,75 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.tool.constant.SystemConstant;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Slf4j
@Configuration
@EnableConfigurationProperties({
BladeProperties.class
})
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@AllArgsConstructor
public class BladeBootAutoConfiguration {
private BladeProperties bladeProperties;
/**
* 全局变量定义
*/
@Bean
public SystemConstant fileConst() {
SystemConstant me = SystemConstant.me();
//设定开发模式
me.setDevMode((bladeProperties.getEnv().equals("dev") ? true : false));
//设定文件上传远程地址
me.setDomain(bladeProperties.get("upload-domain", "http://localhost:8888"));
//设定文件上传是否为远程模式
me.setRemoteMode(bladeProperties.getBoolean("remote-mode", true));
//远程上传地址
me.setRemotePath(bladeProperties.get("remote-path", System.getProperty("user.dir") + "/work/blade"));
//设定文件上传头文件夹
me.setUploadPath(bladeProperties.get("upload-path", "/upload"));
//设定文件下载头文件夹
me.setDownloadPath(bladeProperties.get("download-path", "/download"));
//设定上传图片是否压缩
me.setCompress(bladeProperties.getBoolean("compress", false));
//设定上传图片压缩比例
me.setCompressScale(bladeProperties.getDouble("compress-scale", 2.00));
//设定上传图片缩放选择:true放大;false缩小
me.setCompressFlag(bladeProperties.getBoolean("compress-flag", false));
return me;
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.config;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.feign.BladeFeignRequestHeaderInterceptor;
import org.springblade.core.boot.feign.FeignHystrixConcurrencyStrategy;
import org.springblade.core.boot.resolver.TokenArgumentResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* WEB配置
*/
@Slf4j
@Configuration
@EnableCaching
@Order(Ordered.HIGHEST_PRECEDENCE)
public class BladeWebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new TokenArgumentResolver());
}
@Bean
@ConditionalOnMissingBean
public RequestInterceptor requestInterceptor() {
return new BladeFeignRequestHeaderInterceptor();
}
@Bean
public FeignHystrixConcurrencyStrategy feignHystrixConcurrencyStrategy() {
return new FeignHystrixConcurrencyStrategy();
}
}

View File

@ -0,0 +1,55 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springblade.core.mp.BladeMetaObjectHandler;
import org.springblade.core.launch.constant.AppConstant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* mybatisplus 配置
*/
@Configuration
@MapperScan("org.springblade.**.mapper.**")
public class MybatisPlusConfiguration {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Bean
public MetaObjectHandler metaObjectHandler() {
return new BladeMetaObjectHandler();
}
/**
* SQL执行效率插件
*/
@Bean
@Profile({AppConstant.DEV_CDOE, AppConstant.TEST_CODE})
public PerformanceInterceptor performanceInterceptor() {
return new PerformanceInterceptor();
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
/**
* 重试机制
*/
@Slf4j
@Configuration
public class RetryConfiguration {
@Bean
@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
public RetryOperationsInterceptor configServerRetryInterceptor() {
log.info(String.format(
"configServerRetryInterceptor: Changing backOffOptions " +
"to initial: %s, multiplier: %s, maxInterval: %s",
1000, 1.2, 5000));
return RetryInterceptorBuilder
.stateless()
.backOffOptions(1000, 1.2, 5000)
.maxAttempts(10)
.build();
}
}

View File

@ -0,0 +1,209 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.ctrl;
import org.springblade.core.boot.file.BladeFile;
import org.springblade.core.boot.file.BladeFileUtil;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.api.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Blade控制器封装类
*/
public class BladeController {
/**
* ============================ BINDER =================================================
*/
@InitBinder
protected void initBinder(ServletRequestDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
/**
* ============================ REQUEST =================================================
*/
@Autowired
private HttpServletRequest request;
/**
* 获取request
*/
public HttpServletRequest getRequest() {
return this.request;
}
/**
* 获取当前用户
*
* @return
*/
public BladeUser getUser() {
return SecureUtil.getUser(request);
}
/** ============================ API_RESULT ================================================= */
/**
* 返回ApiResult
*
* @param data
* @return R
*/
public <T> R<T> data(T data) {
return R.data(data);
}
/**
* 返回ApiResult
*
* @param data
* @param message
* @return R
*/
public <T> R<T> data(T data, String message) {
return R.data(data, message);
}
/**
* 返回ApiResult
*
* @param data
* @param message
* @param code
* @return R
*/
public <T> R<T> data(T data, String message, int code) {
return R.data(code, data, message);
}
/**
* 返回ApiResult
*
* @param message
* @return R
*/
public R success(String message) {
return R.success(message);
}
/**
* 返回ApiResult
*
* @param message
* @return R
*/
public R failure(String message) {
return R.failure(message);
}
/**
* 返回ApiResult
*
* @param flag
* @return R
*/
public R status(boolean flag) {
return R.status(flag);
}
/**============================ FILE ================================================= */
/**
* 获取BladeFile封装类
*
* @param file
* @return
*/
public BladeFile getFile(MultipartFile file) {
return BladeFileUtil.getFile(file);
}
/**
* 获取BladeFile封装类
*
* @param file
* @param dir
* @return
*/
public BladeFile getFile(MultipartFile file, String dir) {
return BladeFileUtil.getFile(file, dir);
}
/**
* 获取BladeFile封装类
*
* @param file
* @param dir
* @param path
* @param virtualPath
* @return
*/
public BladeFile getFile(MultipartFile file, String dir, String path, String virtualPath) {
return BladeFileUtil.getFile(file, dir, path, virtualPath);
}
/**
* 获取BladeFile封装类
*
* @param files
* @return
*/
public List<BladeFile> getFiles(List<MultipartFile> files) {
return BladeFileUtil.getFiles(files);
}
/**
* 获取BladeFile封装类
*
* @param files
* @param dir
* @return
*/
public List<BladeFile> getFiles(List<MultipartFile> files, String dir) {
return BladeFileUtil.getFiles(files, dir);
}
/**
* 获取BladeFile封装类
*
* @param files
* @param path
* @param virtualPath
* @return
*/
public List<BladeFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath) {
return BladeFileUtil.getFiles(files, dir, path, virtualPath);
}
}

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/**
* feign 传递Request header
*/
@Slf4j
public class BladeFeignRequestHeaderInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
HttpServletRequest request = attrs.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
/**
* 遍历请求头里面的属性字段将Authorization添加到新的请求头中转发到下游服务
* */
if ("Authorization".equals(name)) {
log.debug("添加自定义请求头key:" + name + ",value:" + value);
requestTemplate.header(name, value);
}
}
} else {
log.warn("FeignHeadConfiguration", "获取请求头失败!");
}
}
}
}

View File

@ -0,0 +1,131 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.feign;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 自定义Feign的隔离策略
*/
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class);
private HystrixConcurrencyStrategy delegate;
public FeignHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
+ metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}

View File

@ -0,0 +1,171 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import org.springblade.core.tool.constant.SystemConstant;
import org.springblade.core.tool.date.DateUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Date;
public class BladeFile {
/**
* 上传文件在附件表中的id
*/
private Object fileId;
/**
* 上传文件
*/
private MultipartFile file;
/**
* 上传分类文件夹
*/
private String dir;
/**
* 上传物理路径
*/
private String uploadPath;
/**
* 上传虚拟路径
*/
private String uploadVirtualPath;
/**
* 文件名
*/
private String fileName;
/**
* 真实文件名
*/
private String originalFileName;
public BladeFile() {
}
public BladeFile(MultipartFile file, String dir) {
this.dir = dir;
this.file = file;
this.fileName = file.getName();
this.originalFileName = file.getOriginalFilename();
this.uploadPath = BladeFileUtil.formatUrl(File.separator + SystemConstant.me().getUploadRealPath() + File.separator + dir + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator + this.originalFileName);
this.uploadVirtualPath = BladeFileUtil.formatUrl(SystemConstant.me().getUploadCtxPath().replace(SystemConstant.me().getContextPath(), "") + File.separator + dir + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator + this.originalFileName);
}
public BladeFile(MultipartFile file, String dir, String uploadPath, String uploadVirtualPath) {
this(file, dir);
if (null != uploadPath){
this.uploadPath = BladeFileUtil.formatUrl(uploadPath);
this.uploadVirtualPath = BladeFileUtil.formatUrl(uploadVirtualPath);
}
}
/**
* 图片上传
*/
public void transfer() {
transfer(SystemConstant.me().isCompress());
}
/**
* 图片上传
* @param compress 是否压缩
*/
public void transfer(boolean compress) {
IFileProxy fileFactory = FileProxyManager.me().getDefaultFileProxyFactory();
this.transfer(fileFactory, compress);
}
/**
* 图片上传
* @param fileFactory 文件上传工厂类
* @param compress 是否压缩
*/
public void transfer(IFileProxy fileFactory, boolean compress) {
try {
File file = new File(uploadPath);
if(null != fileFactory){
String [] path = fileFactory.path(file, dir);
this.uploadPath = path[0];
this.uploadVirtualPath = path[1];
file = fileFactory.rename(file, path[0]);
}
File pfile = file.getParentFile();
if (!pfile.exists()) {
pfile.mkdirs();
}
this.file.transferTo(file);
if (compress) {
fileFactory.compress(this.uploadPath);
}
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
}
public MultipartFile getFile() {
return file;
}
public void setFile(MultipartFile file) {
this.file = file;
}
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
public String getUploadVirtualPath() {
return uploadVirtualPath;
}
public void setUploadVirtualPath(String uploadVirtualPath) {
this.uploadVirtualPath = uploadVirtualPath;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getOriginalFileName() {
return originalFileName;
}
public void setOriginalFileName(String originalFileName) {
this.originalFileName = originalFileName;
}
}

View File

@ -0,0 +1,91 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import org.springblade.core.tool.constant.SystemConstant;
import org.springblade.core.tool.date.DateUtil;
import org.springblade.core.tool.utils.ImageUtil;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;
public class BladeFileProxyFactory implements IFileProxy {
@Override
public File rename(File f, String path) {
File dest = new File(path);
f.renameTo(dest);
return dest;
}
@Override
public String [] path(File f, String dir) {
//避免网络延迟导致时间不同步
long time = System.nanoTime();
StringBuilder uploadPath = new StringBuilder()
.append(getFileDir(dir, SystemConstant.me().getUploadRealPath()))
.append(time)
.append(getFileExt(f.getName()));
StringBuilder virtualPath = new StringBuilder()
.append(getFileDir(dir, SystemConstant.me().getUploadCtxPath()))
.append(time)
.append(getFileExt(f.getName()));
return new String [] {BladeFileUtil.formatUrl(uploadPath.toString()), BladeFileUtil.formatUrl(virtualPath.toString())};
}
/**
* 获取文件后缀
*/
public static String getFileExt(String fileName) {
if (fileName.indexOf(".") == -1)
return ".jpg";
else
return fileName.substring(fileName.lastIndexOf('.'), fileName.length());
}
/**
* 获取文件保存地址
* @param saveDir
* @return
*/
public static String getFileDir(String dir, String saveDir) {
StringBuilder newFileDir = new StringBuilder();
newFileDir.append(saveDir)
.append(File.separator).append(dir).append(File.separator).append(DateUtil.format(new Date(), "yyyyMMdd"))
.append(File.separator);
return newFileDir.toString();
}
/**
* 图片压缩
* @param path 文件地址
* @return
*/
public void compress(String path) {
try {
ImageUtil.zoomScale(ImageUtil.readImage(path), new FileOutputStream(new File(path)), null, SystemConstant.me().getCompressScale(), SystemConstant.me().isCompressFlag());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,200 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
public class BladeFileUtil {
// 定义允许上传的文件扩展名
private static HashMap<String, String> extMap = new HashMap<String, String>();
// 图片扩展名
private static String[] fileTypes = new String[] { "gif", "jpg", "jpeg", "png", "bmp" };
static {
extMap.put("image", ".gif,.jpg,.jpeg,.png,.bmp,.JPG,.JPEG,.PNG");
extMap.put("flash", ".swf,.flv");
extMap.put("media", ".swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb");
extMap.put("file", ".doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
extMap.put("allfile", ".gif,.jpg,.jpeg,.png,.bmp,.swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb,.doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
}
/**
* 获取文件后缀
*
* @param @param fileName
* @param @return 设定文件
* @return String 返回类型
*/
public static String getFileExt(String fileName) {
return fileName.substring(fileName.lastIndexOf('.'), fileName.length());
}
/**
* 测试文件后缀 只让指定后缀的文件上传像jsp,war,sh等危险的后缀禁止
*
* @return
*/
public static boolean testExt(String dir, String fileName) {
String fileExt = getFileExt(fileName);
String ext = extMap.get(dir);
if (StringUtil.isBlank(ext) || ext.indexOf(fileExt) == -1) {
return false;
}
return true;
}
/**
* 文件管理排序
*/
public enum FileSort {
size, type, name;
// 文本排序转换成枚举
public static FileSort of(String sort) {
try {
return FileSort.valueOf(sort);
} catch (Exception e) {
return FileSort.name;
}
}
}
public static class NameComparator implements Comparator {
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) {
return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) {
return 1;
} else {
return ((String) hashA.get("filename")).compareTo((String) hashB.get("filename"));
}
}
}
public static class SizeComparator implements Comparator {
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) {
return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) {
return 1;
} else {
if (((Long) hashA.get("filesize")) > ((Long) hashB.get("filesize"))) {
return 1;
} else if (((Long) hashA.get("filesize")) < ((Long) hashB.get("filesize"))) {
return -1;
} else {
return 0;
}
}
}
}
public static class TypeComparator implements Comparator {
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get("is_dir")) && !((Boolean) hashB.get("is_dir"))) {
return -1;
} else if (!((Boolean) hashA.get("is_dir")) && ((Boolean) hashB.get("is_dir"))) {
return 1;
} else {
return ((String) hashA.get("filetype")).compareTo((String) hashB.get("filetype"));
}
}
}
public static String formatUrl(String url) {
return url.replaceAll("\\\\", "/");
}
/********************************BladeFile封装********************************************************/
/**
* 获取BladeFile封装类
* @param file
* @return
*/
public static BladeFile getFile(MultipartFile file){
return getFile(file, "image", null, null);
}
/**
* 获取BladeFile封装类
* @param file
* @param dir
* @return
*/
public static BladeFile getFile(MultipartFile file, String dir){
return getFile(file, dir, null, null);
}
/**
* 获取BladeFile封装类
* @param file
* @param dir
* @param path
* @param virtualPath
* @return
*/
public static BladeFile getFile(MultipartFile file, String dir, String path, String virtualPath){
return new BladeFile(file, dir, path, virtualPath);
}
/**
* 获取BladeFile封装类
* @param files
* @return
*/
public static List<BladeFile> getFiles(List<MultipartFile> files){
return getFiles(files, "image", null, null);
}
/**
* 获取BladeFile封装类
* @param files
* @param dir
* @return
*/
public static List<BladeFile> getFiles(List<MultipartFile> files, String dir){
return getFiles(files, dir, null, null);
}
/**
* 获取BladeFile封装类
* @param files
* @param path
* @param virtualPath
* @return
*/
public static List<BladeFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath){
List<BladeFile> list = new ArrayList<>();
for (MultipartFile file : files){
list.add(new BladeFile(file, dir, path, virtualPath));
}
return list;
}
}

View File

@ -0,0 +1,209 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.StringPool;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
public class FileMaker {
private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
private File file;
private String fileName;
private HttpServletRequest request;
private HttpServletResponse response;
public static FileMaker init(HttpServletRequest request, HttpServletResponse response, File file) {
return new FileMaker(request, response, file);
}
public static FileMaker init(HttpServletRequest request, HttpServletResponse response, File file, String fileName) {
return new FileMaker(request, response, file, fileName);
}
private FileMaker(HttpServletRequest request, HttpServletResponse response, File file) {
if (file == null) {
throw new IllegalArgumentException("file can not be null.");
}
this.file = file;
this.request = request;
this.response = response;
this.fileName = file.getName();
}
private FileMaker(HttpServletRequest request, HttpServletResponse response, File file, String fileName) {
if (file == null) {
throw new IllegalArgumentException("file can not be null.");
}
this.file = file;
this.request = request;
this.response = response;
this.fileName = fileName;
}
public void start() {
if (file == null || !file.isFile()) {
throw new RuntimeException();
}
// ---------
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Content-disposition", "attachment; filename=" + encodeFileName(fileName));
response.setContentType(DEFAULT_CONTENT_TYPE);
// ---------
if (StringUtil.isBlank(request.getHeader("Range")))
normalStart();
else
rangeStart();
}
private String encodeFileName(String fileName) {
try {
return new String(fileName.getBytes(StringPool.GBK), StringPool.ISO_8859_1);
} catch (UnsupportedEncodingException e) {
return fileName;
}
}
private void normalStart() {
response.setHeader("Content-Length", String.valueOf(file.length()));
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(new FileInputStream(file));
outputStream = response.getOutputStream();
byte[] buffer = new byte[1024];
for (int len = -1; (len = inputStream.read(buffer)) != -1;) {
outputStream.write(buffer, 0, len);
}
outputStream.flush();
}
catch (IOException e) {
throw new RuntimeException(e);
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally {
if (inputStream != null)
try {inputStream.close();} catch (IOException e) {}
if (outputStream != null)
try {outputStream.close();} catch (IOException e) {}
}
}
private void rangeStart() {
Long[] range = {null, null};
processRange(range);
String contentLength = String.valueOf(range[1].longValue() - range[0].longValue() + 1);
response.setHeader("Content-Length", contentLength);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // status = 206
// Content-Range: bytes 0-499/10000
StringBuilder contentRange = new StringBuilder("bytes ").append(String.valueOf(range[0])).append("-").append(String.valueOf(range[1])).append("/").append(String.valueOf(file.length()));
response.setHeader("Content-Range", contentRange.toString());
InputStream inputStream = null;
OutputStream outputStream = null;
try {
long start = range[0];
long end = range[1];
inputStream = new BufferedInputStream(new FileInputStream(file));
if (inputStream.skip(start) != start)
throw new RuntimeException("File skip error");
outputStream = response.getOutputStream();
byte[] buffer = new byte[1024];
long position = start;
for (int len; position <= end && (len = inputStream.read(buffer)) != -1;) {
if (position + len <= end) {
outputStream.write(buffer, 0, len);
position += len;
}
else {
for (int i=0; i<len && position <= end; i++) {
outputStream.write(buffer[i]);
position++;
}
}
}
outputStream.flush();
}
catch (IOException e) {
throw new RuntimeException(e);
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally {
if (inputStream != null)
try {inputStream.close();} catch (IOException e) {}
if (outputStream != null)
try {outputStream.close();} catch (IOException e) {}
}
}
/**
* Examples of byte-ranges-specifier values (assuming an entity-body of length 10000):
* The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-499
* The second 500 bytes (byte offsets 500-999, inclusive): bytes=500-999
* The final 500 bytes (byte offsets 9500-9999, inclusive): bytes=-500
* Or bytes=9500-
*/
private void processRange(Long[] range) {
String rangeStr = request.getHeader("Range");
int index = rangeStr.indexOf(',');
if (index != -1)
rangeStr = rangeStr.substring(0, index);
rangeStr = rangeStr.replace("bytes=", "");
String[] arr = rangeStr.split("-", 2);
if (arr.length < 2)
throw new RuntimeException("Range error");
long fileLength = file.length();
for (int i=0; i<range.length; i++) {
if (StringUtil.isNotBlank(arr[i])) {
range[i] = Long.parseLong(arr[i].trim());
if (range[i] >= fileLength)
range[i] = fileLength - 1;
}
}
// Range format like: 9500-
if (range[0] != null && range[1] == null) {
range[1] = fileLength - 1;
}
// Range format like: -500
else if (range[0] == null && range[1] != null) {
range[0] = fileLength - range[1];
range[1] = fileLength - 1;
}
// check final range
if (range[0] == null || range[1] == null || range[0].longValue() > range[1].longValue())
throw new RuntimeException("Range error");
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import java.io.File;
public class FileProxyManager {
private IFileProxy defaultFileProxyFactory = new BladeFileProxyFactory();
private static FileProxyManager me = new FileProxyManager();
public static FileProxyManager me() {
return me;
}
public IFileProxy getDefaultFileProxyFactory() {
return defaultFileProxyFactory;
}
public void setDefaultFileProxyFactory(IFileProxy defaultFileProxyFactory) {
this.defaultFileProxyFactory = defaultFileProxyFactory;
}
public String[] path(File file, String dir) {
return defaultFileProxyFactory.path(file, dir);
}
public File rename(File file, String path) {
return defaultFileProxyFactory.rename(file, path);
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.file;
import java.io.File;
public interface IFileProxy {
/**
* 返回路径[物理路径][虚拟路径]
* @param file
* @param dir
* @return
*/
String [] path(File file, String dir);
/**
* 文件重命名策略
* @param file
* @param path
* @return
*/
File rename(File file, String path);
/**
* 图片压缩
*/
void compress(String path);
}

View File

@ -0,0 +1,132 @@
package org.springblade.core.boot.logger;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* Spring boot 控制器 请求日志方便代码调试
*
* @author L.cm
*/
@Slf4j
@Aspect
@Configuration
@Profile({"dev", "test"})
public class RequestLogAspect {
/**
* AOP 环切 控制器 R 返回值
* @param point JoinPoint
* @throws Throwable 异常
* @return Object
*/
@Around(
"execution(!static org.springblade.core.tool.api.R<*> *(..)) && " +
"(@within(org.springframework.stereotype.Controller) || " +
"@within(org.springframework.web.bind.annotation.RestController))"
)
public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Object[] args = point.getArgs();
// 请求参数处理
final Map<String, Object> paraMap = new HashMap<>(16);
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// PathVariable 参数跳过
PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class);
if (pathVariable != null) {
continue;
}
RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
Object object = args[i];
// 如果是body的json则是对象
if (requestBody != null && object != null) {
paraMap.putAll(BeanUtil.toMap(object));
} else {
// 参数名
RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class);
String paraName;
if (requestParam != null && StringUtil.isNotBlank(requestParam.value())) {
paraName = requestParam.value();
} else {
paraName = methodParam.getParameterName();
}
paraMap.put(paraName, object);
}
}
HttpServletRequest request = WebUtil.getRequest();
String requestURI = request.getRequestURI();
String requestMethod = request.getMethod();
// 处理 参数
List<String> needRemoveKeys = new ArrayList<>(paraMap.size());
paraMap.forEach((key, value) -> {
if (value instanceof HttpServletRequest) {
needRemoveKeys.add(key);
paraMap.putAll(((HttpServletRequest) value).getParameterMap());
} else if (value instanceof HttpServletResponse) {
needRemoveKeys.add(key);
} else if (value instanceof InputStream) {
needRemoveKeys.add(key);
} else if (value instanceof MultipartFile) {
String fileName = ((MultipartFile) value).getOriginalFilename();
paraMap.put(key, fileName);
} else if (value instanceof InputStreamSource) {
needRemoveKeys.add(key);
} else if (value instanceof WebRequest) {
needRemoveKeys.add(key);
paraMap.putAll(((WebRequest) value).getParameterMap());
}
});
needRemoveKeys.forEach(paraMap::remove);
// 打印请求
if (paraMap.isEmpty()) {
log.info("===> {}: {}", requestMethod, requestURI);
} else {
log.info("===> {}: {} Parameters: {}", requestMethod, requestURI, JsonUtil.toJson(paraMap));
}
// 打印请求头
Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String headerName = headers.nextElement();
String headerValue = request.getHeader(headerName);
log.info("===headers=== {} : {}", headerName, headerValue);
}
// 打印执行时间
long startNs = System.nanoTime();
try {
Object result = point.proceed();
log.info("===Result=== {}", JsonUtil.toJson(result));
return result;
} finally {
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
log.info("<=== {}: {} ({} ms)", request.getMethod(), requestURI, tookMs);
}
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.node;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 节点基类
*
* @author zhuangqian
*/
@Data
public class BaseNode implements INode {
protected Integer id;//主键ID
protected Integer parentId;//父节点ID
protected List<INode> children = new ArrayList<>();//子孙节点
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 森林节点类
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ForestNode extends BaseNode {
private Object content;//节点内容
public ForestNode(Integer id, Integer parentId, Object content) {
this.id = id;
this.parentId = parentId;
this.content = content;
}
}

View File

@ -0,0 +1,62 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.node;
import java.util.ArrayList;
import java.util.List;
/**
* 森林管理类
*
* @author zhuangqian
*/
public class ForestNodeManager {
private List<INode> list;// 森林的所有节点
public ForestNodeManager(List<INode> items) {
list = items;
}
/**
* 根据节点ID获取一个节点
*
* @param id 节点ID
* @return 对应的节点对象
*/
public INode getTreeNodeAT(int id) {
for (INode forestNode : list) {
if (forestNode.getId() == id)
return forestNode;
}
return null;
}
/**
* 获取树的根节点(一个森林对应多颗树)
*
* @return 树的根节点集合
*/
public List<INode> getRoot() {
List<INode> roots = new ArrayList<>();
for (INode forestNode : list) {
if (forestNode.getParentId() == 0)
roots.add(forestNode);
}
return roots;
}
}

View File

@ -0,0 +1,45 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.node;
import java.util.List;
/**
* 森林节点归并类
*
* @author zhuangqian
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林多棵树填充节点的children域
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static List<INode> merge(List<INode> items) {
ForestNodeManager forestNodeManager = new ForestNodeManager(items);
for (INode forestNode : items) {
if (forestNode.getParentId() != 0) {
INode node = forestNodeManager.getTreeNodeAT(forestNode.getParentId());
node.getChildren().add(forestNode);
}
}
return forestNodeManager.getRoot();
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.node;
import java.util.List;
/**
* Created by Blade.
*
* @author zhuangqian
*/
public interface INode {
Integer getId();
Integer getParentId();
List<INode> getChildren();
}

View File

@ -0,0 +1,34 @@
package org.springblade.core.boot.node;
import org.springblade.core.tool.jackson.JsonUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Blade.
*
* @author zhuangqian
*/
public class NodeTest {
public static void main(String[] args) {
List<INode> list = new ArrayList<>();
list.add(new ForestNode(1, 0, "1"));
list.add(new ForestNode(2, 0, "2"));
list.add(new ForestNode(3, 1, "3"));
list.add(new ForestNode(4, 2, "4"));
list.add(new ForestNode(5, 3, "5"));
list.add(new ForestNode(6, 4, "6"));
list.add(new ForestNode(7, 3, "7"));
list.add(new ForestNode(8, 5, "8"));
list.add(new ForestNode(9, 6, "9"));
list.add(new ForestNode(10, 9, "10"));
List<INode> tns = ForestNodeMerger.merge(list);
tns.forEach(node -> {
ForestNode n = (ForestNode) node;
System.out.println(JsonUtil.toJson(n));
});
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.boot.resolver;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.SecureUtil;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
/**
* Token转化BladeUser
*/
@Slf4j
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 1. 入参筛选
*
* @param methodParameter 参数集合
* @return 格式化后的参数
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(BladeUser.class);
}
/**
* @param methodParameter 入参集合
* @param modelAndViewContainer model view
* @param nativeWebRequest web相关
* @param webDataBinderFactory 入参解析
* @return 包装对象
* @throws Exception exception
*/
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
return SecureUtil.getUser(request);
}
}

View File

@ -0,0 +1 @@
restart.include.blade-core-boot=/blade-core-boot[\\w-]+\.jar

View File

@ -0,0 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springblade.core.boot.config.MybatisPlusConfiguration,\
org.springblade.core.boot.logger.RequestLogAspect,\
org.springblade.core.boot.config.RetryConfiguration,\
org.springblade.core.boot.config.BladeWebMvcConfiguration,\
org.springblade.core.boot.config.BladeBootAutoConfiguration

View File

@ -0,0 +1,10 @@
${AnsiColor.BRIGHT_CYAN} _____ _ ${AnsiColor.BLUE} ______ _ _
${AnsiColor.BRIGHT_CYAN}/ ___| (_) ${AnsiColor.BLUE} | ___ \| | | |
${AnsiColor.BRIGHT_CYAN}\ `--. _ __ _ __ _ _ __ __ _ ${AnsiColor.BLUE} | |_/ /| | __ _ __| | ___
${AnsiColor.BRIGHT_CYAN} `--. \| '_ \ | '__|| || '_ \ / _` | ${AnsiColor.BLUE} | ___ \| | / _` | / _` | / _ \
${AnsiColor.BRIGHT_CYAN}/\__/ /| |_) || | | || | | || (_| | ${AnsiColor.BLUE} | |_/ /| || (_| || (_| || __/
${AnsiColor.BRIGHT_CYAN}\____/ | .__/ |_| |_||_| |_| \__, | ${AnsiColor.BLUE} \____/ |_| \__,_| \__,_| \___|
${AnsiColor.BRIGHT_CYAN} | | __/ |
${AnsiColor.BRIGHT_CYAN} |_| |___/
${AnsiColor.BLUE}:: SpringBlade :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}

View File

@ -0,0 +1,87 @@
#服务器配置
server:
undertow:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io-threads: 4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker-threads: 20
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
#spring配置
spring:
cloud:
config:
label: master
profile: ${blade.env}
fail-fast: true
discovery:
enabled: true
service-id: blade-config-server
cache:
ehcache:
config: classpath:config/ehcache.xml
http:
encoding:
charset: UTF-8
force: true
servlet:
multipart:
max-file-size: 256MB
max-request-size: 1024MB
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
devtools:
restart:
log-condition-evaluation-delta: false
#配置日志地址
logging:
config: classpath:log/logback_${blade.env}.xml
# mybatis
mybatis-plus:
mapper-locations: classpath:org/springblade/**/mapper/*Mapper.xml
#实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: org.springblade.**.model
#typeEnumsPackage: org.springblade.dashboard.entity.enums
global-config:
#刷新mapper 调试神器
refresh: ${blade.dev-mode}
# 关闭MP3.0自带的banner
banner: false
db-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
column-underline: true
# 逻辑删除配置
# 逻辑删除全局值1表示已删除这也是Mybatis Plus的默认配置
logic-delete-value: 1
# 逻辑未删除全局值0表示未删除这也是Mybatis Plus的默认配置
logic-not-delete-value: 0
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
#swagger公共信息
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 1.0.0
license: Powered By SpringBlade
licenseUrl: https://springblade.org
terms-of-service-url: https://springblade.org
contact:
name: smallchill
email: smallchill@163.com
url: https://gitee.com/smallc

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="java.io.tmpdir"/>
<cache name="RETRY_LIMIT_CACHE"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true">
</cache>
<!-- 缓存半小时 -->
<cache name="HALF_HOUR"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
diskPersistent="false" />
<!-- 缓存一小时 -->
<cache name="HOUR"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false"
diskPersistent="false" />
<!-- 缓存一天 -->
<cache name="ONE_DAY"
maxElementsInMemory="10000"
maxElementsOnDisk="100000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="false"
diskPersistent="false" />
<!--
name:缓存名称。
maxElementsInMemory缓存最大个数。
eternal:对象是否永久有效一但设置了timeout将不起作用。
timeToIdleSeconds设置对象在失效前的允许闲置时间单位。仅当eternal=false对象不是永久有效时使用可选属性默认值是0也就是可闲置时间无穷大。
timeToLiveSeconds设置对象在失效前允许存活时间单位。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用默认是0.,也就是对象存活时间无穷大。
overflowToDisk当内存中对象数量达到maxElementsInMemory时Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB这个参数设置DiskStore磁盘缓存的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk硬盘最大缓存个数。
diskPersistent是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds磁盘失效线程运行时间间隔默认是120秒。
memoryStoreEvictionPolicy当达到maxElementsInMemory限制时Ehcache将会根据指定的策略去清理内存。默认策略是LRU最近最少使用。你可以设置为FIFO先进先出或是LFU较少使用
clearOnFlush内存数量最大时是否清除。
-->
<defaultCache name="DEFAULT_CACHE"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -->
<configuration scan="true" scanPeriod="60 seconds">
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
<!-- MyBatis log configure -->
<logger name="com.apache.ibatis" level="INFO"/>
<logger name="org.mybatis.spring" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 减少部分debug日志 -->
<logger name="druid.sql" level="INFO"/>
<logger name="org.apache.shiro" level="INFO"/>
<logger name="org.mybatis.spring" level="INFO"/>
<logger name="org.springframework" level="INFO"/>
<logger name="org.springframework.context" level="WARN"/>
<logger name="org.springframework.beans" level="WARN"/>
<logger name="com.baomidou.mybatisplus" level="INFO"/>
<logger name="org.apache.ibatis.io" level="INFO"/>
<logger name="org.apache.velocity" level="INFO"/>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.undertow" level="INFO"/>
<logger name="org.xnio.nio" level="INFO"/>
<logger name="org.thymeleaf" level="INFO"/>
<logger name="springfox.documentation" level="INFO"/>
<logger name="org.hibernate.validator" level="INFO"/>
<logger name="com.netflix.loadbalancer" level="INFO"/>
<logger name="com.netflix.hystrix" level="INFO"/>
<logger name="com.netflix.zuul" level="INFO"/>
<logger name="de.codecentric" level="INFO"/>
<!-- cache INFO -->
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="org.springframework.cache" level="INFO"/>
<!-- cloud -->
<logger name="org.apache.http" level="INFO"/>
<logger name="com.netflix.discovery" level="INFO"/>
<logger name="com.netflix.eureka" level="INFO"/>
<!-- 业务日志 -->
<Logger name="org.springblade" level="DEBUG" />
<Logger name="org.springblade.core.version" level="INFO"/>
</configuration>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -->
<configuration scan="true" scanPeriod="60 seconds">
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 生成日志文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/info-%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志文件保留天数 -->
<MaxHistory>365</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 日志文件最大的大小 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>500MB</MaxFileSize>
</triggeringPolicy>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 生成日志文件 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/error-%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志文件保留天数 -->
<MaxHistory>365</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 日志文件最大的大小 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>500MB</MaxFileSize>
</triggeringPolicy>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="INFO" />
<appender-ref ref="ERROR" />
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
</configuration>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* -->
<configuration scan="true" scanPeriod="60 seconds">
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 生成日志文件 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/info-%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志文件保留天数 -->
<MaxHistory>365</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 日志文件最大的大小 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>500MB</MaxFileSize>
</triggeringPolicy>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 生成日志文件 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件输出的文件名 -->
<FileNamePattern>target/blade/log/error-%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- 日志文件保留天数 -->
<MaxHistory>365</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%n%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{50}] %n%-5level: %msg%n</pattern>
</encoder>
<!-- 日志文件最大的大小 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>500MB</MaxFileSize>
</triggeringPolicy>
<!-- 打印日志级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="INFO" />
<appender-ref ref="ERROR" />
</root>
<logger name="net.sf.ehcache" level="INFO"/>
<logger name="druid.sql" level="INFO"/>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

36
blade-core-launch/pom.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-launch</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Spring-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,109 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.*;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.function.Function;
/**
* 项目启动器搞定环境变量问题
*/
public class BladeApplication {
/**
* Create an application context
*
* @param appName application name
* @param source The sources
* @param args args the command line arguments
* @return an application context created from the current state
* @run java -jar app.jar --spring.profiles.active=prod --server.port=2333
*/
public static ConfigurableApplicationContext run(String appName, Class source, String... args) {
SpringApplicationBuilder builder = createSpringApplicationBuilder(appName, source, args);
return builder.run(args);
}
private static SpringApplicationBuilder createSpringApplicationBuilder(String appName, Class source, String... args) {
Assert.hasText(appName, "[appName]服务名不能为空");
// 读取环境变量使用spring boot的规则
ConfigurableEnvironment environment = new StandardEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.addFirst(new SimpleCommandLinePropertySource(args));
propertySources.addLast(new MapPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, environment.getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, environment.getSystemEnvironment()));
// 获取配置的环境变量
String[] activeProfiles = environment.getActiveProfiles();
// 判断环境:devtestprod
List<String> profiles = Arrays.asList(activeProfiles);
// 预设的环境
List<String> presetProfiles = new ArrayList<>(Arrays.asList(AppConstant.DEV_CDOE, AppConstant.TEST_CODE, AppConstant.PROD_CODE));
// 交集
presetProfiles.retainAll(profiles);
// 当前使用
List<String> activeProfileList = new ArrayList<>(profiles);
Function<Object[], String> joinFun = StringUtils::arrayToCommaDelimitedString;
SpringApplicationBuilder builder = new SpringApplicationBuilder(source);
String profile;
if (activeProfileList.isEmpty()) {
// 默认dev开发
profile = AppConstant.DEV_CDOE;
activeProfileList.add(profile);
builder.profiles(profile);
} else if (activeProfileList.size() == 1) {
profile = activeProfileList.get(0);
} else {
// 同时存在devtestprod环境时
throw new RuntimeException("同时存在环境变量:[" + StringUtils.arrayToCommaDelimitedString(activeProfiles) + "]");
}
String startJarPath = BladeApplication.class.getResource("/").getPath().split("!")[0];
String activePros = joinFun.apply(activeProfileList.toArray());
System.out.println(String.format("----启动中,读取到的环境变量:[%s]jar地址:[%s]----", activePros, startJarPath));
Properties props = System.getProperties();
props.setProperty("spring.application.name", appName);
props.setProperty("spring.profiles.active", profile);
props.setProperty("info.version", AppConstant.APPLICATION_VERSION);
props.setProperty("info.desc", appName);
props.setProperty("blade.env", profile);
props.setProperty("blade.name", appName);
props.setProperty("blade.is-local", String.valueOf(isLocalDev()));
props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true");
props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION);
// 加载自定义组件
ServiceLoader<LauncherService> loader = ServiceLoader.load(LauncherService.class);
loader.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
return builder;
}
/**
* 判断是否为本地开发环境
*
* @return boolean
*/
private static boolean isLocalDev() {
String osName = System.getProperty("os.name");
return StringUtils.hasText(osName) && !(AppConstant.OS_NAME_LINUX.equals(osName.toUpperCase()));
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
/**
* 系统启动完毕后执行
*/
@Component
public class BladeLineRunner implements CommandLineRunner {
@Override
public void run(String... args) {
}
}

View File

@ -0,0 +1,29 @@
package org.springblade.core.launch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;
/**
* 项目启动事件通知
*/
@Slf4j
@Configuration
public class StartEventListener {
@Async
@Order
@EventListener(WebServerInitializedEvent.class)
public void afterStart(WebServerInitializedEvent event) {
Environment environment = event.getApplicationContext().getEnvironment();
String appName = environment.getProperty("spring.application.name").toUpperCase();
int localPort = event.getWebServer().getPort();
String profile = StringUtils.arrayToCommaDelimitedString(environment.getActiveProfiles());
log.info("---[{}]---启动完成,当前使用的端口:[{}],环境变量:[{}]---", appName, localPort, profile);
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.config;
import com.ecwid.consul.v1.ConsulClient;
import org.springblade.core.launch.consul.BladeConsulServiceRegistry;
import org.springblade.core.launch.server.ServerInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cloud.consul.ConditionalOnConsulEnabled;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.discovery.HeartbeatProperties;
import org.springframework.cloud.consul.discovery.TtlScheduler;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistryAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Consul自定义注册规则
*/
@Configuration
@ConditionalOnConsulEnabled
@AutoConfigureBefore(ConsulServiceRegistryAutoConfiguration.class)
public class BladeConsulServiceRegistryConfiguration {
@Autowired(required = false)
private TtlScheduler ttlScheduler;
@Autowired
private ServerInfo serverInfo;
@Bean
public ConsulServiceRegistry consulServiceRegistry(ConsulClient consulClient, ConsulDiscoveryProperties properties,
HeartbeatProperties heartbeatProperties) {
return new BladeConsulServiceRegistry(consulClient, properties, ttlScheduler, heartbeatProperties, serverInfo);
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.config;
import lombok.AllArgsConstructor;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Configuration
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableConfigurationProperties({
BladeProperties.class
})
public class BladeLaunchConfiguration {
private ServerProperties serverProperties;
private InetUtils inetUtils;
/**
* 服务器信息
*/
@Bean
public ServerInfo serverInfo() {
return new ServerInfo(serverProperties, inetUtils);
}
}

View File

@ -0,0 +1,146 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.constant;
/**
* 系统常量
*/
public interface AppConstant {
/**
* 应用版本
*/
String APPLICATION_VERSION = "1.0.0";
/**
* consul dev 地址
*/
String CONSUL_DEV_HOST = "http://localhost";
/**
* consul prod 地址
*/
String CONSUL_PROD_HOST = "http://192.168.186.129";
/**
* consul端口
*/
String CONSUL_PORT = "8500";
/**
* consul端口
*/
String CONSUL_CONFIG_FORMAT = "yaml";
/**
* consul端口
*/
String CONSUL_WATCH_DELAY = "1000";
/**
* consul端口
*/
String CONSUL_WATCH_ENABLED = "true";
/**
* 基础包
*/
String BASE_PACKAGES = "org.springblade";
/**
* zookeeper id
*/
String ZOOKEEPER_ID = "zk";
/**
* zookeeper connect string
*/
String ZOOKEEPER_CONNECT_STRING = "127.0.0.1:2181";
/**
* zookeeper address
*/
String ZOOKEEPER_ADDRESS = "zookeeper://" + ZOOKEEPER_CONNECT_STRING;
/**
* zookeeper root
*/
String ZOOKEEPER_ROOT = "/blade-services";
/**
* 应用名前缀
*/
String APPLICATION_NAME_FREFIX = "blade-";
/**
* 网关模块名称
*/
String APPLICATION_GATEWAY_NAME = APPLICATION_NAME_FREFIX + "gateway";
/**
* 授权模块名称
*/
String APPLICATION_AUTH_NAME = APPLICATION_NAME_FREFIX + "auth";
/**
* 监控模块名称
*/
String APPLICATION_ADMIN_NAME = APPLICATION_NAME_FREFIX + "admin";
/**
* 配置中心模块名称
*/
String APPLICATION_CONFIG_NAME = APPLICATION_NAME_FREFIX + "config-server";
/**
* TX模块名称
*/
String APPLICATION_TX_MANAGER = "tx-manager";
/**
* 首页模块名称
*/
String APPLICATION_DESK_NAME = APPLICATION_NAME_FREFIX + "desk";
/**
* 系统模块名称
*/
String APPLICATION_SYSTEM_NAME = APPLICATION_NAME_FREFIX + "system";
/**
* 用户模块名称
*/
String APPLICATION_USER_NAME = APPLICATION_NAME_FREFIX + "user";
/**
* 日志模块名称
*/
String APPLICATION_LOG_NAME = APPLICATION_NAME_FREFIX + "log";
/**
* 测试模块名称
*/
String APPLICATION_TEST_NAME = APPLICATION_NAME_FREFIX + "test";
/**
* 开发环境
*/
String DEV_CDOE = "dev";
/**
* 生产环境
*/
String PROD_CODE = "prod";
/**
* 测试环境
*/
String TEST_CODE = "test";
/**
* 代码部署于 linux 工作默认为 mac Windows
*/
String OS_NAME_LINUX = "LINUX";
}

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.consul;
import com.ecwid.consul.v1.ConsulClient;
import org.springblade.core.launch.server.ServerInfo;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.discovery.HeartbeatProperties;
import org.springframework.cloud.consul.discovery.TtlScheduler;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;
/**
* Consul自定义注册规则
*/
public class BladeConsulServiceRegistry extends ConsulServiceRegistry {
private ServerInfo serverInfo;
public BladeConsulServiceRegistry(ConsulClient client, ConsulDiscoveryProperties properties, TtlScheduler ttlScheduler, HeartbeatProperties heartbeatProperties, ServerInfo serverInfo) {
super(client, properties, ttlScheduler, heartbeatProperties);
this.serverInfo = serverInfo;
}
@Override
public void register(ConsulRegistration reg) {
reg.getService().setId(reg.getService().getName() + "-" + serverInfo.getIP() + "-" + serverInfo.getPort());
super.register(reg);
}
}

View File

@ -0,0 +1,24 @@
package org.springblade.core.launch.consul;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import java.util.Properties;
/**
* consul启动拓展
*/
public class ConsulLauncherService implements LauncherService {
@Override
public void launcher(SpringApplicationBuilder builder, String appName, String profile) {
Properties props = System.getProperties();
props.setProperty("spring.cloud.consul.host", profile.equals(AppConstant.DEV_CDOE) ? AppConstant.CONSUL_DEV_HOST : AppConstant.CONSUL_PROD_HOST);
props.setProperty("spring.cloud.consul.port", AppConstant.CONSUL_PORT);
props.setProperty("spring.cloud.consul.config.format", AppConstant.CONSUL_CONFIG_FORMAT);
props.setProperty("spring.cloud.consul.watch.delay", AppConstant.CONSUL_WATCH_DELAY);
props.setProperty("spring.cloud.consul.watch.enabled", AppConstant.CONSUL_WATCH_ENABLED);
}
}

View File

@ -0,0 +1,210 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.props;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.lang.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* 配置文件
*/
@ConfigurationProperties("blade")
public class BladeProperties {
/**
* 开发环境
*/
@Getter
@Setter
private String env;
/**
* 服务名
*/
@Getter
@Setter
private String name;
/**
* 判断是否为 本地开发环境
*/
@Getter
@Setter
private Boolean isLocal = Boolean.FALSE;
/**
* 装载自定义配置blade.prop.xxx
*/
@Getter
private final Map<String, String> prop = new HashMap<>();
/**
* 获取配置
*
* @param key key
* @return value
*/
@Nullable
public String get(String key) {
return get(key, null);
}
/**
* 获取配置
*
* @param key key
* @param defaultValue 默认值
* @return value
*/
@Nullable
public String get(String key, @Nullable String defaultValue) {
String value = prop.get(key);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* 获取配置
*
* @param key key
* @return int value
*/
@Nullable
public Integer getInt(String key) {
return getInt(key, null);
}
/**
* 获取配置
*
* @param key key
* @param defaultValue 默认值
* @return int value
*/
@Nullable
public Integer getInt(String key, @Nullable Integer defaultValue) {
String value = prop.get(key);
if (value != null) {
return Integer.valueOf(value.trim());
}
return defaultValue;
}
/**
* 获取配置
*
* @param key key
* @return long value
*/
@Nullable
public Long getLong(String key) {
return getLong(key, null);
}
/**
* 获取配置
*
* @param key key
* @param defaultValue 默认值
* @return long value
*/
@Nullable
public Long getLong(String key, @Nullable Long defaultValue) {
String value = prop.get(key);
if (value != null) {
return Long.valueOf(value.trim());
}
return defaultValue;
}
/**
* 获取配置
*
* @param key key
* @return Boolean value
*/
@Nullable
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
/**
* 获取配置
*
* @param key key
* @param defaultValue 默认值
* @return Boolean value
*/
@Nullable
public Boolean getBoolean(String key, @Nullable Boolean defaultValue) {
String value = prop.get(key);
if (value != null) {
value = value.toLowerCase().trim();
if ("true".equals(value)) {
return Boolean.TRUE;
} else if ("false".equals(value)) {
return Boolean.FALSE;
}
throw new RuntimeException("The value can not parse to Boolean : " + value);
}
return defaultValue;
}
/**
* 获取配置
*
* @param key key
* @return double value
*/
@Nullable
public Double getDouble(String key) {
return getDouble(key, null);
}
/**
* 获取配置
*
* @param key key
* @param defaultValue 默认值
* @return double value
*/
@Nullable
public Double getDouble(String key, @Nullable Double defaultValue) {
String value = prop.get(key);
if (value != null) {
return Double.parseDouble(value.trim());
}
return defaultValue;
}
/**
* 判断是否存在key
*
* @param key prop key
* @return boolean
*/
public boolean containsKey(String key) {
return prop.containsKey(key);
}
}

View File

@ -0,0 +1,62 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.server;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.commons.util.InetUtils;
/**
* 服务器信息
*/
public class ServerInfo {
private ServerProperties serverProperties;
private InetUtils inetUtils;
private String hostName;
private String ip;
private Integer prot;
private String ipWithPort;
public ServerInfo(ServerProperties serverProperties, InetUtils inetUtils) {
this.serverProperties = serverProperties;
this.inetUtils = inetUtils;
this.hostName = getHostInfo().getHostname();
this.ip = getHostInfo().getIpAddress();
this.prot = serverProperties.getPort();
this.ipWithPort = String.format("%s:%d", ip, prot);
}
public InetUtils.HostInfo getHostInfo() {
return inetUtils.findFirstNonLoopbackHostInfo();
}
public String getIP() {
return this.ip;
}
public Integer getPort() {
return this.prot;
}
public String getHostName() {
return this.hostName;
}
public String getIPWithPort() {
return this.ipWithPort;
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.launch.service;
import org.springframework.boot.builder.SpringApplicationBuilder;
/**
* launcher 扩展 用于一些组件发现
*/
public interface LauncherService {
/**
* 启动时 处理 SpringApplicationBuilder
* @param builder SpringApplicationBuilder
* @param appName SpringApplicationAppName
* @param profile SpringApplicationProfile
*/
void launcher(SpringApplicationBuilder builder, String appName, String profile);
}

View File

@ -0,0 +1 @@
org.springblade.core.launch.consul.ConsulLauncherService

View File

@ -0,0 +1,37 @@
{
"hints": [],
"groups": [
{
"sourceType": "org.springblade.core.launch.props.BladeProperties",
"name": "blade",
"type": "org.springblade.core.launch.props.BladeProperties"
}
],
"properties": [
{
"sourceType": "org.springblade.core.launch.props.BladeProperties",
"name": "blade.env",
"description": "开发环境",
"type": "java.lang.String"
},
{
"sourceType": "org.springblade.core.launch.props.BladeProperties",
"defaultValue": false,
"name": "blade.is-local",
"description": "判断是否为 本地开发环境",
"type": "java.lang.Boolean"
},
{
"sourceType": "org.springblade.core.launch.props.BladeProperties",
"name": "blade.name",
"description": "服务名",
"type": "java.lang.String"
},
{
"sourceType": "org.springblade.core.launch.props.BladeProperties",
"name": "blade.prop",
"description": "装载自定义配置blade.prop.xxx",
"type": "java.util.Map<java.lang.String,java.lang.String>"
}
]
}

View File

@ -0,0 +1 @@
restart.include.blade-core-launch=/blade-core-launch[\\w-]+\.jar

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springblade.core.launch.config.BladeLaunchConfiguration,\
org.springblade.core.launch.StartEventListener,\
org.springblade.core.launch.config.BladeConsulServiceRegistryConfiguration

43
blade-core-log/pom.xml Normal file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-log</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!--Feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.annotation;
import java.lang.annotation.*;
/**
* 操作日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiLog {
/**
* 日志描述
*
* @return {String}
*/
String value() default "日志记录";
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.log.publisher.ApiLogPublisher;
/**
* 操作日志使用spring event异步入库
*/
@Slf4j
@Aspect
public class ApiLogAspect {
@Around("@annotation(apiLog)")
public Object around(ProceedingJoinPoint point, ApiLog apiLog) throws Throwable {
//获取类名
String className = point.getTarget().getClass().getName();
//获取方法
String methodName = point.getSignature().getName();
// 发送异步日志事件
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//记录日志
ApiLogPublisher.publishEvent(methodName, className, apiLog, time);
return result;
}
}

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.config;
import lombok.AllArgsConstructor;
import org.springblade.core.log.error.BladeErrorAttributes;
import org.springblade.core.log.error.BladeErrorController;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.Servlet;
/**
* 统一异常处理
*/
@Configuration
@AllArgsConstructor
@ConditionalOnWebApplication
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
public class BladeErrorMvcAutoConfiguration {
private final ServerProperties serverProperties;
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new BladeErrorAttributes();
}
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
return new BladeErrorController(errorAttributes, serverProperties.getError());
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.config;
import lombok.AllArgsConstructor;
import org.springblade.core.log.aspect.ApiLogAspect;
import org.springblade.core.log.event.ApiLogListener;
import org.springblade.core.log.event.BladeLogListener;
import org.springblade.core.log.event.ErrorLogListener;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.feign.ILogClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 日志工具自动配置
*/
@Configuration
@AllArgsConstructor
@ConditionalOnWebApplication
public class BladeLogToolAutoConfiguration {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Bean
public ApiLogAspect apiLogAspect() {
return new ApiLogAspect();
}
@Bean
public BladeLogger bladeLogger() {
return new BladeLogger();
}
@Bean
public ApiLogListener apiLogListener() {
return new ApiLogListener(logService, serverInfo, bladeProperties);
}
@Bean
public ErrorLogListener errorEventListener() {
return new ErrorLogListener(logService, serverInfo, bladeProperties);
}
@Bean
public BladeLogListener bladeEventListener() {
return new BladeLogListener(logService, serverInfo, bladeProperties);
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.constant;
/**
* 事件常量
*/
public interface EventConstant {
/**
* log
*/
String EVENT_LOG = "log";
/**
* request
*/
String EVENT_REQUEST = "request";
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.error;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.publisher.ErrorLogPublisher;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.BeanUtil;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
/**
* 全局异常处理
*/
@Slf4j
public class BladeErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
String requestUri = this.getAttr(webRequest, "javax.servlet.error.request_uri");
Integer status = this.getAttr(webRequest, "javax.servlet.error.status_code");
Throwable error = getError(webRequest);
R result;
if (error == null) {
log.error("URL:{} error status:{}", requestUri, status);
result = R.failure(ResultCode.FAILURE, "系统未知异常[HttpStatus]:" + status);
} else {
log.error(String.format("URL:%s error status:%d", requestUri, status), error);
result = R.failure(status, error.getMessage());
}
//发送服务异常事件
ErrorLogPublisher.publishEvent(error, requestUri);
return BeanUtil.toMap(result);
}
@Nullable
private <T> T getAttr(WebRequest webRequest, String name) {
return (T) webRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.error;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* 更改html请求异常为ajax
*/
public class BladeErrorController extends BasicErrorController {
public BladeErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
super(errorAttributes, errorProperties);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
response.setStatus(status.value());
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setObjectMapper(JsonUtil.getInstance());
view.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
return new ModelAndView(view, body);
}
}

View File

@ -0,0 +1,160 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.error;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.log.publisher.ErrorLogPublisher;
import org.springblade.core.secure.exception.SecureException;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.URLUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.Servlet;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;
/**
* 全局异常处理处理可预见的异常
*/
@Slf4j
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@RestControllerAdvice
public class BladeRestExceptionTranslator {
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MissingServletRequestParameterException e) {
log.warn("缺少请求参数", e.getMessage());
String message = String.format("缺少必要的请求参数: %s", e.getParameterName());
return R.failure(ResultCode.PARAM_MISS, message);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MethodArgumentTypeMismatchException e) {
log.warn("请求参数格式错误", e.getMessage());
String message = String.format("请求参数格式错误: %s", e.getName());
return R.failure(ResultCode.PARAM_TYPE_ERROR, message);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(MethodArgumentNotValidException e) {
log.warn("参数验证失败", e.getMessage());
return handleError(e.getBindingResult());
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(BindException e) {
log.warn("参数绑定失败", e.getMessage());
return handleError(e.getBindingResult());
}
private R handleError(BindingResult result) {
FieldError error = result.getFieldError();
String message = String.format("%s:%s", error.getField(), error.getDefaultMessage());
return R.failure(ResultCode.PARAM_BIND_ERROR, message);
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(ConstraintViolationException e) {
log.warn("参数验证失败", e.getMessage());
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
String path = ((PathImpl) violation.getPropertyPath()).getLeafNode().getName();
String message = String.format("%s:%s", path, violation.getMessage());
return R.failure(ResultCode.PARAM_VALID_ERROR, message);
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public R handleError(NoHandlerFoundException e) {
log.error("404没找到请求:{}", e.getMessage());
return R.failure(ResultCode.NOT_FOUND, e.getMessage());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(HttpMessageNotReadableException e) {
log.error("消息不能读取:{}", e.getMessage());
return R.failure(ResultCode.MSG_NOT_READABLE, e.getMessage());
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public R handleError(HttpRequestMethodNotSupportedException e) {
log.error("不支持当前请求方法:{}", e.getMessage());
return R.failure(ResultCode.METHOD_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
public R handleError(HttpMediaTypeNotSupportedException e) {
log.error("不支持当前媒体类型:{}", e.getMessage());
return R.failure(ResultCode.MEDIA_TYPE_NOT_SUPPORTED, e.getMessage());
}
@ExceptionHandler(ServiceException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleError(ServiceException e) {
log.error("业务异常", e);
return R.failure(e.getResultCode(), e.getMessage());
}
@ExceptionHandler(SecureException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public R handleError(SecureException e) {
log.error("认证异常", e);
return R.failure(e.getResultCode(), e.getMessage());
}
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleError(Throwable e) {
log.error("服务器异常", e);
//发送服务异常事件
ErrorLogPublisher.publishEvent(e, URLUtil.getPath(WebUtil.getRequest().getRequestURI()));
return R.failure(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage()));
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 系统日志事件
*/
public class ApiLogEvent extends ApplicationEvent {
public ApiLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@ -0,0 +1,73 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.tool.utils.URLUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogApi;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 异步监听日志事件
*/
@Slf4j
@Component
@AllArgsConstructor
public class ApiLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ApiLogEvent.class)
public void saveApiLog(ApiLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogApi logApi = (LogApi) source.get(EventConstant.EVENT_LOG);
HttpServletRequest request = (HttpServletRequest) source.get(EventConstant.EVENT_REQUEST);
logApi.setServiceId(bladeProperties.getName());
logApi.setServerHost(serverInfo.getHostName());
logApi.setServerIp(serverInfo.getIPWithPort());
logApi.setEnv(bladeProperties.getEnv());
logApi.setRemoteIp(WebUtil.getIP(request));
logApi.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER));
logApi.setRequestUri(URLUtil.getPath(request.getRequestURI()));
logApi.setMethod(request.getMethod());
logApi.setParams(WebUtil.getRequestParamString(request));
logApi.setCreateBy(SecureUtil.getUser(request).getAccount());
logApi.setCreateTime(LocalDateTime.now());
logService.saveApiLog(logApi);
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 系统日志事件
*/
public class BladeLogEvent extends ApplicationEvent {
public BladeLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogBlade;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.utils.URLUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 异步监听日志事件
*/
@Slf4j
@Component
@AllArgsConstructor
public class BladeLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(BladeLogEvent.class)
public void saveBladeLog(BladeLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogBlade logBlade = (LogBlade) source.get(EventConstant.EVENT_LOG);
HttpServletRequest request = (HttpServletRequest) source.get(EventConstant.EVENT_REQUEST);
logBlade.setRequestUri(URLUtil.getPath(request.getRequestURI()));
logBlade.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER));
logBlade.setMethod(request.getMethod());
logBlade.setParams(WebUtil.getRequestParamString(request));
logBlade.setServiceId(bladeProperties.getName());
logBlade.setServerHost(serverInfo.getHostName());
logBlade.setServerIp(serverInfo.getIPWithPort());
logBlade.setEnv(bladeProperties.getEnv());
logBlade.setCreateBy(SecureUtil.getUser(request).getAccount());
logBlade.setCreateTime(LocalDateTime.now());
logService.saveBladeLog(logBlade);
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import org.springframework.context.ApplicationEvent;
import java.util.Map;
/**
* 错误日志事件
*/
public class ErrorLogEvent extends ApplicationEvent {
public ErrorLogEvent(Map<String, Object> source) {
super(source);
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.event;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.launch.server.ServerInfo;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.core.log.feign.ILogClient;
import org.springblade.core.log.model.LogError;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Map;
/**
* 异步监听错误日志事件
*/
@Slf4j
@Component
@AllArgsConstructor
public class ErrorLogListener {
private final ILogClient logService;
private final ServerInfo serverInfo;
private final BladeProperties bladeProperties;
@Async
@Order
@EventListener(ErrorLogEvent.class)
public void saveErrorLog(ErrorLogEvent event) {
Map<String, Object> source = (Map<String, Object>) event.getSource();
LogError logError = (LogError) source.get(EventConstant.EVENT_LOG);
HttpServletRequest request = (HttpServletRequest) source.get(EventConstant.EVENT_REQUEST);
logError.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER));
logError.setMethod(request.getMethod());
logError.setParams(WebUtil.getRequestParamString(request));
logError.setServiceId(bladeProperties.getName());
logError.setServerHost(serverInfo.getHostName());
logError.setServerIp(serverInfo.getIPWithPort());
logError.setEnv(bladeProperties.getEnv());
logError.setCreateBy(SecureUtil.getUser(request).getAccount());
logError.setCreateTime(LocalDateTime.now());
logService.saveErrorLog(logError);
}
}

View File

@ -0,0 +1,60 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.exception;
import lombok.Getter;
import org.springblade.core.tool.api.IResultCode;
import org.springblade.core.tool.api.ResultCode;
/**
* 业务异常
*/
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 2359767895161832954L;
@Getter
private final IResultCode resultCode;
public ServiceException(String message) {
super(message);
this.resultCode = ResultCode.INTERNAL_SERVER_ERROR;
}
public ServiceException(IResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public ServiceException(IResultCode resultCode, Throwable cause) {
super(cause);
this.resultCode = resultCode;
}
/**
* 提高性能
* @return Throwable
*/
@Override
public Throwable fillInStackTrace() {
return this;
}
public Throwable doFillInStackTrace() {
return super.fillInStackTrace();
}
}

View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.feign;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.log.model.LogApi;
import org.springblade.core.log.model.LogBlade;
import org.springblade.core.log.model.LogError;
import org.springblade.core.tool.api.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* Feign接口类
*/
@FeignClient(
value = AppConstant.APPLICATION_LOG_NAME
)
public interface ILogClient {
String API_PREFIX = "/log";
/**
* 保存错误日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveBladeLog")
R<Boolean> saveBladeLog(@RequestBody LogBlade log);
/**
* 保存操作日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveApiLog")
R<Boolean> saveApiLog(@RequestBody LogApi log);
/**
* 保存错误日志
*
* @param log
* @return
*/
@PostMapping(API_PREFIX + "/saveErrorLog")
R<Boolean> saveErrorLog(@RequestBody LogError log);
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.logger;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.publisher.BladeLogPublisher;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
/**
* 日志工具类
*/
@Slf4j
public class BladeLogger implements InitializingBean {
@Value("${spring.application.name}")
private String serviceId;
public void info(String id, String data) {
BladeLogPublisher.publishEvent("info", id, data);
}
public void debug(String id, String data) {
BladeLogPublisher.publishEvent("debug", id, data);
}
public void warn(String id, String data) {
BladeLogPublisher.publishEvent("warn", id, data);
}
public void error(String id, String data) {
BladeLogPublisher.publishEvent("error", id, data);
}
@Override
public void afterPropertiesSet() throws Exception {
log.info(serviceId + ": BladeLogger init success!");
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springblade.core.tool.date.DatePattern;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 实体类
*
* @author Blade
*/
@Data
@TableName("blade_log_api")
public class LogApi implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 日志类型
*/
private String type;
/**
* 日志标题
*/
private String title;
/**
* 服务ID
*/
private String serviceId;
/**
* 服务器 ip
*/
private String serverIp;
/**
* 服务器名
*/
private String serverHost;
/**
* 环境
*/
private String env;
/**
* 操作IP地址
*/
private String remoteIp;
/**
* 用户代理
*/
private String userAgent;
/**
* 请求URI
*/
private String requestUri;
/**
* 操作方式
*/
private String method;
/**
* 方法类
*/
private String methodClass;
/**
* 方法名
*/
private String methodName;
/**
* 操作提交的数据
*/
private String params;
/**
* 执行时间
*/
private String time;
/**
* 异常信息
*/
private String exception;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,103 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springblade.core.tool.date.DatePattern;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 实体类
*
* @author Blade
* @since 2018-10-12
*/
@Data
@TableName("blade_log")
public class LogBlade implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 服务ID
*/
private String serviceId;
/**
* 服务器名
*/
private String serverHost;
/**
* 服务器IP地址
*/
private String serverIp;
/**
* 系统环境
*/
private String env;
/**
* 日志级别
*/
private String logLevel;
/**
* 日志业务id
*/
private String logId;
/**
* 日志数据
*/
private String logData;
/**
* 操作方式
*/
private String method;
/**
* 请求URI
*/
private String requestUri;
/**
* 用户代理
*/
private String userAgent;
/**
* 操作提交的数据
*/
private String params;
/**
* 创建者
*/
private String createBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,118 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springblade.core.tool.date.DatePattern;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.lang.Nullable;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 服务 异常
*/
@Data
@TableName("blade_log_error")
public class LogError implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "id", type = IdType.ID_WORKER)
private Long id;
/**
* 应用名
*/
private String serviceId;
/**
* 环境
*/
private String env;
/**
* 服务器 ip
*/
private String serverIp;
/**
* 服务器名
*/
private String serverHost;
/**
* 用户代理
*/
private String userAgent;
/**
* 请求url
*/
@Nullable
private String requestUri;
/**
* 操作方式
*/
private String method;
/**
* 堆栈信息
*/
private String stackTrace;
/**
* 异常名
*/
private String exceptionName;
/**
* 异常消息
*/
private String message;
/**
* 类名
*/
private String methodClass;
/**
* 文件名
*/
private String fileName;
/**
* 方法名
*/
private String methodName;
/**
* 操作提交的数据
*/
private String params;
/**
* 代码行数
*/
private Integer lineNumber;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ApiLogEvent;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.core.log.model.LogApi;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* API日志信息事件发送
*/
public class ApiLogPublisher {
public static void publishEvent(String methodName, String methodClass, ApiLog apiLog, long time) {
HttpServletRequest request = WebUtil.getRequest();
LogApi logApi = new LogApi();
logApi.setType(BladeConstant.LOG_NORMAL_TYPE);
logApi.setTitle(apiLog.value());
logApi.setTime(String.valueOf(time));
logApi.setMethodClass(methodClass);
logApi.setMethodName(methodName);
Map<String, Object> event = new HashMap<>();
event.put(EventConstant.EVENT_LOG, logApi);
event.put(EventConstant.EVENT_REQUEST, request);
SpringUtil.publishEvent(new ApiLogEvent(event));
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.BladeLogEvent;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springblade.core.log.model.LogBlade;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* BLADE日志信息事件发送
*/
public class BladeLogPublisher {
public static void publishEvent(String level, String id, String data) {
HttpServletRequest request = WebUtil.getRequest();
LogBlade logBlade = new LogBlade();
logBlade.setLogLevel(level);
logBlade.setLogId(id);
logBlade.setLogData(data);
Map<String, Object> event = new HashMap<>();
event.put(EventConstant.EVENT_LOG, logBlade);
event.put(EventConstant.EVENT_REQUEST, request);
SpringUtil.publishEvent(new BladeLogEvent(event));
}
}

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.log.publisher;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ErrorLogEvent;
import org.springblade.core.tool.utils.*;
import org.springblade.core.log.model.LogError;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 异常信息事件发送
*/
public class ErrorLogPublisher {
public static void publishEvent(Throwable error, String requestUri) {
HttpServletRequest request = WebUtil.getRequest();
LogError logError = new LogError();
logError.setRequestUri(requestUri);
if (Func.isNotEmpty(error)) {
logError.setStackTrace(Exceptions.getStackTraceAsString(error));
logError.setExceptionName(error.getClass().getName());
logError.setMessage(error.getMessage());
StackTraceElement[] elements = error.getStackTrace();
if (Func.isNotEmpty(elements)) {
StackTraceElement element = elements[0];
logError.setMethodName(element.getMethodName());
logError.setMethodClass(element.getClassName());
logError.setFileName(element.getFileName());
logError.setLineNumber(element.getLineNumber());
}
}
Map<String, Object> event = new HashMap<>();
event.put(EventConstant.EVENT_LOG, logError);
event.put(EventConstant.EVENT_REQUEST, request);
SpringUtil.publishEvent(new ErrorLogEvent(event));
}
}

View File

@ -0,0 +1 @@
restart.include.blade-core-log=/blade-core-log[\\w-]+\.jar

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springblade.core.log.config.BladeLogToolAutoConfiguration,\
org.springblade.core.log.config.BladeErrorMvcAutoConfiguration,\
org.springblade.core.log.error.BladeRestExceptionTranslator

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-mybatis</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
<version>${blade.tool.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
/**
* mybatisplus自定义填充
*/
@Slf4j
public class BladeMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
}
@Override
public void updateFill(MetaObject metaObject) {
}
}

View File

@ -0,0 +1,78 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.base;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springblade.core.tool.date.DatePattern;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class BaseEntity implements Serializable {
/**
* 主键id
*/
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "主键id")
private Integer id;
/**
* 创建人
*/
@ApiModelProperty(value = "创建人")
private Integer createUser;
/**
* 创建时间
*/
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
/**
* 更新人
*/
@ApiModelProperty(value = "更新人")
private Integer updateUser;
/**
* 更新时间
*/
@DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
/**
* 状态[1:正常]
*/
@ApiModelProperty(value = "业务状态")
private Integer status;
/**
* 状态[0:未删除,1:删除]
*/
@ApiModelProperty(value = "是否已删除")
private Integer isDeleted;
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.base;
import com.baomidou.mybatisplus.extension.service.IService;
import javax.validation.constraints.NotEmpty;
import java.util.List;
public interface BaseService<T> extends IService<T> {
/**
* 逻辑删除
* @param ids id集合(逗号分隔)
* @return
*/
boolean deleteLogic(@NotEmpty List<Integer> ids);
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.base;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.BeanUtil;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotEmpty;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.List;
/**
* 业务封装基础类
*
* @param <M> mapper
* @param <T> model
*/
@Validated
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements BaseService<T> {
private Class<T> modelClass;
@SuppressWarnings("unchecked")
public BaseServiceImpl() {
Type type = this.getClass().getGenericSuperclass();
this.modelClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[1];
}
@Override
public boolean save(T entity) {
BladeUser user = SecureUtil.getUser();
LocalDateTime now = LocalDateTime.now();
entity.setCreateUser(user.getUserId());
entity.setCreateTime(now);
entity.setUpdateUser(user.getUserId());
entity.setUpdateTime(now);
entity.setStatus(BladeConstant.DB_STATUS_NORMAL);
entity.setIsDeleted(BladeConstant.DB_NOT_DELETED);
return super.save(entity);
}
@Override
public boolean updateById(T entity) {
BladeUser user = SecureUtil.getUser();
entity.setUpdateUser(user.getUserId());
entity.setUpdateTime(LocalDateTime.now());
return super.updateById(entity);
}
@Override
public boolean deleteLogic(@NotEmpty List<Integer> ids) {
BladeUser user = SecureUtil.getUser();
T entity = BeanUtil.newInstance(modelClass);
entity.setUpdateUser(user.getUserId());
entity.setUpdateTime(LocalDateTime.now());
entity.setIsDeleted(BladeConstant.DB_IS_DELETED);
UpdateWrapper<T> uw = new UpdateWrapper<>();
uw.in(BladeConstant.DB_PRIMARY_KEY, ids);
return super.update(entity, uw);
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.support;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.List;
import java.util.stream.Collectors;
/**
* 视图包装基类
*/
public abstract class BaseEntityWrapper<E, V> {
public abstract V entityVO(E entity);
public IPage<V> pageVO(IPage<E> pages) {
List<V> records = pages.getRecords().stream().map(this::entityVO).collect(Collectors.toList());
IPage<V> pageVo = new Page<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
pageVo.setRecords(records);
return pageVo;
}
}

View File

@ -0,0 +1,74 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.support;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.BeanUtil;
import java.util.Map;
/**
* 分页工具
*/
public class Condition {
/**
* 转化成mybatis plus中的Page
*
* @param query
* @return
*/
public static <T> IPage<T> getPage(Query query) {
Page<T> page = new Page<>(Func.toInt(query.getCurrent(), 1), Func.toInt(query.getSize(), 10));
page.setAsc(Func.toStrArray(query.getAscs()));
page.setDesc(Func.toStrArray(query.getDescs()));
return page;
}
/**
* 获取mybatis plus中的QueryWrapper
*
* @param entity
* @param <T>
* @return
*/
public static <T> QueryWrapper<T> getQueryWrapper(T entity) {
return new QueryWrapper<>(entity);
}
/**
* 获取mybatis plus中的QueryWrapper
*
* @param query
* @param clazz
* @param <T>
* @return
*/
public static <T> QueryWrapper<T> getQueryWrapper(Map<String, Object> query, Class<T> clazz) {
QueryWrapper<T> qw = new QueryWrapper<>();
qw.setEntity(BeanUtil.newInstance(clazz));
if (Func.isNotEmpty(query)) {
query.forEach((k, v) -> {
if (Func.isNotEmpty(v)) qw.like(k, v);
});
}
return qw;
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.mp.support;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 分页工具
*/
@Data
@ApiModel(description = "查询条件")
public class Query {
/**
* 当前页
*/
@ApiModelProperty(value = "当前页")
private Integer current;
/**
* 每页的数量
*/
@ApiModelProperty(value = "每页的数量")
private Integer size;
/**
* 排序的字段名
*/
@ApiModelProperty(value = "升序字段")
private String ascs;
/**
* 排序方式
*/
@ApiModelProperty(value = "降序字段")
private String descs;
}

33
blade-core-secure/pom.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-secure</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${blade.tool.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* AuthInfo
*/
@Data
@ApiModel(description = "认证信息")
public class AuthInfo {
@ApiModelProperty(value = "令牌")
private String accessToken;
@ApiModelProperty(value = "令牌类型")
private String tokenType;
@ApiModelProperty(value = "角色名")
private String authority;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "账号名")
private String account;
@ApiModelProperty(value = "过期时间")
private long expiresIn;
@ApiModelProperty(value = "许可证")
private String license = "made by blade";
}

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure;
import lombok.Data;
import java.io.Serializable;
/**
* 用户实体
*/
@Data
public class BladeUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private Integer userId;
/**
* 昵称
*/
private String userName;
/**
* 账号
*/
private String account;
/**
* 角色id
*/
private String roleId;
/**
* 角色名
*/
private String roleName;
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.annotation;
import java.lang.annotation.*;
/**
* 权限注解 用于检查权限 规定访问权限
*
* @example @PreAuth("#userVO.id<10")
* @example @PreAuth("hasRole(#test, #test1)")
* @example @PreAuth("hasPermission(#test) and @PreAuth.hasPermission(#test)")
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuth {
/**
* Spring el
* 文档地址https://docs.spring.io/spring/docs/4.3.16.RELEASE/spring-framework-reference/htmlsingle/#expressions-operators-logical
*/
String value();
}

View File

@ -0,0 +1,118 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.secure.auth.AuthFun;
import org.springblade.core.secure.exception.SecureException;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.core.MethodParameter;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
/**
* AOP 鉴权
*/
@Aspect
public class AuthAspect implements ApplicationContextAware {
/**
* 表达式处理
*/
private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser();
/**
* 方法 类上的 @PreAuth 注解
* @param point 切点
* @return Object
* @throws Throwable 没有权限的异常
*/
@Around(
"@annotation(org.springblade.core.secure.annotation.PreAuth) || " +
"@within(org.springblade.core.secure.annotation.PreAuth)"
)
public Object preAuth(ProceedingJoinPoint point) throws Throwable {
if (handleAuth(point)) {
return point.proceed();
}
throw new SecureException(ResultCode.UN_AUTHORIZED);
}
/**
* 处理权限
*
* @param point 切点
*/
private boolean handleAuth(ProceedingJoinPoint point) {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
// 读取权限注解优先方法上没有则读取类
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
// 判断表达式
String condition = preAuth.value();
if (StringUtil.isNotBlank(condition)) {
Expression expression = SPEL_PARSER.parseExpression(condition);
// 方法参数值
Object[] args = point.getArgs();
StandardEvaluationContext context = getEvaluationContext(method, args);
return expression.getValue(context, Boolean.class);
}
return false;
}
/**
* 获取方法上的参数
*
* @param method 方法
* @param args 变量
* @return {SimpleEvaluationContext}
*/
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
// 初始化Sp el表达式上下文并设置 AuthFun
StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
// 设置表达式支持spring bean
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// 设置方法 参数名和值 为sp el变量
context.setVariable(methodParam.getParameterName(), args[i]);
}
return context;
}
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@ -0,0 +1,75 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.auth;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
/**
* 权限判断
*/
public class AuthFun {
/**
* 放行所有请求
*
* @return {boolean}
*/
public boolean permitAll() {
return true;
}
/**
* 只有超管角色才可访问
*
* @return {boolean}
*/
public boolean denyAll() {
return hasRole(RoleConstant.ADMIN);
}
/**
* 判断是否有该角色权限
*
* @param role 单角色
* @return {boolean}
*/
public boolean hasRole(String role) {
return hasAnyRole(role);
}
/**
* 判断是否有该角色权限
*
* @param role 角色集合
* @return {boolean}
*/
public boolean hasAnyRole(String... role) {
String userRole = SecureUtil.getUser().getRoleName();
if (StringUtil.isBlank(userRole)) {
return false;
}
String[] roles = Func.toStrArray(userRole);
if (CollectionUtil.contains(roles, role)) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.config;
import org.springblade.core.secure.registry.SecureRegistry;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* secure模块api放行默认配置
*/
@Configuration
@AutoConfigureBefore(SecureConfiguration.class)
public class RegistryConfiguration {
@Bean
@ConditionalOnMissingBean(SecureRegistry.class)
public SecureRegistry secureRegistry() {
return new SecureRegistry();
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.config;
import lombok.AllArgsConstructor;
import org.springblade.core.secure.aspect.AuthAspect;
import org.springblade.core.secure.interceptor.SecureInterceptor;
import org.springblade.core.secure.registry.SecureRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Order
@Configuration
@AllArgsConstructor
public class SecureConfiguration implements WebMvcConfigurer {
private final SecureRegistry secureRegistry;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (secureRegistry.isEnable()) {
registry.addInterceptor(new SecureInterceptor())
.excludePathPatterns(secureRegistry.getExcludePatterns())
.excludePathPatterns(secureRegistry.getDefaultExcludePatterns());
}
}
@Bean
public AuthAspect authAspect() {
return new AuthAspect();
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.exception;
import lombok.Getter;
import org.springblade.core.tool.api.IResultCode;
import org.springblade.core.tool.api.ResultCode;
/**
* Secure异常
*/
public class SecureException extends RuntimeException {
private static final long serialVersionUID = 2359767895161832954L;
@Getter
private final IResultCode resultCode;
public SecureException(String message) {
super(message);
this.resultCode = ResultCode.INTERNAL_SERVER_ERROR;
}
public SecureException(IResultCode resultCode) {
super(resultCode.getMessage());
this.resultCode = resultCode;
}
public SecureException(IResultCode resultCode, Throwable cause) {
super(cause);
this.resultCode = resultCode;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}

View File

@ -0,0 +1,58 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.utils.SecureUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
/**
* jwt拦截器校验
*/
@Slf4j
public class SecureInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (null != SecureUtil.getUser().getUserId()) {
return true;
} else {
log.warn("签名认证失败,请求接口:{}请求IP{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
R result = R.failure(ResultCode.UN_AUTHORIZED);
response.setCharacterEncoding(StringPool.UTF_8);
response.setHeader("Content-type", MediaType.APPLICATION_JSON_UTF8_VALUE);
response.setStatus(HttpServletResponse.SC_OK);
try {
response.getWriter().write(Objects.requireNonNull(JsonUtil.toJson(result)));
} catch (IOException ex) {
log.error(ex.getMessage());
}
return false;
}
}
}

View File

@ -0,0 +1,62 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.registry;
import lombok.Data;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* secure api放行配置
*/
@Data
public class SecureRegistry {
private boolean enable = true;
private final List<String> defaultExcludePatterns = new ArrayList<>();
private final List<String> excludePatterns = new ArrayList<>();
public SecureRegistry() {
this.defaultExcludePatterns.add("/actuator/health/**");
this.defaultExcludePatterns.add("/v2/api-docs/**");
this.defaultExcludePatterns.add("/v2/api-docs-ext/**");
this.defaultExcludePatterns.add("/auth/**");
this.defaultExcludePatterns.add("/token/**");
this.defaultExcludePatterns.add("/log/**");
this.defaultExcludePatterns.add("/user/userInfo");
this.defaultExcludePatterns.add("/error/**");
}
/**
* 设置放行api
*/
public SecureRegistry excludePathPatterns(String... patterns) {
return excludePathPatterns(Arrays.asList(patterns));
}
/**
* 设置放行api
*/
public SecureRegistry excludePathPatterns(List<String> patterns) {
this.excludePatterns.addAll(patterns);
return this;
}
}

View File

@ -0,0 +1,184 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.tool.date.DateField;
import org.springblade.core.tool.date.DateTime;
import org.springblade.core.tool.date.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.Map;
/**
* Secure工具类
*/
public class SecureUtil {
public final static String header = "Authorization";
public final static String bearer = "bearer";
public final static String account = "account";
public final static String userId = "userId";
public final static String roleId = "roleId";
public final static String userName = "userName";
public final static String roleName = "roleName";
private static String base64Security = DatatypeConverter.printBase64Binary("SpringBlade".getBytes());
/**
* 获取用户信息
*
* @return
*/
public static BladeUser getUser() {
return getUser(WebUtil.getRequest());
}
/**
* 获取用户信息
*
* @return
*/
public static BladeUser getUser(HttpServletRequest request) {
Claims claims = getClaims(request);
if (claims == null) {
return new BladeUser();
}
Integer userId = Func.toInt(claims.get(SecureUtil.userId));
String roleId = Func.toStr(claims.get(SecureUtil.roleId));
String account = Func.toStr(claims.get(SecureUtil.account));
String roleName = Func.toStr(claims.get(SecureUtil.roleName));
BladeUser bladeUser = new BladeUser();
bladeUser.setAccount(account);
bladeUser.setUserId(userId);
bladeUser.setRoleId(roleId);
bladeUser.setRoleName(roleName);
return bladeUser;
}
/**
* 获取Claims
*
* @return
*/
public static Claims getClaims(HttpServletRequest request) {
String auth = request.getHeader(SecureUtil.header);
if ((auth != null) && (auth.length() > 7)) {
String HeadStr = auth.substring(0, 6).toLowerCase();
if (HeadStr.compareTo(SecureUtil.bearer) == 0) {
auth = auth.substring(7);
return SecureUtil.parseJWT(auth);
}
}
return null;
}
/**
* 获取请求头
*
* @return
*/
public static String getHeader() {
return getHeader(WebUtil.getRequest());
}
/**
* 获取请求头
*
* @param request
* @return
*/
public static String getHeader(HttpServletRequest request) {
return request.getHeader(header);
}
/**
* 解析jsonWebToken
*
* @param jsonWebToken
* @return
*/
public static Claims parseJWT(String jsonWebToken) {
try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
.parseClaimsJws(jsonWebToken).getBody();
return claims;
} catch (Exception ex) {
return null;
}
}
/**
* 创建jwt
*
* @param user 用户
* @param audience audience
* @param issuer issuer
* @param isExpire isExpire
* @return
*/
public static String createJWT(Map<String, String> user, String audience, String issuer, boolean isExpire) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//生成签名密钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//添加构成JWT的类
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken")
.setIssuer(issuer)
.setAudience(audience)
.signWith(signatureAlgorithm, signingKey);
//设置JWT参数
user.forEach(builder::claim);
//添加Token过期时间
if (isExpire) {
long expMillis = nowMillis + getExpire();
Date exp = new Date(expMillis);
builder.setExpiration(exp).setNotBefore(now);
}
//生成JWT
return builder.compact();
}
/**
* 获取过期时间(次日凌晨3点)
*
* @return
*/
public static long getExpire() {
DateTime dateTime = DateUtil.endOfDay(new Date());
DateTime offset = DateUtil.offset(dateTime, DateField.HOUR, 3);
return offset.getTime() - System.currentTimeMillis();
}
}

View File

@ -0,0 +1 @@
restart.include.blade-core-secure=/blade-core-secure[\\w-]+\.jar

View File

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springblade.core.secure.config.RegistryConfiguration,\
org.springblade.core.secure.config.SecureConfiguration

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>1.0.0-RC1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-swagger</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger.bootstrapui.version}</version>
</dependency>
</dependencies>
</project>

Some files were not shown because too many files have changed in this diff Show More