├── .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