├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── README.md ├── pom.xml ├── script ├── createTable.sql └── doc.md └── src ├── main ├── java │ └── com │ │ └── lian │ │ ├── AppApplication.java │ │ ├── common │ │ ├── Constant.java │ │ ├── lock │ │ │ ├── DistributedLocker.java │ │ │ └── RedissonDistributedLocker.java │ │ ├── pubsub │ │ │ ├── RabbitReceiver.java │ │ │ └── RedisReceiver.java │ │ └── util │ │ │ ├── IpUtil.java │ │ │ ├── LogUtil.java │ │ │ └── VerifyCodeUtil.java │ │ ├── conf │ │ ├── CacheConfig.java │ │ ├── DataSourceShiroRealm.java │ │ ├── HTMLFilter.java │ │ ├── RedisConfig.java │ │ ├── SQLFilter.java │ │ ├── ShiroConfig.java │ │ ├── SwaggerConfig.java │ │ ├── XssFilter.java │ │ └── XssHttpServletRequestWrapper.java │ │ ├── domain │ │ ├── entity │ │ │ ├── OrderDoc.java │ │ │ ├── OrderEntity.java │ │ │ ├── RolesPermissionEntity.java │ │ │ ├── UserEntity.java │ │ │ └── UserRoleEntity.java │ │ ├── mongorepository │ │ │ └── OrderDocRepository.java │ │ └── repository │ │ │ ├── OrderRepository.java │ │ │ ├── RolesPermissionRepository.java │ │ │ ├── UserRepository.java │ │ │ └── UserRoleRepository.java │ │ ├── service │ │ ├── AuthService.java │ │ ├── OrderService.java │ │ ├── UserService.java │ │ └── impl │ │ │ ├── AuthServiceImpl.java │ │ │ ├── OrderServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── task │ │ ├── AsyncTask.java │ │ ├── ScheduleTask.java │ │ └── ScheduleTaskImpl.java │ │ └── web │ │ ├── DAOController.java │ │ ├── DemoController.java │ │ ├── MongoController.java │ │ ├── OrderController.java │ │ ├── RedirectController.java │ │ ├── RedisController.java │ │ ├── SecurityController.java │ │ ├── UserController.java │ │ ├── param │ │ ├── ParamFilter.java │ │ ├── ParamRequestWrapper.java │ │ └── ResponseWrapper.java │ │ └── response │ │ ├── GlobalExceptionHandler.java │ │ ├── MyException.java │ │ └── RestInfo.java └── resources │ ├── application-dev.properties │ ├── application-prod.properties │ ├── application.properties │ ├── static │ └── hi.txt │ └── templates │ └── hello.html └── test └── java └── test └── lian └── UserServiceTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | 27 | transaction-logs/ 28 | 29 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjlian/springboot-distributed-demo/d333b4f9d464649dfdda204cd2e95613bd17ab92/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # springboot-distributed-demo 2 | [以最优雅的方式示例了springboot最简单的最全集成的例子](https://github.com/sjlian/springboot-distributed-demo) 3 | 4 | [使用文档请点击此处](https://github.com/sjlian/springboot-distributed-demo/blob/master/script/doc.md) 5 | 6 | 本项目所有内容只是demo,只好含了各种技术以及中间件和springboot的集成使用,以及各种技术的常见使用场景。 7 | 适合结合具体知识点理解的基础上进行初步学习,对业务和知识点的深入理解请自行阅读其他相关资料。 8 | 9 | 核心技术 10 | 11 | 控制层: 12 | mvc交互,注解数据校验,统一接口返回(异常拦截与正常返回统一),日期交互优雅处理,返回数据序列化与非空化处理, 13 | 分布式session, 文件上传下载,拦截器,监听器,过滤器示例,更换jetty,在线文档,图片验证码 14 | 安全层: 15 | shiro安全校验,CSRF/XSS拦截 ,单点登录,jwt 16 | 持久层: 17 | JPA数据持久,数据库优雅分页,注解缓存,分布式缓存 redis,druid数据库连接池,lettuce缓存redis连接池, 18 | mysql主从读写分离,数据分片,乐观锁,mongodb,rabbitmq 19 | 业务层: 20 | 注解式事务,jta分布式事务,注解定时任务,注解异步,多环境 21 | 监控层: 22 | 系统监控,日志系统 23 | 常用工具包: 24 | 二维码,EXCEL导入导出,CSV导入导出,反射工具包,加密工具包 25 | 中间件: 26 | mysql,mongo,redis,zookeeper,rabbitmq,es,logback,sharding-sphere,swagger,java-melody等 27 | 28 | 29 | ~~对于org.springframework.boot.bind包的说明: 30 | 该包用于解决sharding-jdbc和spring版本不一致问题。 31 | io.shardingjdbc.spring.boot.SpringBootConfiguration第49行的RelaxedPropertyResolver所在包已经被spring删除。 32 | spring做法是Environment直接继承RelaxedPropertyResolver,可以直接用Environment对象调用getProperty,无需再new一个对象。 33 | 这里给出该package的原因是为了优雅的使用sharding-jdbc,待sharding-jdbc升级改掉该issue后删去该package即可~~ 34 | 35 | 36 | 更新日志 37 | 38 | 18.12.27 更新 39 | 40 | 所有依赖更新为最新版本 41 | 更改tomcat为jetty 42 | 集成redis,并和cache注解结合。 43 | 加入XSS、sql注入拦截。 44 | 加入分页查询示例 45 | 加入常用工具包:二维码,随机验证码 46 | 文件上传下载 47 | 分布式session共享 48 | 49 | 19.01.02 更新 50 | 51 | 引入shiro权限控制。用户-角色-权限 三级级联 52 | 修复部分小问题 53 | 54 | 19.01.08 更新 55 | 56 | 加入sharding-jdbc 分库分表,1主库,2从库,且对t_order表进行了数据分片 57 | 加入注解式事务管理 58 | 加入jta分布式事务 59 | 加入单元测试 60 | 美化日志输出(主要是加了颜色,文件卷积日期) 61 | 修复部分小问题 62 | 63 | 64 | 19.07.26 更新 65 | 66 | 1.加入了使用文档 67 | 2.升级所有依赖为最新稳定版本. 68 | 3.修改项目名称,groupId和package,使其保持一致。 69 | 4.加入mongodb增删改查demo 70 | 5.引入lombok支持 71 | 72 | 19.07.29 更新 73 | 74 | 1.解决所有依赖冲突,以及依赖冲突引起的问题 75 | 2.解决sharding-sphere自动事务配置错误的问题 76 | 3.解决升级sharding-sphere读写分离,数据分片集成的错误 77 | 4.调整package更清晰 78 | 5.引入redisson,示例redis分布式锁 79 | 6.示例redis发布订阅demo 80 | 7.RelaxedPropertyResolver问题已解决,删除org.springframework.boot.bind包 81 | 8.待解决 82 | 83 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.lian 7 | springboot-distributed-demo 8 | 0.0.2-SNAPSHOT 9 | jar 10 | 11 | springboot-distributed-demo 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.6.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-tomcat 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-jetty 42 | compile 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-aop 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-data-redis 59 | 60 | 61 | org.redisson 62 | redisson-spring-boot-starter 63 | 3.11.1 64 | 65 | 66 | org.javassist 67 | javassist 68 | 69 | 70 | org.objenesis 71 | objenesis 72 | 73 | 74 | 75 | 76 | 77 | org.springframework.session 78 | spring-session-data-redis 79 | 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-data-mongodb 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-starter-data-jpa 89 | 90 | 91 | 92 | org.springframework.boot 93 | spring-boot-starter-validation 94 | 95 | 96 | 97 | mysql 98 | mysql-connector-java 99 | 8.0.16 100 | 101 | 102 | 103 | com.alibaba 104 | druid 105 | 1.1.19 106 | 107 | 108 | 109 | io.shardingsphere 110 | sharding-jdbc-spring-boot-starter 111 | 3.1.0 112 | 113 | 114 | com.google.guava 115 | guava 116 | 117 | 118 | com.h2database 119 | h2 120 | 121 | 122 | 123 | 124 | com.google.guava 125 | guava 126 | 29.0-jre 127 | 128 | 129 | 130 | io.shardingsphere 131 | sharding-transaction-spring-boot-starter 132 | 3.1.0 133 | 134 | 135 | com.google.guava 136 | guava 137 | 138 | 139 | 140 | 141 | 142 | org.springframework.boot 143 | spring-boot-starter-thymeleaf 144 | 145 | 146 | 147 | net.bull.javamelody 148 | javamelody-spring-boot-starter 149 | 1.78.0 150 | 151 | 152 | 153 | io.springfox 154 | springfox-swagger-ui 155 | 2.9.2 156 | 157 | 158 | io.springfox 159 | springfox-swagger2 160 | 2.9.2 161 | 162 | 163 | 164 | com.google.zxing 165 | core 166 | 3.4.0 167 | 168 | 169 | 170 | org.apache.commons 171 | commons-lang3 172 | 3.9 173 | 174 | 175 | 176 | org.apache.shiro 177 | shiro-spring-boot-web-starter 178 | 1.4.1 179 | 180 | 181 | org.projectlombok 182 | lombok 183 | 1.18.8 184 | provided 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | org.springframework.boot 193 | spring-boot-starter-amqp 194 | 195 | 196 | 197 | 198 | 199 | spring-libs-release 200 | Spring Releases 201 | https://repo.spring.io/libs-release 202 | 203 | false 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | org.springframework.boot 212 | spring-boot-maven-plugin 213 | 214 | 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /script/createTable.sql: -------------------------------------------------------------------------------- 1 | -- mysql 2 | create database demo_0; 3 | create database demo_0; 4 | create database demo_1; 5 | create database demo_2; 6 | -- 每个database分别执行下面创建表的sql 7 | CREATE TABLE `t_order1` ( 8 | `order_no` bigint(20) NOT NULL, 9 | `user_id` bigint(20) DEFAULT NULL, 10 | PRIMARY KEY (`order_no`) 11 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 12 | CREATE TABLE `t_order2` ( 13 | `order_no` bigint(20) NOT NULL, 14 | `user_id` bigint(20) DEFAULT NULL, 15 | PRIMARY KEY (`order_no`) 16 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 17 | 18 | CREATE TABLE `t_roles_permission` ( 19 | `role_name` varchar(255) NOT NULL, 20 | `permission` varchar(255) NOT NULL, 21 | PRIMARY KEY (`role_name`) 22 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 23 | CREATE TABLE `t_user` ( 24 | `id` bigint(20) NOT NULL, 25 | `username` varchar(255) NOT NULL, 26 | `password` varchar(255) DEFAULT NULL, 27 | `salt` varchar(255) DEFAULT NULL, 28 | `version` bigint(20) DEFAULT NULL, 29 | PRIMARY KEY (`id`) 30 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 31 | CREATE TABLE `t_user_roles` ( 32 | `username` varchar(255) NOT NULL, 33 | `role_name` varchar(255) NOT NULL, 34 | PRIMARY KEY (`username`) 35 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /script/doc.md: -------------------------------------------------------------------------------- 1 | 1.克隆项目到本地环境 2 | 3 | 4 | git clone git@github.com:sjlian/springboot-distributed-demo.git 5 | 6 | 2.导入到ide中 7 | 8 | 9 | 建议用mvn方式导入。这里省略。不熟悉的同学请移步idea和eclipse怎么导入maven项目的搜索。安装lombok插件。 10 | 11 | 3.配置mysql环境 12 | 13 | 14 | 安装mysql,并配置。[具体请移步mysql官网](https://www.mysql.com/downloads/)。修改配置文件中mysql连接数据。 15 | 执行createTable的sql文件创建库表。 16 | 17 | 4.配置redis环境 18 | 19 | 20 | 安装redis,并配置。[具体见redis官网](http://redis.io/)。修改配置文件中的redis连接数据。 21 | 22 | 5.配置mongo环境 23 | 24 | 25 | 安装mongo,[详见mongodb官网](www.mongodb.org/)。修改配置文件中的mongo连接数据。 26 | 创建db-demo。命令行:use demo; 27 | 28 | 6.配置rabbitmq环境 29 | 30 | 31 | 安装rabbitmq,[详见rabbitmq官网](www.rabbitmq.com/)。修改配置文件中的rabbitmq连接数据。 32 | 33 | 34 | 7.启动项目 35 | 36 | 7.1 [在线文档](http://localhost:8080/api/swagger-ui.html) 37 | 7.2 [监控统计](http://localhost:8080/api/monitoring)用户名密码见配置文件,默认admin,pwd 38 | -------------------------------------------------------------------------------- /src/main/java/com/lian/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.lian; 2 | 3 | import org.springframework.beans.factory.ObjectProvider; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; 8 | import org.springframework.boot.web.servlet.ServletComponentScan; 9 | import org.springframework.cache.annotation.EnableCaching; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 12 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 13 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 14 | import org.springframework.scheduling.annotation.EnableAsync; 15 | import org.springframework.scheduling.annotation.EnableScheduling; 16 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; 17 | import org.springframework.transaction.PlatformTransactionManager; 18 | import org.springframework.transaction.annotation.EnableTransactionManagement; 19 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 20 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 21 | 22 | import javax.sql.DataSource; 23 | 24 | @SpringBootApplication 25 | @EnableAsync 26 | @EnableScheduling 27 | @EnableCaching 28 | @EnableSwagger2 29 | @EnableJpaRepositories(basePackages = "com.lian.domain.repository") 30 | @EnableMongoRepositories(basePackages = "com.lian.domain.mongorepository") 31 | @EnableWebMvc 32 | @EnableRedisHttpSession 33 | @ServletComponentScan 34 | @EnableTransactionManagement 35 | public class AppApplication { 36 | 37 | public static void main(String[] args) { 38 | SpringApplication.run(AppApplication.class, args); 39 | } 40 | 41 | @Bean 42 | public PlatformTransactionManager transactionManager(DataSource dataSource, ObjectProvider transactionManagerCustomizers) { 43 | DataSourceTransactionManager result = new DataSourceTransactionManager(dataSource); 44 | if (null != transactionManagerCustomizers.getIfAvailable()) { 45 | transactionManagerCustomizers.getIfAvailable().customize(result); 46 | } 47 | return result; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/Constant.java: -------------------------------------------------------------------------------- 1 | package com.lian.common; 2 | 3 | public class Constant { 4 | /** 5 | * 用户上传文件路径(根据系统自定义,注意分配权限) 6 | */ 7 | public static final String UP_FILE_ROOT_PATH = "/var/file/"; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/lock/DistributedLocker.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.lock; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.concurrent.locks.Lock; 5 | 6 | /** 7 | * @Author lian 8 | * @Date 2019-07-29 9 | */ 10 | 11 | public interface DistributedLocker { 12 | Lock lock(String lockKey); 13 | 14 | Lock lock(String lockKey, int timeout); 15 | 16 | Lock lock(String lockKey, TimeUnit unit, int timeout); 17 | 18 | boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime); 19 | 20 | void unlock(String lockKey); 21 | 22 | void unlock(Lock lock); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/lock/RedissonDistributedLocker.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.lock; 2 | 3 | import com.lian.common.lock.DistributedLocker; 4 | import org.redisson.api.RLock; 5 | import org.redisson.api.RedissonClient; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.locks.Lock; 11 | 12 | /** 13 | * @Author lian 14 | * @Date 2019-07-29 15 | */ 16 | @Component 17 | public class RedissonDistributedLocker implements DistributedLocker { 18 | @Autowired 19 | private RedissonClient redissonClient; 20 | 21 | @Override 22 | public RLock lock(String lockKey) { 23 | RLock lock = redissonClient.getLock(lockKey); 24 | lock.lock(); 25 | return lock; 26 | } 27 | 28 | @Override 29 | public RLock lock(String lockKey, int leaseTime) { 30 | RLock lock = redissonClient.getLock(lockKey); 31 | lock.lock(leaseTime, TimeUnit.SECONDS); 32 | return lock; 33 | } 34 | 35 | @Override 36 | public RLock lock(String lockKey, TimeUnit unit ,int timeout) { 37 | RLock lock = redissonClient.getLock(lockKey); 38 | lock.lock(timeout, unit); 39 | return lock; 40 | } 41 | 42 | @Override 43 | public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { 44 | RLock lock = redissonClient.getLock(lockKey); 45 | try { 46 | return lock.tryLock(waitTime, leaseTime, unit); 47 | } catch (InterruptedException e) { 48 | return false; 49 | } 50 | } 51 | 52 | @Override 53 | public void unlock(String lockKey) { 54 | RLock lock = redissonClient.getLock(lockKey); 55 | lock.unlock(); 56 | } 57 | 58 | @Override 59 | public void unlock(Lock lock) { 60 | lock.unlock(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/pubsub/RabbitReceiver.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.pubsub; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.concurrent.CountDownLatch; 6 | 7 | /** 8 | * @Author lian 9 | * @Date 2019-07-29 10 | */ 11 | @Component 12 | public class RabbitReceiver { 13 | public static final String topicExchangeName = "com.lian.demo.exchange"; 14 | 15 | public static final String queueName = "com.lian.demo"; 16 | 17 | 18 | private CountDownLatch latch = new CountDownLatch(1); 19 | 20 | public void receiveMessage(String message) { 21 | System.out.println("Received <" + message + ">"); 22 | latch.countDown(); 23 | } 24 | 25 | public CountDownLatch getLatch() { 26 | return latch; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/pubsub/RedisReceiver.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.pubsub; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * @Author lian 7 | * @Date 2019-07-29 8 | */ 9 | @Component 10 | public class RedisReceiver { 11 | public static final String MESSAGE_CHANEL = "com.lian.demo"; 12 | 13 | /** 14 | * 接收消息的方法 15 | */ 16 | public void receiveMessage(String message, String chanel) { 17 | System.out.println("收到一条消息:" + message); 18 | System.out.println("通道名称:" + chanel); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/util/IpUtil.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import org.springframework.web.context.request.ServletRequestAttributes; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | /** 10 | * @Author lian 11 | * @Date 2018/1/31 12 | */ 13 | public class IpUtil { 14 | 15 | public static String getIpAddress(HttpServletRequest request) { 16 | String xIp = request.getHeader("X-Real-IP"); 17 | String xFor = request.getHeader("X-Forwarded-For"); 18 | if(StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)){ 19 | //多次反向代理后会有多个ip值,第一个ip才是真实ip 20 | int index = xFor.indexOf(","); 21 | if(index != -1){ 22 | return xFor.substring(0,index); 23 | }else{ 24 | return xFor; 25 | } 26 | } 27 | xFor = xIp; 28 | if(StringUtils.isNotEmpty(xFor) && !"unKnown".equalsIgnoreCase(xFor)){ 29 | return xFor; 30 | } 31 | if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) { 32 | xFor = request.getHeader("Proxy-Client-IP"); 33 | } 34 | if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) { 35 | xFor = request.getHeader("WL-Proxy-Client-IP"); 36 | } 37 | if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) { 38 | xFor = request.getHeader("HTTP_CLIENT_IP"); 39 | } 40 | if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) { 41 | xFor = request.getHeader("HTTP_X_FORWARDED_FOR"); 42 | } 43 | if (StringUtils.isBlank(xFor) || "unknown".equalsIgnoreCase(xFor)) { 44 | xFor = request.getRemoteAddr(); 45 | } 46 | return xFor; 47 | } 48 | 49 | public static String getIpAddress() { 50 | HttpServletRequest request = null; 51 | try { 52 | request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 53 | } catch (Exception e) { 54 | return "192.168.1.1"; 55 | } 56 | return getIpAddress(request); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/util/LogUtil.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** 7 | * slf4j,对接所有的Log 8 | * 单例的Logger 9 | */ 10 | public class LogUtil { 11 | private static final Logger LOGGER = LoggerFactory.getLogger("log"); 12 | public static Logger getLogger(){ 13 | return LOGGER; 14 | } 15 | public static Logger getLogger(Class z){ 16 | return LoggerFactory.getLogger(z); 17 | } 18 | public static void info(String s,Object ...o){ 19 | LOGGER.info(s,o); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/lian/common/util/VerifyCodeUtil.java: -------------------------------------------------------------------------------- 1 | package com.lian.common.util; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.*; 5 | import java.awt.geom.AffineTransform; 6 | import java.awt.image.BufferedImage; 7 | import java.io.File; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.util.Random; 12 | 13 | /** 14 | * @Author lian 15 | * @Date 2018/3/26 16 | */ 17 | 18 | public class VerifyCodeUtil { 19 | //使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o,2,z几个容易混淆的字符 20 | private static final String VERIFY_CODES = "3456789ABCDEFGHJKLMNPQRSTUVWXY"; 21 | private static Random random = new Random(); 22 | 23 | 24 | /** 25 | * 使用系统默认字符源生成验证码 26 | * 27 | * @param verifySize 验证码长度 28 | * @return 29 | */ 30 | public static String generateVerifyCode(int verifySize) { 31 | return generateVerifyCode(verifySize, VERIFY_CODES); 32 | } 33 | 34 | /** 35 | * 使用指定源生成验证码 36 | * 37 | * @param verifySize 验证码长度 38 | * @param sources 验证码字符源 39 | * @return 40 | */ 41 | public static String generateVerifyCode(int verifySize, String sources) { 42 | if (sources == null || sources.length() == 0) { 43 | sources = VERIFY_CODES; 44 | } 45 | int codesLen = sources.length(); 46 | Random rand = new Random(System.currentTimeMillis()); 47 | StringBuilder verifyCode = new StringBuilder(verifySize); 48 | for (int i = 0; i < verifySize; i++) { 49 | verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1))); 50 | } 51 | return verifyCode.toString(); 52 | } 53 | 54 | /** 55 | * 生成随机验证码文件,并返回验证码值 56 | * 57 | * @param w 58 | * @param h 59 | * @param outputFile 60 | * @param verifySize 61 | * @return 62 | * @throws IOException 63 | */ 64 | public static String outputVerifyImage(int w, int h, File outputFile, int verifySize) throws IOException { 65 | String verifyCode = generateVerifyCode(verifySize); 66 | outputImage(w, h, outputFile, verifyCode); 67 | return verifyCode; 68 | } 69 | 70 | /** 71 | * 输出随机验证码图片流,并返回验证码值 72 | * 73 | * @param w 74 | * @param h 75 | * @param os 76 | * @param verifySize 77 | * @return 78 | * @throws IOException 79 | */ 80 | public static String outputVerifyImage(int w, int h, OutputStream os, int verifySize) throws IOException { 81 | String verifyCode = generateVerifyCode(verifySize); 82 | outputImage(w, h, os, verifyCode); 83 | return verifyCode; 84 | } 85 | 86 | /** 87 | * 生成指定验证码图像文件 88 | * 89 | * @param w 90 | * @param h 91 | * @param outputFile 92 | * @param code 93 | * @throws IOException 94 | */ 95 | public static void outputImage(int w, int h, File outputFile, String code) throws IOException { 96 | if (outputFile == null) { 97 | return; 98 | } 99 | File dir = outputFile.getParentFile(); 100 | if (!dir.exists()) { 101 | dir.mkdirs(); 102 | } 103 | try { 104 | outputFile.createNewFile(); 105 | FileOutputStream fos = new FileOutputStream(outputFile); 106 | outputImage(w, h, fos, code); 107 | fos.close(); 108 | } catch (IOException e) { 109 | throw e; 110 | } 111 | } 112 | 113 | /** 114 | * 输出指定验证码图片流 115 | * 116 | * @param w 117 | * @param h 118 | * @param os 119 | * @param code 120 | * @throws IOException 121 | */ 122 | public static void outputImage(int w, int h, OutputStream os, String code) throws IOException { 123 | int verifySize = code.length(); 124 | BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 125 | Random rand = new Random(); 126 | Graphics2D g2 = image.createGraphics(); 127 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 128 | 129 | g2.setColor(Color.GRAY);// 设置边框色 130 | g2.fillRect(0, 0, w, h); 131 | 132 | Color c = getRandColor(200, 250); 133 | g2.setColor(c);// 设置背景色 134 | g2.fillRect(0, 2, w, h - 4); 135 | 136 | //绘制干扰线 137 | Random random = new Random(); 138 | g2.setColor(getRandColor(160, 200));// 设置线条的颜色 139 | for (int i = 0; i < 10; i++) { 140 | int x = random.nextInt(w - 1); 141 | int y = random.nextInt(h - 1); 142 | int xl = random.nextInt(6) + 1; 143 | int yl = random.nextInt(12) + 1; 144 | g2.drawLine(x, y, x + xl + 40, y + yl + 20); 145 | } 146 | 147 | // 添加噪点 148 | float yapRate = 0.03f;// 噪声率 149 | int area = (int) (yapRate * w * h); 150 | for (int i = 0; i < area; i++) { 151 | int x = random.nextInt(w); 152 | int y = random.nextInt(h); 153 | int rgb = getRandomIntColor(); 154 | image.setRGB(x, y, rgb); 155 | } 156 | 157 | shear(g2, w, h, c);// 使图片扭曲 158 | 159 | g2.setColor(getRandColor(100, 160)); 160 | int fontSize = h - 4; 161 | 162 | Font font = new Font("Algerian", Font.ITALIC, fontSize); 163 | g2.setFont(font); 164 | char[] chars = code.toCharArray(); 165 | for (int i = 0; i < verifySize; i++) { 166 | AffineTransform affine = new AffineTransform(); 167 | affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1), (w / verifySize) * i + fontSize / 2, h / 2); 168 | g2.setTransform(affine); 169 | g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10); 170 | } 171 | 172 | g2.dispose(); 173 | ImageIO.write(image, "jpeg", os); 174 | } 175 | 176 | private static Color getRandColor(int fc, int bc) { 177 | if (fc > 255) { 178 | fc = 255; 179 | } 180 | if (bc > 255) { 181 | bc = 255; 182 | } 183 | int r = fc + random.nextInt(bc - fc); 184 | int g = fc + random.nextInt(bc - fc); 185 | int b = fc + random.nextInt(bc - fc); 186 | return new Color(r, g, b); 187 | } 188 | 189 | private static int getRandomIntColor() { 190 | int[] rgb = getRandomRgb(); 191 | int color = 0; 192 | for (int c : rgb) { 193 | color = color << 8; 194 | color = color | c; 195 | } 196 | return color; 197 | } 198 | 199 | private static int[] getRandomRgb() { 200 | int[] rgb = new int[3]; 201 | for (int i = 0; i < 3; i++) { 202 | rgb[i] = random.nextInt(255); 203 | } 204 | return rgb; 205 | } 206 | 207 | private static void shear(Graphics g, int w1, int h1, Color color) { 208 | shearX(g, w1, h1, color); 209 | shearY(g, w1, h1, color); 210 | } 211 | 212 | private static void shearX(Graphics g, int w1, int h1, Color color) { 213 | shearX(g, w1, h1, color, true); 214 | } 215 | 216 | private static void shearX(Graphics g, int w1, int h1, Color color, boolean borderGap) { 217 | 218 | int period = random.nextInt(2); 219 | int frames = 1; 220 | int phase = random.nextInt(2); 221 | 222 | for (int i = 0; i < h1; i++) { 223 | double d = (double) (period >> 1) 224 | * Math.sin((double) i / (double) period 225 | + (6.2831853071795862D * (double) phase) 226 | / (double) frames); 227 | g.copyArea(0, i, w1, 1, (int) d, 0); 228 | if (borderGap) { 229 | g.setColor(color); 230 | g.drawLine((int) d, i, 0, i); 231 | g.drawLine((int) d + w1, i, w1, i); 232 | } 233 | } 234 | 235 | } 236 | 237 | private static void shearY(Graphics g, int w1, int h1, Color color, boolean borderGap) { 238 | 239 | int period = random.nextInt(40) + 10; // 50; 240 | 241 | int frames = 20; 242 | int phase = 7; 243 | for (int i = 0; i < w1; i++) { 244 | double d = (double) (period >> 1) 245 | * Math.sin((double) i / (double) period 246 | + (6.2831853071795862D * (double) phase) 247 | / (double) frames); 248 | g.copyArea(i, 0, 1, h1, 0, (int) d); 249 | if (borderGap) { 250 | g.setColor(color); 251 | g.drawLine(i, (int) d, i, 0); 252 | g.drawLine(i, (int) d + h1, i, h1); 253 | } 254 | 255 | } 256 | 257 | } 258 | 259 | private static void shearY(Graphics g, int w1, int h1, Color color) { 260 | shearY(g, w1, h1, color, true); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/CacheConfig.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.cache.CacheManager; 8 | import org.springframework.cache.annotation.CachingConfigurerSupport; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.data.redis.cache.RedisCacheConfiguration; 12 | import org.springframework.data.redis.cache.RedisCacheManager; 13 | import org.springframework.data.redis.connection.RedisPassword; 14 | import org.springframework.data.redis.connection.RedisStandaloneConfiguration; 15 | import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; 16 | import org.springframework.data.redis.core.RedisTemplate; 17 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 18 | import org.springframework.data.redis.serializer.RedisSerializationContext; 19 | import org.springframework.data.redis.serializer.RedisSerializer; 20 | import org.springframework.data.redis.serializer.StringRedisSerializer; 21 | 22 | import java.time.Duration; 23 | 24 | @Configuration 25 | @ConfigurationProperties(prefix = "spring.redis") 26 | public class CacheConfig extends CachingConfigurerSupport { 27 | private Duration timeToLive = Duration.ofSeconds(3600 * 15); 28 | private String host = "localhost"; 29 | private Integer port = 6379; 30 | private String password = "123456"; 31 | 32 | 33 | @Bean 34 | public LettuceConnectionFactory lettuceConnectionFactory() { 35 | RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(); 36 | standaloneConfiguration.setPassword(RedisPassword.of(password)); 37 | standaloneConfiguration.setHostName(host); 38 | standaloneConfiguration.setPort(port); 39 | return new LettuceConnectionFactory(standaloneConfiguration); 40 | } 41 | 42 | //缓存key自动生成器(建议自定义,不建议使用该生成器。因为需要加入和删除缓存的key不同) 43 | // @Bean 44 | // public KeyGenerator keyGenerator() { 45 | // return (target, method, params) -> { 46 | // StringBuilder sb = new StringBuilder(); 47 | // sb.append(target.getClass().getName()); 48 | // sb.append(method.getName()); 49 | // for (Object obj : params) { 50 | // sb.append(obj.toString()); 51 | // } 52 | // return sb.toString(); 53 | // }; 54 | // } 55 | 56 | 57 | // 缓存管理器 58 | @Bean 59 | public CacheManager cacheManager() { 60 | RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() 61 | .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))//key序列化方式 62 | .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))//value序列化方式 63 | .disableCachingNullValues() 64 | .entryTtl(timeToLive);//缓存过期时间 65 | RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder 66 | .fromConnectionFactory(lettuceConnectionFactory()) 67 | .cacheDefaults(config) 68 | .transactionAware(); 69 | return builder.build(); 70 | } 71 | 72 | 73 | /** 74 | * RedisTemplate配置 75 | */ 76 | @Bean 77 | public RedisTemplate redisTemplate() { 78 | // 设置序列化 79 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); 80 | ObjectMapper om = new ObjectMapper(); 81 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 82 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 83 | jackson2JsonRedisSerializer.setObjectMapper(om); 84 | // 配置redisTemplate 85 | RedisTemplate redisTemplate = new RedisTemplate<>(); 86 | redisTemplate.setConnectionFactory(lettuceConnectionFactory()); 87 | RedisSerializer stringSerializer = new StringRedisSerializer(); 88 | redisTemplate.setKeySerializer(stringSerializer);// key序列化 89 | redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化 90 | redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化 91 | redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化 92 | redisTemplate.afterPropertiesSet(); 93 | return redisTemplate; 94 | } 95 | 96 | private RedisSerializer keySerializer() { 97 | return new StringRedisSerializer(); 98 | } 99 | 100 | private RedisSerializer valueSerializer() { 101 | // 设置序列化 102 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer( 103 | Object.class); 104 | ObjectMapper om = new ObjectMapper(); 105 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 106 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 107 | jackson2JsonRedisSerializer.setObjectMapper(om); 108 | return jackson2JsonRedisSerializer; 109 | } 110 | 111 | public String getHost() { 112 | return host; 113 | } 114 | 115 | public void setHost(String host) { 116 | this.host = host; 117 | } 118 | 119 | public Integer getPort() { 120 | return port; 121 | } 122 | 123 | public void setPort(Integer port) { 124 | this.port = port; 125 | } 126 | 127 | public String getPassword() { 128 | return password; 129 | } 130 | 131 | public void setPassword(String password) { 132 | this.password = password; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/DataSourceShiroRealm.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import com.lian.domain.entity.RolesPermissionEntity; 4 | import com.lian.domain.entity.UserEntity; 5 | import com.lian.domain.entity.UserRoleEntity; 6 | import com.lian.service.AuthService; 7 | import com.lian.service.UserService; 8 | import org.apache.shiro.authc.*; 9 | import org.apache.shiro.authz.AuthorizationException; 10 | import org.apache.shiro.authz.AuthorizationInfo; 11 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 12 | import org.apache.shiro.realm.AuthorizingRealm; 13 | import org.apache.shiro.subject.PrincipalCollection; 14 | import org.apache.shiro.util.ByteSource; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Set; 20 | 21 | public class DataSourceShiroRealm extends AuthorizingRealm { 22 | @Autowired 23 | private UserService userService; 24 | @Autowired 25 | private AuthService authService; 26 | 27 | 28 | @Override 29 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 30 | if (principals == null) { 31 | throw new AuthorizationException("PrincipalCollection method argument cannot be null."); 32 | } else { 33 | String username = (String)this.getAvailablePrincipal(principals); 34 | UserEntity userEntity = userService.findByUsername(username); 35 | if (userEntity == null){ 36 | throw new UnknownAccountException("No account found for user [" + username + "]"); 37 | } 38 | List roleEntities = authService.getUserRoleByUser(username); 39 | if (roleEntities == null || roleEntities.size() <= 0){ 40 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(null); 41 | info.setStringPermissions(null); 42 | return info; 43 | } 44 | Set roleNames = new HashSet<>(); 45 | Set permissions = new HashSet<>(32); 46 | for (UserRoleEntity roleEntity : roleEntities){ 47 | if (roleEntity.getRoleName() != null){ 48 | roleNames.add(roleEntity.getRoleName()); 49 | } 50 | } 51 | for (String role : roleNames){ 52 | List permissionEntities = authService.getRolesPermissionByRole(role); 53 | for (RolesPermissionEntity permissionEntity : permissionEntities){ 54 | permissions.add(permissionEntity.getPermission()); 55 | } 56 | } 57 | SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames); 58 | info.setStringPermissions(permissions); 59 | return info; 60 | } 61 | } 62 | 63 | @Override 64 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { 65 | UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken; 66 | String username = upToken.getUsername(); 67 | if (username == null) { 68 | throw new AccountException("Null username are not allowed by this realm."); 69 | } else { 70 | UserEntity userEntity = userService.findByUsername(username); 71 | if (userEntity == null){ 72 | throw new UnknownAccountException("No account found for user [" + username + "]"); 73 | } 74 | String salt; 75 | String password; 76 | password = userEntity.getPassword(); 77 | salt = userEntity.getSalt(); 78 | 79 | if (password == null) { 80 | throw new UnknownAccountException("No password found for user [" + username + "]"); 81 | } 82 | SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password.toCharArray(), this.getName()); 83 | if (salt != null) { 84 | info.setCredentialsSalt(ByteSource.Util.bytes(salt)); 85 | } 86 | return info; 87 | } 88 | } 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/HTMLFilter.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import java.util.*; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.concurrent.ConcurrentMap; 6 | import java.util.logging.Logger; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * 12 | * HTML filtering utility for protecting against XSS (Cross Site Scripting). 13 | * 14 | * This code is licensed LGPLv3 15 | * 16 | * This code is a Java port of the original work in PHP by Cal Hendersen. 17 | * http://code.iamcal.com/php/lib_filter/ 18 | * 19 | * The trickiest part of the translation was handling the differences in regex handling 20 | * between PHP and Java. These resources were helpful in the process: 21 | * 22 | * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html 23 | * http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php 24 | * http://www.regular-expressions.info/modifiers.html 25 | * 26 | * A note on naming conventions: instance variables are prefixed with a "v"; global 27 | * constants are in all caps. 28 | * 29 | * Sample use: 30 | * String input = ... 31 | * String clean = new HTMLFilter().filter( input ); 32 | * 33 | * The class is not thread safe. Create a new instance if in doubt. 34 | * 35 | * If you find bugs or have suggestions on improvement (especially regarding 36 | * performance), please contact us. The latest version of this 37 | * source, and our contact details, can be found at http://xss-html-filter.sf.net 38 | * 39 | * @author Joseph O'Connell 40 | * @author Cal Hendersen 41 | * @author Michael Semb Wever 42 | */ 43 | public final class HTMLFilter { 44 | 45 | /** regex flag union representing /si modifiers in php **/ 46 | private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; 47 | private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); 48 | private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); 49 | private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); 50 | private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); 51 | private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); 52 | private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); 53 | private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); 54 | private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); 55 | private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); 56 | private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); 57 | private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); 58 | private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); 59 | private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); 60 | private static final Pattern P_END_ARROW = Pattern.compile("^>"); 61 | private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); 62 | private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); 63 | private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); 64 | private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); 65 | private static final Pattern P_AMP = Pattern.compile("&"); 66 | private static final Pattern P_QUOTE = Pattern.compile("<"); 67 | private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); 68 | private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); 69 | private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); 70 | 71 | // @xxx could grow large... maybe use sesat's ReferenceMap 72 | private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap(); 73 | private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap(); 74 | 75 | /** set of allowed html elements, along with allowed attributes for each element **/ 76 | private final Map> vAllowed; 77 | /** counts of open tags for each (allowable) html element **/ 78 | private final Map vTagCounts = new HashMap(); 79 | 80 | /** html elements which must always be self-closing (e.g. "") **/ 81 | private final String[] vSelfClosingTags; 82 | /** html elements which must always have separate opening and closing tags (e.g. "") **/ 83 | private final String[] vNeedClosingTags; 84 | /** set of disallowed html elements **/ 85 | private final String[] vDisallowed; 86 | /** attributes which should be checked for valid protocols **/ 87 | private final String[] vProtocolAtts; 88 | /** allowed protocols **/ 89 | private final String[] vAllowedProtocols; 90 | /** tags which should be removed if they contain no content (e.g. "" or "") **/ 91 | private final String[] vRemoveBlanks; 92 | /** entities allowed within html markup **/ 93 | private final String[] vAllowedEntities; 94 | /** flag determining whether comments are allowed in input String. */ 95 | private final boolean stripComment; 96 | private final boolean encodeQuotes; 97 | private boolean vDebug = false; 98 | /** 99 | * flag determining whether to try to make tags when presented with "unbalanced" 100 | * angle brackets (e.g. "" becomes " text "). If set to false, 101 | * unbalanced angle brackets will be html escaped. 102 | */ 103 | private final boolean alwaysMakeTags; 104 | 105 | /** Default constructor. 106 | * 107 | */ 108 | public HTMLFilter() { 109 | vAllowed = new HashMap<>(); 110 | 111 | final ArrayList a_atts = new ArrayList<>(); 112 | a_atts.add("href"); 113 | a_atts.add("target"); 114 | vAllowed.put("a", a_atts); 115 | 116 | final ArrayList img_atts; 117 | img_atts = new ArrayList(); 118 | img_atts.add("src"); 119 | img_atts.add("width"); 120 | img_atts.add("height"); 121 | img_atts.add("alt"); 122 | vAllowed.put("img", img_atts); 123 | 124 | final ArrayList no_atts; 125 | no_atts = new ArrayList(); 126 | vAllowed.put("b", no_atts); 127 | vAllowed.put("strong", no_atts); 128 | vAllowed.put("i", no_atts); 129 | vAllowed.put("em", no_atts); 130 | 131 | vSelfClosingTags = new String[]{"img"}; 132 | vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"}; 133 | vDisallowed = new String[]{}; 134 | vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp. 135 | vProtocolAtts = new String[]{"src", "href"}; 136 | vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"}; 137 | vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"}; 138 | stripComment = true; 139 | encodeQuotes = true; 140 | alwaysMakeTags = true; 141 | } 142 | 143 | /** Set debug flag to true. Otherwise use default settings. See the default constructor. 144 | * 145 | * @param debug turn debug on with a true argument 146 | */ 147 | public HTMLFilter(final boolean debug) { 148 | this(); 149 | vDebug = debug; 150 | 151 | } 152 | 153 | /** Map-parameter configurable constructor. 154 | * 155 | * @param conf map containing configuration. keys match field names. 156 | */ 157 | public HTMLFilter(final Map conf) { 158 | 159 | assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; 160 | assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; 161 | assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; 162 | assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; 163 | assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; 164 | assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; 165 | assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; 166 | assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; 167 | 168 | vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); 169 | vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); 170 | vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); 171 | vDisallowed = (String[]) conf.get("vDisallowed"); 172 | vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); 173 | vProtocolAtts = (String[]) conf.get("vProtocolAtts"); 174 | vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); 175 | vAllowedEntities = (String[]) conf.get("vAllowedEntities"); 176 | stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; 177 | encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; 178 | alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; 179 | } 180 | 181 | private void reset() { 182 | vTagCounts.clear(); 183 | } 184 | 185 | private void debug(final String msg) { 186 | if (vDebug) { 187 | Logger.getAnonymousLogger().info(msg); 188 | } 189 | } 190 | 191 | //--------------------------------------------------------------- 192 | // my versions of some PHP library functions 193 | public static String chr(final int decimal) { 194 | return String.valueOf((char) decimal); 195 | } 196 | 197 | public static String htmlSpecialChars(final String s) { 198 | String result = s; 199 | result = regexReplace(P_AMP, "&", result); 200 | result = regexReplace(P_QUOTE, """, result); 201 | result = regexReplace(P_LEFT_ARROW, "<", result); 202 | result = regexReplace(P_RIGHT_ARROW, ">", result); 203 | return result; 204 | } 205 | 206 | //--------------------------------------------------------------- 207 | /** 208 | * given a user submitted input String, filter out any invalid or restricted 209 | * html. 210 | * 211 | * @param input text (i.e. submitted by a user) than may contain html 212 | * @return "clean" version of input, with only valid, whitelisted html elements allowed 213 | */ 214 | public String filter(final String input) { 215 | reset(); 216 | String s = input; 217 | 218 | debug("************************************************"); 219 | debug(" INPUT: " + input); 220 | 221 | s = escapeComments(s); 222 | debug(" escapeComments: " + s); 223 | 224 | s = balanceHTML(s); 225 | debug(" balanceHTML: " + s); 226 | 227 | s = checkTags(s); 228 | debug(" checkTags: " + s); 229 | 230 | s = processRemoveBlanks(s); 231 | debug("processRemoveBlanks: " + s); 232 | 233 | s = validateEntities(s); 234 | debug(" validateEntites: " + s); 235 | 236 | debug("************************************************\n\n"); 237 | return s; 238 | } 239 | 240 | public boolean isAlwaysMakeTags(){ 241 | return alwaysMakeTags; 242 | } 243 | 244 | public boolean isStripComments(){ 245 | return stripComment; 246 | } 247 | 248 | private String escapeComments(final String s) { 249 | final Matcher m = P_COMMENTS.matcher(s); 250 | final StringBuffer buf = new StringBuffer(); 251 | if (m.find()) { 252 | final String match = m.group(1); //(.*?) 253 | m.appendReplacement(buf, Matcher.quoteReplacement("")); 254 | } 255 | m.appendTail(buf); 256 | 257 | return buf.toString(); 258 | } 259 | 260 | private String balanceHTML(String s) { 261 | if (alwaysMakeTags) { 262 | // 263 | // try and form html 264 | // 265 | s = regexReplace(P_END_ARROW, "", s); 266 | s = regexReplace(P_BODY_TO_END, "<$1>", s); 267 | s = regexReplace(P_XML_CONTENT, "$1<$2", s); 268 | 269 | } else { 270 | // 271 | // escape stray brackets 272 | // 273 | s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); 274 | s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); 275 | 276 | // 277 | // the last regexp causes '<>' entities to appear 278 | // (we need to do a lookahead assertion so that the last bracket can 279 | // be used in the next pass of the regexp) 280 | // 281 | s = regexReplace(P_BOTH_ARROWS, "", s); 282 | } 283 | 284 | return s; 285 | } 286 | 287 | private String checkTags(String s) { 288 | Matcher m = P_TAGS.matcher(s); 289 | 290 | final StringBuffer buf = new StringBuffer(); 291 | while (m.find()) { 292 | String replaceStr = m.group(1); 293 | replaceStr = processTag(replaceStr); 294 | m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); 295 | } 296 | m.appendTail(buf); 297 | 298 | s = buf.toString(); 299 | 300 | // these get tallied in processTag 301 | // (remember to reset before subsequent calls to filter method) 302 | for (String key : vTagCounts.keySet()) { 303 | for (int ii = 0; ii < vTagCounts.get(key); ii++) { 304 | s += ""; 305 | } 306 | } 307 | 308 | return s; 309 | } 310 | 311 | private String processRemoveBlanks(final String s) { 312 | String result = s; 313 | for (String tag : vRemoveBlanks) { 314 | if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){ 315 | P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); 316 | } 317 | result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); 318 | if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){ 319 | P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); 320 | } 321 | result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); 322 | } 323 | 324 | return result; 325 | } 326 | 327 | private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) { 328 | Matcher m = regex_pattern.matcher(s); 329 | return m.replaceAll(replacement); 330 | } 331 | 332 | private String processTag(final String s) { 333 | // ending tags 334 | Matcher m = P_END_TAG.matcher(s); 335 | if (m.find()) { 336 | final String name = m.group(1).toLowerCase(); 337 | if (allowed(name)) { 338 | if (!inArray(name, vSelfClosingTags)) { 339 | if (vTagCounts.containsKey(name)) { 340 | vTagCounts.put(name, vTagCounts.get(name) - 1); 341 | return ""; 342 | } 343 | } 344 | } 345 | } 346 | 347 | // starting tags 348 | m = P_START_TAG.matcher(s); 349 | if (m.find()) { 350 | final String name = m.group(1).toLowerCase(); 351 | final String body = m.group(2); 352 | String ending = m.group(3); 353 | 354 | //debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); 355 | if (allowed(name)) { 356 | String params = ""; 357 | 358 | final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); 359 | final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); 360 | final List paramNames = new ArrayList(); 361 | final List paramValues = new ArrayList(); 362 | while (m2.find()) { 363 | paramNames.add(m2.group(1)); //([a-z0-9]+) 364 | paramValues.add(m2.group(3)); //(.*?) 365 | } 366 | while (m3.find()) { 367 | paramNames.add(m3.group(1)); //([a-z0-9]+) 368 | paramValues.add(m3.group(3)); //([^\"\\s']+) 369 | } 370 | 371 | String paramName, paramValue; 372 | for (int ii = 0; ii < paramNames.size(); ii++) { 373 | paramName = paramNames.get(ii).toLowerCase(); 374 | paramValue = paramValues.get(ii); 375 | 376 | // debug( "paramName='" + paramName + "'" ); 377 | // debug( "paramValue='" + paramValue + "'" ); 378 | // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); 379 | 380 | if (allowedAttribute(name, paramName)) { 381 | if (inArray(paramName, vProtocolAtts)) { 382 | paramValue = processParamProtocol(paramValue); 383 | } 384 | params += " " + paramName + "=\"" + paramValue + "\""; 385 | } 386 | } 387 | 388 | if (inArray(name, vSelfClosingTags)) { 389 | ending = " /"; 390 | } 391 | 392 | if (inArray(name, vNeedClosingTags)) { 393 | ending = ""; 394 | } 395 | 396 | if (ending == null || ending.length() < 1) { 397 | if (vTagCounts.containsKey(name)) { 398 | vTagCounts.put(name, vTagCounts.get(name) + 1); 399 | } else { 400 | vTagCounts.put(name, 1); 401 | } 402 | } else { 403 | ending = " /"; 404 | } 405 | return "<" + name + params + ending + ">"; 406 | } else { 407 | return ""; 408 | } 409 | } 410 | 411 | // comments 412 | m = P_COMMENT.matcher(s); 413 | if (!stripComment && m.find()) { 414 | return "<" + m.group() + ">"; 415 | } 416 | 417 | return ""; 418 | } 419 | 420 | private String processParamProtocol(String s) { 421 | s = decodeEntities(s); 422 | final Matcher m = P_PROTOCOL.matcher(s); 423 | if (m.find()) { 424 | final String protocol = m.group(1); 425 | if (!inArray(protocol, vAllowedProtocols)) { 426 | // bad protocol, turn into local anchor link instead 427 | s = "#" + s.substring(protocol.length() + 1, s.length()); 428 | if (s.startsWith("#//")) { 429 | s = "#" + s.substring(3, s.length()); 430 | } 431 | } 432 | } 433 | 434 | return s; 435 | } 436 | 437 | private String decodeEntities(String s) { 438 | StringBuffer buf = new StringBuffer(); 439 | 440 | Matcher m = P_ENTITY.matcher(s); 441 | while (m.find()) { 442 | final String match = m.group(1); 443 | final int decimal = Integer.decode(match); 444 | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); 445 | } 446 | m.appendTail(buf); 447 | s = buf.toString(); 448 | 449 | buf = new StringBuffer(); 450 | m = P_ENTITY_UNICODE.matcher(s); 451 | while (m.find()) { 452 | final String match = m.group(1); 453 | final int decimal = Integer.valueOf(match, 16); 454 | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); 455 | } 456 | m.appendTail(buf); 457 | s = buf.toString(); 458 | 459 | buf = new StringBuffer(); 460 | m = P_ENCODE.matcher(s); 461 | while (m.find()) { 462 | final String match = m.group(1); 463 | final int decimal = Integer.valueOf(match, 16); 464 | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); 465 | } 466 | m.appendTail(buf); 467 | s = buf.toString(); 468 | 469 | s = validateEntities(s); 470 | return s; 471 | } 472 | 473 | private String validateEntities(final String s) { 474 | StringBuffer buf = new StringBuffer(); 475 | 476 | // validate entities throughout the string 477 | Matcher m = P_VALID_ENTITIES.matcher(s); 478 | while (m.find()) { 479 | final String one = m.group(1); //([^&;]*) 480 | final String two = m.group(2); //(?=(;|&|$)) 481 | m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); 482 | } 483 | m.appendTail(buf); 484 | 485 | return encodeQuotes(buf.toString()); 486 | } 487 | 488 | private String encodeQuotes(final String s){ 489 | if(encodeQuotes){ 490 | StringBuffer buf = new StringBuffer(); 491 | Matcher m = P_VALID_QUOTES.matcher(s); 492 | while (m.find()) { 493 | final String one = m.group(1); //(>|^) 494 | final String two = m.group(2); //([^<]+?) 495 | final String three = m.group(3); //(<|$) 496 | m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three)); 497 | } 498 | m.appendTail(buf); 499 | return buf.toString(); 500 | }else{ 501 | return s; 502 | } 503 | } 504 | 505 | private String checkEntity(final String preamble, final String term) { 506 | 507 | return ";".equals(term) && isValidEntity(preamble) 508 | ? '&' + preamble 509 | : "&" + preamble; 510 | } 511 | 512 | private boolean isValidEntity(final String entity) { 513 | return inArray(entity, vAllowedEntities); 514 | } 515 | 516 | private static boolean inArray(final String s, final String[] array) { 517 | for (String item : array) { 518 | if (item != null && item.equals(s)) { 519 | return true; 520 | } 521 | } 522 | return false; 523 | } 524 | 525 | private boolean allowed(final String name) { 526 | return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); 527 | } 528 | 529 | private boolean allowedAttribute(final String name, final String paramName) { 530 | return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); 531 | } 532 | } -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import com.lian.common.pubsub.RedisReceiver; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.connection.RedisConnectionFactory; 7 | import org.springframework.data.redis.listener.PatternTopic; 8 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; 9 | import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; 10 | 11 | /** 12 | * @Author lian 13 | * @Date 2019-07-29 14 | */ 15 | @Configuration 16 | public class RedisConfig { 17 | @Bean 18 | RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory, MessageListenerAdapter listenerAdapter) { 19 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); 20 | container.setConnectionFactory(redisConnectionFactory); 21 | //订阅通道 22 | container.addMessageListener(listenerAdapter, new PatternTopic(RedisReceiver.MESSAGE_CHANEL)); 23 | return container; 24 | } 25 | 26 | 27 | /** 28 | * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法 29 | * 30 | * @param cs 31 | * @return 32 | */ 33 | @Bean 34 | MessageListenerAdapter listenerAdapter(RedisReceiver cs) { 35 | return new MessageListenerAdapter(cs, "receiveMessage"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/SQLFilter.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import com.lian.web.response.MyException; 4 | /** 5 | * SQL过滤 6 | * @author chenshun 7 | */ 8 | public class SQLFilter { 9 | 10 | /** 11 | * SQL注入过滤 12 | * @param str 待验证的字符串 13 | */ 14 | public static String sqlInject(String str){ 15 | if(str == null || str.length() <= 0){ 16 | return null; 17 | } 18 | //去掉'|"|;|\字符 19 | str = str.replace( "'", ""); 20 | str = str.replace( "\"", ""); 21 | str = str.replace( ";", ""); 22 | str = str.replace("\\", ""); 23 | 24 | //转换成小写 25 | str = str.toLowerCase(); 26 | 27 | //非法字符 28 | String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; 29 | 30 | //判断是否包含非法字符 31 | for(String keyword : keywords){ 32 | if(str.contains(keyword)){ 33 | throw MyException.notAllowed(null,"包含非法字符"); 34 | } 35 | } 36 | 37 | return str; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import org.apache.shiro.realm.Realm; 4 | import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; 5 | import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class ShiroConfig { 11 | @Bean 12 | public Realm dataSourceRealm(){ 13 | return new DataSourceShiroRealm(); 14 | } 15 | 16 | @Bean 17 | public ShiroFilterChainDefinition shiroFilterChainDefinition() { 18 | DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); 19 | //TODO 根据实际情况进行配置 20 | 21 | // logged in users with the 'admin' role 22 | chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]"); 23 | 24 | // logged in users with the 'document:read' permission 25 | chainDefinition.addPathDefinition("/docs/**", "authc, perms[document:read]"); 26 | 27 | // all other paths require a logged in user 28 | chainDefinition.addPathDefinition("/**", "anon"); 29 | 30 | return chainDefinition; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 8 | import springfox.documentation.builders.ApiInfoBuilder; 9 | import springfox.documentation.builders.PathSelectors; 10 | import springfox.documentation.builders.RequestHandlerSelectors; 11 | import springfox.documentation.service.ApiInfo; 12 | import springfox.documentation.service.Contact; 13 | import springfox.documentation.spi.DocumentationType; 14 | import springfox.documentation.spring.web.plugins.Docket; 15 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 16 | 17 | /** 18 | * @Author lian 19 | * @Date 2018/3/20 20 | */ 21 | @Configuration 22 | @ConfigurationProperties(prefix = "swagger.conf") 23 | @EnableSwagger2 24 | public class SwaggerConfig extends WebMvcConfigurationSupport { 25 | private String name; 26 | private String url; 27 | private String email; 28 | @Bean 29 | public Docket createRestApi() { 30 | return new Docket(DocumentationType.SWAGGER_2) 31 | .apiInfo(apiInfo()) 32 | .select() 33 | .apis(RequestHandlerSelectors.basePackage("com.lian.web")) 34 | .paths(PathSelectors.any()) 35 | .build(); 36 | } 37 | 38 | private ApiInfo apiInfo() { 39 | return new ApiInfoBuilder() 40 | .title("DEMO——文档") 41 | .description("这里是详细说明") 42 | .contact(new Contact(name,url,email)) 43 | .version("1.0") 44 | .build(); 45 | } 46 | 47 | @Override 48 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 49 | registry.addResourceHandler("swagger-ui.html") 50 | .addResourceLocations("classpath:/META-INF/resources/"); 51 | registry.addResourceHandler("/webjars/**") 52 | .addResourceLocations("classpath:/META-INF/resources/webjars/"); 53 | 54 | } 55 | 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | public void setName(String name) { 61 | this.name = name; 62 | } 63 | 64 | public String getUrl() { 65 | return url; 66 | } 67 | 68 | public void setUrl(String url) { 69 | this.url = url; 70 | } 71 | 72 | public String getEmail() { 73 | return email; 74 | } 75 | 76 | public void setEmail(String email) { 77 | this.email = email; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/XssFilter.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import javax.servlet.*; 4 | import javax.servlet.annotation.WebFilter; 5 | import javax.servlet.http.HttpServletRequest; 6 | import java.io.IOException; 7 | 8 | /** 9 | * 演示filter使用方法 10 | * XSS过滤 11 | * @author 12 | */ 13 | @WebFilter(filterName = "xssFilter", urlPatterns = "/*", asyncSupported = true) 14 | public class XssFilter implements Filter { 15 | 16 | @Override 17 | public void init(FilterConfig config) { 18 | } 19 | 20 | @Override 21 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 22 | throws IOException, ServletException { 23 | XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper( 24 | (HttpServletRequest) request); 25 | chain.doFilter(xssRequest, response); 26 | } 27 | 28 | @Override 29 | public void destroy() { 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /src/main/java/com/lian/conf/XssHttpServletRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lian.conf; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.MediaType; 5 | 6 | import javax.servlet.ReadListener; 7 | import javax.servlet.ServletInputStream; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletRequestWrapper; 10 | import java.io.BufferedReader; 11 | import java.io.ByteArrayInputStream; 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map; 17 | import java.util.stream.Collectors; 18 | 19 | /** 20 | * XSS过滤处理 21 | * 22 | * @author chenshun 23 | */ 24 | public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { 25 | //没被包装过的HttpServletRequest(特殊场景,需要自己过滤) 26 | private HttpServletRequest orgRequest; 27 | //html过滤 28 | private final static HTMLFilter HTML_FILTER = new HTMLFilter(); 29 | 30 | public XssHttpServletRequestWrapper(HttpServletRequest request) { 31 | super(request); 32 | orgRequest = request; 33 | } 34 | 35 | @Override 36 | public ServletInputStream getInputStream() throws IOException { 37 | //非json类型,直接返回 38 | if (!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))) { 39 | return super.getInputStream(); 40 | } 41 | 42 | //为空,直接返回 43 | String json = new BufferedReader(new InputStreamReader(super.getInputStream())) 44 | .lines().collect(Collectors.joining(System.lineSeparator())); 45 | if (json == null || json.length() <= 0) { 46 | return super.getInputStream(); 47 | } 48 | 49 | //xss过滤 50 | json = xssEncode(json); 51 | final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); 52 | return new ServletInputStream() { 53 | @Override 54 | public boolean isFinished() { 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean isReady() { 60 | return true; 61 | } 62 | 63 | @Override 64 | public void setReadListener(ReadListener readListener) { 65 | } 66 | 67 | @Override 68 | public int read() throws IOException { 69 | return bis.read(); 70 | } 71 | }; 72 | } 73 | 74 | @Override 75 | public String getParameter(String name) { 76 | String value = super.getParameter(xssEncode(name)); 77 | if (value != null && value.length() > 0) { 78 | value = xssEncode(value); 79 | } 80 | return value; 81 | } 82 | 83 | @Override 84 | public String[] getParameterValues(String name) { 85 | String[] parameters = super.getParameterValues(name); 86 | if (parameters == null || parameters.length == 0) { 87 | return null; 88 | } 89 | 90 | for (int i = 0; i < parameters.length; i++) { 91 | parameters[i] = xssEncode(parameters[i]); 92 | } 93 | return parameters; 94 | } 95 | 96 | @Override 97 | public Map getParameterMap() { 98 | Map map = new LinkedHashMap<>(); 99 | Map parameters = super.getParameterMap(); 100 | for (String key : parameters.keySet()) { 101 | String[] values = parameters.get(key); 102 | for (int i = 0; i < values.length; i++) { 103 | values[i] = xssEncode(values[i]); 104 | } 105 | map.put(key, values); 106 | } 107 | return map; 108 | } 109 | 110 | @Override 111 | public String getHeader(String name) { 112 | String value = super.getHeader(xssEncode(name)); 113 | if (value != null && value.length() > 0) { 114 | value = xssEncode(value); 115 | } 116 | return value; 117 | } 118 | 119 | private String xssEncode(String input) { 120 | return HTML_FILTER.filter(input); 121 | } 122 | 123 | /** 124 | * 获取最原始的request 125 | */ 126 | public HttpServletRequest getOrgRequest() { 127 | return orgRequest; 128 | } 129 | 130 | /** 131 | * 获取最原始的request 132 | */ 133 | public static HttpServletRequest getOrgRequest(HttpServletRequest request) { 134 | if (request instanceof XssHttpServletRequestWrapper) { 135 | return ((XssHttpServletRequestWrapper) request).getOrgRequest(); 136 | } 137 | 138 | return request; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/entity/OrderDoc.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.entity; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.data.mongodb.core.index.Indexed; 6 | import org.springframework.data.mongodb.core.mapping.Document; 7 | 8 | import javax.persistence.Id; 9 | 10 | /** 11 | * @Author lian 12 | * @Date 2019-07-26 13 | */ 14 | @Document(collection = "order") 15 | @Setter 16 | @Getter 17 | public class OrderDoc { 18 | @Id 19 | private String id; 20 | 21 | @Indexed 22 | private Long orderNo; 23 | 24 | private Long userId; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/entity/OrderEntity.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.entity; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | 12 | @Entity 13 | @Table(name = "t_order") 14 | @Setter 15 | @Getter 16 | public class OrderEntity { 17 | /** 18 | * 根据订单生成器生成 19 | */ 20 | @Id 21 | @Column(name = "order_no") 22 | @ApiModelProperty("订单号") 23 | private Long orderNo; 24 | 25 | @Column(name = "user_id") 26 | @ApiModelProperty("用户id") 27 | private Long userId; 28 | 29 | //TODO other params 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/entity/RolesPermissionEntity.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.entity; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | import javax.validation.constraints.NotNull; 12 | 13 | @Entity 14 | @Table(name = "t_roles_permission") 15 | @Setter 16 | @Getter 17 | public class RolesPermissionEntity { 18 | @Id 19 | @Column(name = "role_name") 20 | @ApiModelProperty(value = "角色名") 21 | @NotNull(message = "角色名不能为空") 22 | private String roleName; 23 | 24 | @Column(name = "permission") 25 | @ApiModelProperty(value = "角色权限",allowEmptyValue = true) 26 | @NotNull(message = "角色权限") 27 | private String permission; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/entity/UserEntity.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.entity; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.springframework.data.annotation.CreatedDate; 7 | 8 | import javax.persistence.*; 9 | import javax.validation.constraints.NotNull; 10 | import java.util.Date; 11 | 12 | /** 13 | * @Author lian 14 | * @Date 2018/3/20 15 | */ 16 | @Entity 17 | @Table(name = "t_user") 18 | @Setter 19 | @Getter 20 | public class UserEntity { 21 | @Id 22 | @Column(name = "id") 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | @ApiModelProperty("id") 25 | private Long id; 26 | 27 | @Column(name = "username") 28 | @ApiModelProperty(value = "用户名",allowEmptyValue = false,readOnly=false) 29 | @NotNull(message = "用户名不能为空") 30 | private String username; 31 | 32 | @Column(name = "password") 33 | @ApiModelProperty("密码") 34 | private String password; 35 | 36 | @Column(name = "salt") 37 | @ApiModelProperty("密码盐") 38 | private String salt; 39 | 40 | @Version 41 | @ApiModelProperty("乐观锁,version=version时才更新,同时version+1,jpa加上version注解后自动实现了") 42 | @Column(name = "version") 43 | private Long version; 44 | 45 | @CreatedDate 46 | private Date createdDate; 47 | } -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/entity/UserRoleEntity.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.entity; 2 | 3 | import io.swagger.annotations.ApiModelProperty; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.Id; 10 | import javax.persistence.Table; 11 | import javax.validation.constraints.NotNull; 12 | 13 | @Entity 14 | @Table(name = "t_user_roles") 15 | @Setter 16 | @Getter 17 | public class UserRoleEntity { 18 | @Id 19 | @Column(name = "username") 20 | @ApiModelProperty(value = "用户名",allowEmptyValue = false,readOnly=false) 21 | @NotNull(message = "用户名不能为空") 22 | private String username; 23 | 24 | @Column(name = "role_name") 25 | @ApiModelProperty(value = "角色名",allowEmptyValue = false,readOnly=false) 26 | @NotNull(message = "角色名不能为空") 27 | private String roleName; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/mongorepository/OrderDocRepository.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.mongorepository; 2 | 3 | import com.lian.domain.entity.OrderDoc; 4 | import org.springframework.data.mongodb.repository.MongoRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | /** 8 | * @Author lian 9 | * @Date 2019-07-26 10 | */ 11 | @Repository 12 | public interface OrderDocRepository extends MongoRepository { 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/repository/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.repository; 2 | 3 | import com.lian.domain.entity.OrderEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface OrderRepository extends JpaRepository { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/repository/RolesPermissionRepository.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.repository; 2 | 3 | import com.lian.domain.entity.RolesPermissionEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface RolesPermissionRepository extends JpaRepository { 11 | List findByRoleName(String roleName); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.repository; 2 | 3 | import com.lian.domain.entity.UserEntity; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.stereotype.Repository; 8 | 9 | /** 10 | * @Author lian 11 | * @Date 2018/3/20 12 | */ 13 | @Repository 14 | public interface UserRepository extends JpaRepository { 15 | UserEntity findByUsername(String username); 16 | 17 | Page findAll(Pageable pageable); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/lian/domain/repository/UserRoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.lian.domain.repository; 2 | 3 | import com.lian.domain.entity.UserRoleEntity; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.List; 8 | 9 | @Repository 10 | public interface UserRoleRepository extends JpaRepository { 11 | List findByUsername(String username); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package com.lian.service; 2 | 3 | import com.lian.domain.entity.RolesPermissionEntity; 4 | import com.lian.domain.entity.UserRoleEntity; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @Author lian 10 | * @Date 2018/3/29 11 | */ 12 | 13 | public interface AuthService { 14 | List getRolesPermissionByRole(String roleName); 15 | 16 | List getUserRoleByUser(String username); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.lian.service; 2 | 3 | import com.lian.domain.entity.OrderEntity; 4 | 5 | import java.util.List; 6 | 7 | public interface OrderService { 8 | OrderEntity saveOrder(OrderEntity orderEntity); 9 | 10 | OrderEntity getOrder(Long orderNo); 11 | 12 | List list(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.lian.service; 2 | 3 | import com.lian.domain.entity.UserEntity; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @Author lian 11 | * @Date 2018/3/20 12 | */ 13 | 14 | public interface UserService { 15 | List findAll(); 16 | 17 | UserEntity save(UserEntity entity); 18 | 19 | void delete(UserEntity entity); 20 | 21 | void deleteById(Long id); 22 | 23 | UserEntity findById(Long id); 24 | 25 | UserEntity update(UserEntity entity); 26 | 27 | Page pageUser(Pageable pageable); 28 | 29 | UserEntity findByUsername(String username); 30 | 31 | void deleteWithTransaction(UserEntity entity); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/impl/AuthServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lian.service.impl; 2 | 3 | import com.lian.domain.entity.RolesPermissionEntity; 4 | import com.lian.domain.entity.UserRoleEntity; 5 | import com.lian.domain.repository.RolesPermissionRepository; 6 | import com.lian.domain.repository.UserRoleRepository; 7 | import com.lian.service.AuthService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Author lian 15 | * @Date 2018/3/29 16 | */ 17 | @Service 18 | public class AuthServiceImpl implements AuthService{ 19 | 20 | @Autowired 21 | private UserRoleRepository userRoleRepository; 22 | @Autowired 23 | private RolesPermissionRepository rolesPermissionRepository; 24 | @Override 25 | public List getRolesPermissionByRole(String roleName) { 26 | return rolesPermissionRepository.findByRoleName(roleName); 27 | } 28 | 29 | @Override 30 | public List getUserRoleByUser(String username) { 31 | return userRoleRepository.findByUsername(username); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/impl/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lian.service.impl; 2 | 3 | import com.lian.domain.entity.OrderEntity; 4 | import com.lian.domain.repository.OrderRepository; 5 | import com.lian.service.OrderService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | @Service 13 | public class OrderServiceImpl implements OrderService { 14 | @Autowired 15 | private OrderRepository orderRepository; 16 | @Override 17 | public OrderEntity saveOrder(OrderEntity orderEntity) { 18 | return orderRepository.save(orderEntity); 19 | } 20 | 21 | @Override 22 | public OrderEntity getOrder(Long orderNo) { 23 | return orderRepository.getOne(orderNo); 24 | } 25 | 26 | @Override 27 | public List list() { 28 | return orderRepository.findAll(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/lian/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.lian.service.impl; 2 | 3 | import com.lian.common.util.LogUtil; 4 | import com.lian.domain.entity.UserEntity; 5 | import com.lian.domain.repository.UserRepository; 6 | import com.lian.service.UserService; 7 | import com.lian.web.response.MyException; 8 | import lombok.extern.slf4j.Slf4j; 9 | import lombok.extern.slf4j.XSlf4j; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.cache.annotation.CacheEvict; 12 | import org.springframework.cache.annotation.Cacheable; 13 | import org.springframework.data.domain.Page; 14 | import org.springframework.data.domain.Pageable; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Isolation; 17 | import org.springframework.transaction.annotation.Propagation; 18 | import org.springframework.transaction.annotation.Transactional; 19 | 20 | import java.util.List; 21 | import java.util.Optional; 22 | 23 | /** 24 | * @Author lian 25 | * @Date 2018/3/20 26 | */ 27 | @Service 28 | @Slf4j 29 | public class UserServiceImpl implements UserService { 30 | @Autowired 31 | private UserRepository userRepository; 32 | 33 | @Override 34 | public List findAll() { 35 | return userRepository.findAll(); 36 | } 37 | 38 | @Override 39 | public UserEntity save(UserEntity entity) { 40 | return userRepository.save(entity); 41 | } 42 | 43 | @Override 44 | @CacheEvict(cacheNames = "entity", key = "'user_' + #entity.id") 45 | public void delete(UserEntity entity) { 46 | userRepository.delete(entity); 47 | } 48 | 49 | @Override 50 | @CacheEvict(cacheNames = "entity", key = "'user_' + #id") 51 | public void deleteById(Long id) { 52 | if (id == 1) { 53 | throw MyException.notAllowed(null, "不允许删除"); 54 | } 55 | userRepository.deleteById(id); 56 | } 57 | 58 | @Override 59 | @Cacheable(cacheNames = "entity", key = "'user_' + #id") 60 | public UserEntity findById(Long id) { 61 | log.debug("从数据库里获取数据" + id); 62 | Optional optional = userRepository.findById(id); 63 | return optional.orElseGet(UserEntity::new); 64 | 65 | } 66 | 67 | @Override 68 | public Page pageUser(Pageable pageable) { 69 | return userRepository.findAll(pageable); 70 | } 71 | 72 | @Override 73 | public UserEntity findByUsername(String username) { 74 | return userRepository.findByUsername(username); 75 | } 76 | 77 | /** 78 | * 事务控制,传播属性,隔离级别,超时,只读属性 79 | * 80 | * @param entity 81 | */ 82 | @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = -1, readOnly = false) 83 | @CacheEvict(cacheNames = "entity", key = "'user_' + #entity.id", beforeInvocation = true) 84 | @Override 85 | public void deleteWithTransaction(UserEntity entity) { 86 | if (entity == null || entity.getId() == null) { 87 | throw MyException.inputError(null, "user id not allow null"); 88 | } 89 | userRepository.delete(entity); 90 | throw MyException.notAllowed(null, "这里手动抛出一个异常,以测试事务回滚"); 91 | } 92 | 93 | 94 | @Override 95 | @CacheEvict(cacheNames = "entity", key = "'user_' + #entity.id") 96 | public UserEntity update(UserEntity entity) { 97 | return userRepository.save(entity); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/lian/task/AsyncTask.java: -------------------------------------------------------------------------------- 1 | package com.lian.task; 2 | 3 | import org.springframework.scheduling.annotation.Async; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * @author lian 8 | * @date 2017/11/22. 9 | */ 10 | @Component 11 | public class AsyncTask { 12 | @Async 13 | public void asyncSayHello() { 14 | try { 15 | Thread.sleep(30000); 16 | } catch (InterruptedException e) { 17 | e.printStackTrace(); 18 | } 19 | System.out.println("I am asyncSayHello,and i will be after sayHello"); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/lian/task/ScheduleTask.java: -------------------------------------------------------------------------------- 1 | package com.lian.task; 2 | 3 | import org.springframework.scheduling.annotation.Scheduled; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @author lian 10 | * @date 2017/12/13 11 | */ 12 | public interface ScheduleTask { 13 | 14 | @Scheduled(fixedRate = 300000000) 15 | void ratePrintTime(); 16 | @Scheduled(cron = "0 0/10 * * * ?") 17 | void schedulePrintTime(); 18 | } -------------------------------------------------------------------------------- /src/main/java/com/lian/task/ScheduleTaskImpl.java: -------------------------------------------------------------------------------- 1 | package com.lian.task; 2 | 3 | import org.springframework.scheduling.annotation.Scheduled; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @Author lian 10 | * @Date 2019-07-26 11 | */ 12 | @Component 13 | public class ScheduleTaskImpl implements ScheduleTask{ 14 | //每隔3000000ms会执行一次 15 | @Override 16 | public void ratePrintTime() { 17 | System.out.println(new Date()); 18 | } 19 | //可以具体到哪月哪几天周几几分几秒,具体配置请自行搜索Scheduled 20 | @Override 21 | public void schedulePrintTime() { 22 | System.out.println(new Date()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/DAOController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.domain.entity.UserEntity; 4 | import com.lian.web.response.RestInfo; 5 | import com.lian.service.UserService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.domain.Page; 8 | import org.springframework.data.domain.PageRequest; 9 | import org.springframework.data.domain.Pageable; 10 | import org.springframework.validation.annotation.Validated; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 演示数据层操作,各种传参方式,参数校验 17 | * @Author lian 18 | * @Date 2018/3/20 19 | */ 20 | @RestController 21 | @RequestMapping("/dao") 22 | public class DAOController { 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | @GetMapping("/listUser") 28 | public RestInfo getUserList() { 29 | List list = userService.findAll(); 30 | return new RestInfo<>(list); 31 | } 32 | 33 | @GetMapping("/getUser/{id}") 34 | public RestInfo getUser(@PathVariable("id") Long id) { 35 | UserEntity entity = userService.findById(id); 36 | return new RestInfo<>(entity); 37 | } 38 | 39 | 40 | @PostMapping("/saveUser") 41 | public RestInfo save(@RequestBody @Validated UserEntity userEntity) { 42 | return new RestInfo<>(userService.save(userEntity)); 43 | } 44 | 45 | @PostMapping("/deleteUser/{id}") 46 | public RestInfo delete(@PathVariable Long id) { 47 | userService.deleteById(id); 48 | return new RestInfo(); 49 | } 50 | 51 | @GetMapping("/pageUser") 52 | public RestInfo page(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page, 53 | @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) { 54 | Pageable pageable = PageRequest.of(page, size); 55 | Page list = userService.pageUser(pageable); 56 | return new RestInfo<>(list); 57 | } 58 | 59 | /** 60 | * 测试事务回滚 61 | * @param userEntity 62 | * @return 63 | */ 64 | @PostMapping("/deleteUserWithTransaction") 65 | public RestInfo deleteWithTransaction(@RequestBody UserEntity userEntity){ 66 | userService.deleteWithTransaction(userEntity); 67 | return new RestInfo<>(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.common.Constant; 4 | import io.swagger.annotations.ApiOperation; 5 | import org.springframework.format.annotation.DateTimeFormat; 6 | import org.springframework.web.bind.annotation.*; 7 | import org.springframework.web.context.request.RequestContextHolder; 8 | import org.springframework.web.context.request.ServletRequestAttributes; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import javax.servlet.http.HttpSession; 14 | import java.io.*; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.Date; 17 | import java.util.UUID; 18 | 19 | /** 20 | * 演示时间类型,分布式session,文件上传下载 21 | * 22 | * @Author lian 23 | * @Date 2018/3/20 24 | */ 25 | @RestController // @RestController返回的是json数据,@Controller返回的是跳转控制(有模版引擎时使用) 26 | @RequestMapping("/demo") // 方法上的@RequestMapping用于区分不同的模块 27 | public class DemoController { 28 | 29 | @ApiOperation(value = "演示rest", notes = "此处表示get方法,用户的url就是/demo/getHello,得到的数据就是hello字符串") 30 | @GetMapping("/getHello") 31 | public String getHello() { 32 | return "hello"; 33 | } 34 | 35 | @ApiOperation(value = "演示http相关", notes = "") 36 | @GetMapping("/setSession") 37 | public String setSession(String key, String value) { 38 | //对于参数前面没有修饰的,都是url传值,参数是可选参数,当前端不传value时,value接受到的是null 39 | ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 40 | assert requestAttributes != null; 41 | HttpServletRequest request = requestAttributes.getRequest(); 42 | HttpServletResponse response = requestAttributes.getResponse(); 43 | HttpSession session = request.getSession(); 44 | session.setAttribute(key, value); 45 | assert response != null; 46 | response.setStatus(200); 47 | return "success"; 48 | } 49 | 50 | @GetMapping("/getSession") 51 | public String getSession(String key, HttpServletRequest request, HttpServletResponse response, HttpSession session) { 52 | //系统会自动获取requset,response,session 53 | return (String) session.getAttribute(key); 54 | } 55 | 56 | @ApiOperation(value = "演示传日期时间类型", notes = "演示传日期时间类型") 57 | @GetMapping("/time") 58 | public Date time(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date time) { 59 | return time; 60 | } 61 | 62 | @PutMapping(value = "/upFile") 63 | public String upFile(MultipartFile[] files) { 64 | //TODO 文件安全校验 65 | for (MultipartFile file : files) { 66 | String fileName = UUID.randomUUID().toString(); 67 | try { 68 | file.transferTo(new File(Constant.UP_FILE_ROOT_PATH + fileName)); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | return "fail"; 72 | } 73 | } 74 | return "success"; 75 | } 76 | 77 | @GetMapping(value = "/downFile") 78 | public void downFile(HttpServletResponse resp, String fileName) { 79 | //设置文件名 80 | String acName; 81 | acName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); 82 | resp.setHeader("Content-Disposition", "attachment; filename=" + acName);//以附件方式下载,防止浏览器直接打开 83 | FileInputStream inputStream = null; 84 | OutputStream os = null; 85 | try { 86 | inputStream = new FileInputStream(Constant.UP_FILE_ROOT_PATH + fileName); 87 | byte[] b = new byte[1024]; 88 | int i; 89 | StringBuilder stringBuilder = new StringBuilder(); 90 | os = resp.getOutputStream(); 91 | while ((i = inputStream.read(b)) > 0) { 92 | stringBuilder.append(new String(b, 0, i)); 93 | } 94 | byte[] bytes = stringBuilder.toString().getBytes(); 95 | os.write(bytes); 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | } finally { 99 | try { 100 | if (inputStream != null){ 101 | inputStream.close(); 102 | } 103 | if (os != null){ 104 | os.close(); 105 | } 106 | } catch (IOException ignored) { 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/MongoController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.domain.entity.OrderDoc; 4 | import com.lian.domain.mongorepository.OrderDocRepository; 5 | import com.lian.web.response.RestInfo; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | /** 14 | * @Author lian 15 | * @Date 2019-07-26 16 | */ 17 | @RestController 18 | @RequestMapping("/mongo") 19 | public class MongoController { 20 | @Autowired 21 | private OrderDocRepository orderDocRepository; 22 | 23 | @GetMapping("/list") 24 | public RestInfo getUserList() { 25 | List list = orderDocRepository.findAll(); 26 | return new RestInfo<>(list); 27 | } 28 | 29 | @GetMapping("/get/{id}") 30 | public RestInfo getUser(@PathVariable("id") String id) { 31 | Optional entityOption = orderDocRepository.findById(id); 32 | return new RestInfo<>(entityOption); 33 | } 34 | 35 | @PostMapping("/save") 36 | public RestInfo save(@RequestBody @Validated OrderDoc orderDoc) { 37 | return new RestInfo<>(orderDocRepository.save(orderDoc)); 38 | } 39 | 40 | @PostMapping("/del/{id}") 41 | public RestInfo del(@PathVariable("id") String id) { 42 | orderDocRepository.deleteById(id); 43 | return new RestInfo<>(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.domain.entity.OrderEntity; 4 | import com.lian.web.response.RestInfo; 5 | import com.lian.service.OrderService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.validation.annotation.Validated; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | @RestController 11 | @RequestMapping("/order") 12 | public class OrderController { 13 | @Autowired 14 | private OrderService orderService; 15 | 16 | @GetMapping("/list") 17 | public RestInfo listOrder() { 18 | return new RestInfo<>(orderService.list()); 19 | } 20 | 21 | @GetMapping("/get/{orderNo}") 22 | public RestInfo getOrder(@PathVariable("orderNo") Long orderNo) { 23 | OrderEntity entity = orderService.getOrder(orderNo); 24 | return new RestInfo<>(entity); 25 | } 26 | 27 | @PostMapping("/save") 28 | public RestInfo save(@RequestBody @Validated OrderEntity entity) { 29 | return new RestInfo<>(orderService.saveOrder(entity)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/RedirectController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | 6 | /** 7 | * 演示控制跳转 8 | * @Author lian 9 | * @Date 2018/7/5 10 | */ 11 | @Controller 12 | public class RedirectController { 13 | @GetMapping("/") 14 | public String index() { 15 | return "redirect:/hello"; 16 | } 17 | 18 | @GetMapping("/hello") 19 | public String list() { 20 | return "hello.html"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/RedisController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.common.pubsub.RedisReceiver; 4 | import com.lian.web.response.RestInfo; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.redis.core.StringRedisTemplate; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | /** 12 | * @Author lian 13 | * @Date 2019-07-29 14 | */ 15 | @RestController 16 | @RequestMapping("/redis") 17 | public class RedisController { 18 | @Autowired 19 | private StringRedisTemplate stringRedisTemplate; 20 | 21 | 22 | @PostMapping("/send") 23 | public RestInfo send(String msg){ 24 | stringRedisTemplate.convertAndSend(RedisReceiver.MESSAGE_CHANEL, msg); 25 | return new RestInfo<>(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/SecurityController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.domain.entity.UserEntity; 4 | import com.lian.web.response.RestInfo; 5 | import com.lian.service.UserService; 6 | import org.apache.shiro.authz.annotation.RequiresPermissions; 7 | import org.apache.shiro.authz.annotation.RequiresRoles; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.domain.Page; 10 | import org.springframework.data.domain.PageRequest; 11 | import org.springframework.data.domain.Pageable; 12 | import org.springframework.validation.annotation.Validated; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * 演示shiro权限注解控制 19 | * @Author lian 20 | * @Date 2018/3/20 21 | */ 22 | @RestController 23 | @RequestMapping("/security") 24 | public class SecurityController { 25 | @Autowired 26 | private UserService userService; 27 | 28 | @RequiresRoles("admin") 29 | @GetMapping("/list") 30 | public RestInfo getUserList() { 31 | List list = userService.findAll(); 32 | return new RestInfo<>(list); 33 | } 34 | 35 | @RequiresPermissions("user:read") 36 | @GetMapping("/get/{id}") 37 | public RestInfo getUser(@PathVariable("id") Long id) { 38 | UserEntity entity = userService.findById(id); 39 | return new RestInfo<>(entity); 40 | } 41 | 42 | @RequiresPermissions("user:save") 43 | @PostMapping("/save") 44 | public RestInfo save(@RequestBody @Validated UserEntity userEntity) { 45 | return new RestInfo<>(userService.save(userEntity)); 46 | } 47 | 48 | @RequiresPermissions("user:delete") 49 | @PostMapping("/delete/{id}") 50 | public RestInfo delete(@PathVariable Long id) { 51 | userService.deleteById(id); 52 | return new RestInfo(); 53 | } 54 | 55 | @RequiresPermissions("user:read") 56 | @GetMapping("/page") 57 | public RestInfo page(@RequestParam(value = "page", required = false, defaultValue = "1") Integer page, 58 | @RequestParam(value = "size", required = false, defaultValue = "10") Integer size) { 59 | Pageable pageable = PageRequest.of(page, size); 60 | Page list = userService.pageUser(pageable); 61 | return new RestInfo<>(list); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/UserController.java: -------------------------------------------------------------------------------- 1 | package com.lian.web; 2 | 3 | import com.lian.common.util.VerifyCodeUtil; 4 | import com.lian.domain.entity.UserEntity; 5 | import com.lian.web.response.MyException; 6 | import com.lian.web.response.RestInfo; 7 | import io.swagger.annotations.ApiOperation; 8 | import org.apache.shiro.SecurityUtils; 9 | import org.apache.shiro.authc.AuthenticationException; 10 | import org.apache.shiro.authc.IncorrectCredentialsException; 11 | import org.apache.shiro.authc.LockedAccountException; 12 | import org.apache.shiro.authc.UsernamePasswordToken; 13 | import org.apache.shiro.subject.Subject; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import javax.servlet.http.HttpSession; 19 | import java.io.IOException; 20 | import java.util.*; 21 | 22 | /** 23 | * 24 | * @Author lian 25 | * @Date 2018/3/20 26 | */ 27 | @RestController 28 | @RequestMapping("/user") 29 | public class UserController { 30 | 31 | @GetMapping(value = "/unAuth") 32 | public RestInfo unAuth() { 33 | Map map = new HashMap<>(2); 34 | map.put("code", "1000000"); 35 | map.put("msg", "未登录"); 36 | return new RestInfo<>(map); 37 | } 38 | 39 | @PostMapping(value = "/login") 40 | public RestInfo login(@RequestBody UserEntity userEntity) { 41 | Subject subject = SecurityUtils.getSubject(); 42 | UsernamePasswordToken token = new UsernamePasswordToken(userEntity.getUsername(), userEntity.getPassword()); 43 | Map result = new HashMap<>(8); 44 | try { 45 | subject.login(token); 46 | result.put("token", subject.getSession().getId()); 47 | result.put("msg", "登录成功"); 48 | } catch (IncorrectCredentialsException e) { 49 | result.put("msg", "密码错误"); 50 | } catch (LockedAccountException e) { 51 | result.put("msg", "登录失败,该用户已被冻结"); 52 | } catch (AuthenticationException e) { 53 | result.put("msg", "该用户不存在"); 54 | } catch (Exception e) { 55 | result.put("msg", e.getMessage()); 56 | e.printStackTrace(); 57 | } 58 | return new RestInfo<>(result); 59 | } 60 | 61 | @ApiOperation("获取验证码图片") 62 | @GetMapping("/getVerifyImg") 63 | public void getVerifyImg(HttpServletRequest request, HttpServletResponse response) throws IOException { 64 | response.setHeader("Pragma", "No-cache"); 65 | response.setHeader("Cache-Control", "no-cache"); 66 | response.setDateHeader("Expires", 0); 67 | response.setContentType("image/jpeg"); 68 | //生成随机字串,参数1是长度,参数2是从中候选字符,可以设置成-—*zH等 69 | String verifyCode = VerifyCodeUtil.generateVerifyCode(4, "1234567890"); 70 | HttpSession session = request.getSession(true); 71 | //删除以前的 72 | session.removeAttribute("verCode"); 73 | //存入会话session 74 | session.setAttribute("verCode", verifyCode.toLowerCase()); 75 | //生成图片 76 | int w = 100, h = 30; 77 | VerifyCodeUtil.outputImage(w, h, response.getOutputStream(), verifyCode); 78 | } 79 | 80 | @ApiOperation("校验验证码") 81 | @GetMapping("/validVerifyImg") 82 | public RestInfo validVerifyImg(HttpServletRequest request, @RequestParam String code) { 83 | //存入会话session 84 | HttpSession session = request.getSession(); 85 | if (!code.equals(session.getAttribute("verCode"))) { 86 | throw MyException.notAllowed(null, "验证码错误"); 87 | } 88 | //验证成功后移除 89 | session.removeAttribute("verCode"); 90 | return new RestInfo(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/param/ParamFilter.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.param; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import javax.servlet.*; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.PrintWriter; 11 | import java.util.Enumeration; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | //@WebFilter(urlPatterns = "/api/*",filterName = "param") 16 | @Slf4j(topic = "print-param:") 17 | public class ParamFilter implements Filter { 18 | @Override 19 | public void init(FilterConfig filterConfig) { 20 | } 21 | 22 | @Override 23 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 24 | HttpServletRequest r = (HttpServletRequest) servletRequest; 25 | String path = r.getQueryString(); 26 | if (path == null) { 27 | Map map = new HashMap<>(); 28 | Enumeration headerNames = ((HttpServletRequest) servletRequest).getHeaderNames(); 29 | while (headerNames.hasMoreElements()) {//循环遍历Header中的参数,把遍历出来的参数放入Map中 30 | String key = (String) headerNames.nextElement(); 31 | String value = ((HttpServletRequest) servletRequest).getHeader(key); 32 | map.put(key, value); 33 | } 34 | path = map.toString(); 35 | } 36 | String url = r.getRequestURI(); 37 | ParamRequestWrapper requestWrapper; 38 | String replaceUrl = url.replaceAll("/", "").trim(); 39 | log.info("request url:" + url + " & queryString:" + path); 40 | 41 | ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) servletResponse); 42 | requestWrapper = new ParamRequestWrapper((HttpServletRequest) servletRequest); 43 | try { 44 | Map map = servletRequest.getParameterMap(); 45 | log.info("request parameter map:" + map); 46 | BufferedReader bufferedReader = requestWrapper.getReader(); 47 | String line; 48 | StringBuilder sb = new StringBuilder(); 49 | while ((line = bufferedReader.readLine()) != null) { 50 | sb.append(line); 51 | } 52 | log.info("request header:" + sb.toString()); 53 | } catch (Exception e) { 54 | log.warn("request error:", e); 55 | } 56 | filterChain.doFilter(requestWrapper, responseWrapper); 57 | 58 | String result = new String(responseWrapper.getResponseData()); 59 | servletResponse.setContentLength(-1);//解决可能在运行的过程中页面只输出一部分 60 | servletResponse.setCharacterEncoding("UTF-8"); 61 | PrintWriter out = servletResponse.getWriter(); 62 | out.write(result); 63 | out.flush(); 64 | out.close(); 65 | log.info("response return data:" + result); 66 | log.info("response url:" + url + " httpStatus:" + ((HttpServletResponse) servletResponse).getStatus() + ""); 67 | } 68 | 69 | @Override 70 | public void destroy() { 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/param/ParamRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.param; 2 | 3 | import javax.servlet.ReadListener; 4 | import javax.servlet.ServletInputStream; 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletRequestWrapper; 7 | import java.io.*; 8 | 9 | public class ParamRequestWrapper extends HttpServletRequestWrapper { 10 | private final byte[] body; 11 | 12 | ParamRequestWrapper(HttpServletRequest request) throws IOException { 13 | super(request); 14 | body = toByteArray(request.getInputStream()); 15 | } 16 | 17 | @Override 18 | public BufferedReader getReader() throws IOException { 19 | return new BufferedReader(new InputStreamReader(getInputStream())); 20 | } 21 | 22 | @Override 23 | public ServletInputStream getInputStream() throws IOException { 24 | final ByteArrayInputStream bais = new ByteArrayInputStream(body); 25 | return new ServletInputStream() { 26 | 27 | public boolean isFinished() { 28 | return false; 29 | } 30 | 31 | public boolean isReady() { 32 | return false; 33 | } 34 | 35 | @Override 36 | public void setReadListener(ReadListener readListener) { 37 | 38 | } 39 | 40 | @Override 41 | public int read() throws IOException { 42 | return bais.read(); 43 | } 44 | }; 45 | } 46 | 47 | private byte[] toByteArray(InputStream input) throws IOException { 48 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 49 | byte[] buffer = new byte[4096]; 50 | int n = 0; 51 | while (-1 != (n = input.read(buffer))) { 52 | output.write(buffer, 0, n); 53 | } 54 | return output.toByteArray(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/param/ResponseWrapper.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.param; 2 | 3 | import javax.servlet.ServletOutputStream; 4 | import javax.servlet.WriteListener; 5 | import javax.servlet.http.HttpServletResponse; 6 | import javax.servlet.http.HttpServletResponseWrapper; 7 | import java.io.*; 8 | 9 | public class ResponseWrapper extends HttpServletResponseWrapper { 10 | private ByteArrayOutputStream buffer = null;//输出到byte array 11 | private ServletOutputStream out = null; 12 | private PrintWriter writer = null; 13 | 14 | ResponseWrapper(HttpServletResponse resp) throws IOException { 15 | super(resp); 16 | buffer = new ByteArrayOutputStream();// 真正存储数据的流 17 | out = new WrapperOutputStream(buffer); 18 | writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding())); 19 | } 20 | 21 | @Override 22 | public ServletOutputStream getOutputStream() throws IOException { 23 | return out; 24 | } 25 | 26 | @Override 27 | public PrintWriter getWriter() throws UnsupportedEncodingException { 28 | return writer; 29 | } 30 | 31 | @Override 32 | public void flushBuffer() throws IOException { 33 | if (out != null) { 34 | out.flush(); 35 | } 36 | if (writer != null) { 37 | writer.flush(); 38 | } 39 | } 40 | 41 | @Override 42 | public void reset() { 43 | buffer.reset(); 44 | } 45 | 46 | public byte[] getResponseData() throws IOException { 47 | flushBuffer(); 48 | return buffer.toByteArray(); 49 | } 50 | 51 | private class WrapperOutputStream extends ServletOutputStream { 52 | private ByteArrayOutputStream bos; 53 | 54 | public WrapperOutputStream(ByteArrayOutputStream stream) throws IOException { 55 | bos = stream; 56 | } 57 | 58 | @Override 59 | public void write(int b) throws IOException { 60 | bos.write(b); 61 | } 62 | 63 | @Override 64 | public void write(byte[] b) throws IOException { 65 | bos.write(b, 0, b.length); 66 | } 67 | 68 | @Override 69 | public boolean isReady() { 70 | return false; 71 | } 72 | 73 | @Override 74 | public void setWriteListener(WriteListener writeListener) { 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/response/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.response; 2 | 3 | import org.springframework.web.bind.annotation.ControllerAdvice; 4 | import org.springframework.web.bind.annotation.ExceptionHandler; 5 | import org.springframework.web.bind.annotation.ResponseBody; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | /** 10 | * @Author lian 11 | * @Date 2018/3/20 12 | */ 13 | @ControllerAdvice 14 | public class GlobalExceptionHandler { 15 | @ExceptionHandler(value = MyException.class) 16 | @ResponseBody 17 | public RestInfo jsonErrorHandler(HttpServletRequest req, Throwable t) { 18 | MyException myException = MyException.wrapIfNeeded(t); 19 | RestInfo r = new RestInfo<>(); 20 | r.setMessage(myException.getMessage()); 21 | r.setCode(myException.getCode()); 22 | r.setData("fail"); 23 | r.setUrl(req.getRequestURI()); 24 | return r; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/response/MyException.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.response; 2 | 3 | /** 4 | * @Author lian 5 | * @Date 2018/3/20 6 | */ 7 | 8 | public class MyException extends RuntimeException{ 9 | //TODO 自定义状态码 10 | public static final Integer PARAM_ERROR = 101; 11 | public static final Integer INPUT_ERROR = 102; 12 | public static final Integer NOT_ALLOWED = 103; 13 | public static final Integer INTERNAL_ERROR = 500; 14 | 15 | 16 | private Integer code; 17 | private String message; 18 | private String url; 19 | 20 | public MyException() { 21 | } 22 | 23 | private MyException(Throwable throwable, Integer code, String message) { 24 | super(throwable); 25 | this.code = code; 26 | this.message = message; 27 | } 28 | public static MyException wrapIfNeeded(Throwable t) { 29 | if (t instanceof MyException) { 30 | return (MyException) t; 31 | } else { //TODO 这里可以捕获各种异常,如主键冲突异常,权限验证异常等等然后转换成自定义通知。 32 | //所有自定义的异常都捕获不到,说明可能是系统内部错误导致的。 33 | return internalError(t, t.getMessage()); 34 | } 35 | } 36 | 37 | //101 输入参数不规范 38 | public static MyException inputError(Throwable ex, String detail) { 39 | return new MyException(ex, MyException.INPUT_ERROR, detail); 40 | } 41 | 42 | //103 操作不允许 43 | public static MyException notAllowed(Throwable ex, String detail) { 44 | return new MyException(ex, MyException.NOT_ALLOWED, detail); 45 | } 46 | 47 | // 5开头的为系统内部错误,不需要程序员去捕获5开头的异常,客户端返回值出现该异常一定是开发者bug。 48 | public static MyException internalError(Throwable ex, String detail) { 49 | return new MyException(ex, MyException.INTERNAL_ERROR, detail); 50 | } 51 | 52 | public Integer getCode() { 53 | return code; 54 | } 55 | 56 | public void setCode(Integer code) { 57 | this.code = code; 58 | } 59 | 60 | @Override 61 | public String getMessage() { 62 | return message; 63 | } 64 | 65 | public void setMessage(String message) { 66 | this.message = message; 67 | } 68 | 69 | public String getUrl() { 70 | return url; 71 | } 72 | 73 | public void setUrl(String url) { 74 | this.url = url; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/lian/web/response/RestInfo.java: -------------------------------------------------------------------------------- 1 | package com.lian.web.response; 2 | 3 | /** 4 | * @Author lian 5 | * @Date 2018/3/20 6 | */ 7 | public class RestInfo { 8 | private final Integer OK = 0; 9 | private final String SUCCESS = "success"; 10 | 11 | private Integer code; 12 | private String message; 13 | private String url; 14 | private T data; 15 | 16 | public RestInfo() { 17 | } 18 | 19 | public RestInfo(T data) { 20 | if (data != null){ 21 | this.data = data; 22 | } 23 | this.code = OK; 24 | this.message = SUCCESS; 25 | } 26 | 27 | public Integer getCode() { 28 | return code; 29 | } 30 | 31 | public void setCode(Integer code) { 32 | this.code = code; 33 | } 34 | 35 | public String getMessage() { 36 | return message; 37 | } 38 | 39 | public void setMessage(String message) { 40 | this.message = message; 41 | } 42 | 43 | public String getUrl() { 44 | return url; 45 | } 46 | 47 | public void setUrl(String url) { 48 | this.url = url; 49 | } 50 | 51 | public T getData() { 52 | return data; 53 | } 54 | 55 | public void setData(T data) { 56 | this.data = data; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | ##服务端口和路径基本配置 2 | server.port=8080 3 | server.servlet.context-path=/api 4 | spring.aop.proxy-target-class=true 5 | ##shiro未登陆默认跳转位置 6 | shiro.loginUrl=/user/unAuth 7 | 8 | ##数据源,数据库主从读写分离,分库分表配置,druid属性配置 9 | spring.jpa.show-sql=true 10 | sharding.jdbc.datasource.names=master,slave1,slave2 11 | 12 | sharding.jdbc.datasource.master.type=com.alibaba.druid.pool.DruidDataSource 13 | sharding.jdbc.datasource.master.driver-class-name=com.mysql.jdbc.Driver 14 | sharding.jdbc.datasource.master.url=jdbc:mysql://localhost:3306/demo_0 15 | sharding.jdbc.datasource.master.username=root 16 | sharding.jdbc.datasource.master.password=123456 17 | 18 | sharding.jdbc.datasource.slave1.type=com.alibaba.druid.pool.DruidDataSource 19 | sharding.jdbc.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver 20 | sharding.jdbc.datasource.slave1.url=jdbc:mysql://localhost:3306/demo_1 21 | sharding.jdbc.datasource.slave1.username=root 22 | sharding.jdbc.datasource.slave1.password=123456 23 | 24 | sharding.jdbc.datasource.slave2.type=com.alibaba.druid.pool.DruidDataSource 25 | sharding.jdbc.datasource.slave2.driver-class-name=com.mysql.jdbc.Driver 26 | sharding.jdbc.datasource.slave2.url=jdbc:mysql://localhost:3306/demo_2 27 | sharding.jdbc.datasource.slave2.username=root 28 | sharding.jdbc.datasource.slave2.password=123456 29 | 30 | #sharding.jdbc.config.masterslave.name=ms 31 | #sharding.jdbc.config.masterslave.master-data-source-name=master 32 | #sharding.jdbc.config.masterslave.slave-data-source-names=slave1,slave2 33 | 34 | sharding.jdbc.config.sharding.tables.t_order.actual-data-nodes=master.t_order$->{0..1} 35 | sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.sharding-column=order_no 36 | sharding.jdbc.config.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order$->{order_no % 2} 37 | #sharding.jdbc.config.sharding.tables.t_order.key-generator-column-name=order_no 38 | 39 | 40 | ##在线文档配置 41 | swagger.conf.name=demo 42 | swagger.conf.url=http://demo.com 43 | swagger.conf.email=demo@smtp.com 44 | 45 | ##日志配置 46 | logging.path=${user.home}/log/demo.log 47 | logging. 48 | logging.pattern.file=${user.home}/log/demo-%d{yyyy-MM-dd}.log 49 | logging.pattern.console=%red(%d{HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{5}) - %cyan(%msg%n) 50 | logging.level.root=INFO 51 | logging.level.org.springframework.web=INFO 52 | logging.level.org.hibernate=INFO 53 | 54 | ## redis配置 55 | spring.redis.database=0 56 | spring.redis.password=123456 57 | spring.redis.host=localhost 58 | spring.redis.port=6379 59 | # 最大活跃连接数,负数为不限制 60 | spring.redis.lettuce.pool.max-active=-1 61 | # 等待可用连接的最大时间,负数为不限制 62 | spring.redis.lettuce.pool.max-wait=-1ms 63 | # 最大空闲连接数 64 | spring.redis.lettuce.pool.max-idle=100 65 | # 最小空闲连接数 66 | spring.redis.lettuce.pool.min-idle=0 67 | spring.cache.redis.key-prefix=- 68 | 69 | ##服务器状态监控配置 70 | javamelody.enabled=true 71 | javamelody.excluded-datasources=secretSource,topSecretSource 72 | javamelody.init-parameters.log=true 73 | javamelody.init-parameters.url-exclude-pattern=(/webjars/.*|/css/.*|/images/.*|/fonts/.*|/js/.*) 74 | javamelody.init-parameters.advisor-auto-proxy-creator-enabled=false 75 | javamelody.init-parameters.storage-directory=${user.home}/melody-log/ 76 | javamelody.init-parameters.authorized-users=admin:pwd 77 | javamelody.init-parameters.javamelody.monitoring-path=/monitoring 78 | 79 | ##mongo配置 80 | #mongodb 81 | spring.data.mongodb.host=127.0.0.1 82 | spring.data.mongodb.port=27017 83 | spring.data.mongodb.database=demo 84 | #spring.data.mongodb.username=dev 85 | #spring.data.mongodb.password=123456 86 | 87 | ##rabitmq配置 88 | #spring.rabbitmq.host=127.0.0.1 89 | #spring.rabbitmq.port=5672 90 | ##spring.rabbitmq.username=admin 91 | ##spring.rabbitmq.password=123456 92 | #spring.rabbitmq.template.retry.enabled=true 93 | #spring.rabbitmq.template.retry.initial-interval=2s 94 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | server.port=80 2 | server.servlet.context-path=/api 3 | 4 | spring.datasource.druid.url=jdbc:mysql://localhost:3306/xyb?characterEncoding=utf8&&useAffectedRows=true 5 | spring.datasource.druid.username= 6 | spring.datasource.druid.password= 7 | spring.datasource.driver-class-name=com.mysql.jdbc.Driver 8 | 9 | spring.jpa.properties.hibernate.hbm2ddl.auto=update 10 | 11 | 12 | swagger.conf.name=xyb 13 | swagger.conf.url=http://demo.com 14 | swagger.conf.email=xyb@smtp.com 15 | 16 | logging.path=${user.home}/log/demo.log 17 | logging.level.root=INFO 18 | logging.level.org.springframework.web=INFO 19 | logging.level.org.hibernate=ERROR 20 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev -------------------------------------------------------------------------------- /src/main/resources/static/hi.txt: -------------------------------------------------------------------------------- 1 | hi,i am here -------------------------------------------------------------------------------- /src/main/resources/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | hello world! 9 | 10 | -------------------------------------------------------------------------------- /src/test/java/test/lian/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package test.lian; 2 | 3 | import com.lian.AppApplication; 4 | import com.lian.web.response.MyException; 5 | import org.junit.*; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | 10 | @RunWith(SpringRunner.class) 11 | @SpringBootTest(classes = AppApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK) 12 | public class UserServiceTest { 13 | @BeforeClass 14 | public void beforeClass(){ 15 | System.out.println("before class"); 16 | } 17 | 18 | @Before 19 | public void initData(){ 20 | System.out.println("before test.I will init data"); 21 | } 22 | 23 | @Test(timeout = 1000,expected = MyException.class) 24 | public void test(){ 25 | System.out.println("the test method"); 26 | } 27 | 28 | @After 29 | public void delData(){ 30 | System.out.println("after test.I will delete data"); 31 | } 32 | 33 | @AfterClass 34 | public void afterClass(){ 35 | System.out.println("after class"); 36 | } 37 | 38 | 39 | /** 40 | * 系统在测试时会自动忽略掉该注解修饰方法 41 | */ 42 | @Ignore("not ready yet") 43 | public void notReadyYet(){ 44 | int i= 3 / 0; 45 | } 46 | 47 | 48 | } 49 | --------------------------------------------------------------------------------