├── .gitignore ├── README.md ├── pom.xml ├── spring-eureka-demo ├── eureka-consumer-service │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── eureka │ │ │ ├── ConsumerApplication.java │ │ │ ├── config │ │ │ └── WebConfig.java │ │ │ └── controller │ │ │ └── RestTemplateController.java │ │ └── resources │ │ └── application.yml ├── eureka-producer-service │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── eureka │ │ │ ├── ProducerApplication.java │ │ │ └── controller │ │ │ └── HelloController.java │ │ └── resources │ │ └── application.yml ├── eureka-server-duplicate │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── eureka │ │ │ └── EurekaDuplicateServer.java │ │ └── resources │ │ └── application.yml └── eureka-server │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── eureka │ │ └── EurekaServer.java │ └── resources │ └── application.yml ├── spring-mongodb-demo ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── mongodb │ │ │ ├── SpringMongodbApplication.java │ │ │ ├── entity │ │ │ ├── HistoryGoods.java │ │ │ └── User.java │ │ │ └── service │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ └── UserServiceImpl.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── example │ └── mongodb │ ├── MongoMergeTest.java │ ├── MongoQueryTest.java │ └── UserHistoryTest.java ├── spring-rabbitmq-demo ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── rabbitmq │ │ │ ├── SpringRabbitmqApplication.java │ │ │ ├── auto │ │ │ ├── component │ │ │ │ ├── RabbitConsumer.java │ │ │ │ ├── RabbitDirectConsumer.java │ │ │ │ ├── RabbitFanoutConsumer.java │ │ │ │ ├── RabbitJsonConsumer.java │ │ │ │ ├── RabbitProduce.java │ │ │ │ └── RabbitTopicConsumer.java │ │ │ ├── config │ │ │ │ ├── JacksonConfig.java │ │ │ │ └── RabbitmqConfig.java │ │ │ └── entity │ │ │ │ ├── Client.java │ │ │ │ └── User.java │ │ │ └── prototype │ │ │ ├── Consumer.java │ │ │ └── Producer.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── org │ └── example │ └── rabbitmq │ └── auto │ └── component │ └── RabbitProduceTest.java ├── spring-security-demo ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── example │ │ │ └── security │ │ │ ├── SpringSecurityApplication.java │ │ │ └── auth │ │ │ ├── api │ │ │ ├── AuthController.java │ │ │ └── TestController.java │ │ │ ├── bo │ │ │ ├── AccessToken.java │ │ │ ├── ApiPage.java │ │ │ ├── ApiResult.java │ │ │ ├── LoginInfo.java │ │ │ └── PermissionInfoBO.java │ │ │ ├── cache │ │ │ ├── Cache.java │ │ │ └── CaffeineCache.java │ │ │ ├── component │ │ │ ├── AccessDecisionProcessor.java │ │ │ ├── InitProcessor.java │ │ │ ├── JwtAuthenticationTokenFilter.java │ │ │ ├── RestAuthenticationEntryPoint.java │ │ │ └── RestfulAccessDeniedHandler.java │ │ │ ├── config │ │ │ ├── JacksonConfig.java │ │ │ ├── MyConfig.java │ │ │ └── SpringSecurityConfig.java │ │ │ ├── constant │ │ │ ├── ApiStatus.java │ │ │ ├── CacheName.java │ │ │ └── RoleEnum.java │ │ │ ├── entity │ │ │ ├── PermissionInfo.java │ │ │ ├── RoleInfo.java │ │ │ ├── UserDetail.java │ │ │ └── UserInfo.java │ │ │ ├── mapper │ │ │ ├── PermissionMapper.java │ │ │ ├── RoleInfoMapper.java │ │ │ └── UserMapper.java │ │ │ ├── properties │ │ │ └── JwtProperties.java │ │ │ ├── provider │ │ │ ├── AuthProvider.java │ │ │ └── JwtProvider.java │ │ │ ├── service │ │ │ ├── AuthService.java │ │ │ ├── PermissionService.java │ │ │ ├── RoleInfoService.java │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ │ ├── AuthServiceImpl.java │ │ │ │ ├── CustomUserDetailsService.java │ │ │ │ ├── PermissionServiceImpl.java │ │ │ │ ├── RoleInfoServiceImpl.java │ │ │ │ └── UserServiceImpl.java │ │ │ └── util │ │ │ └── JacksonUtil.java │ └── resources │ │ ├── application.yml │ │ ├── doc │ │ └── spring-boot-learning.sql │ │ └── mapper │ │ ├── PermissionMapper.xml │ │ └── RoleInfoMapper.xml │ └── test │ └── java │ └── org │ └── example │ └── security │ └── UserServiceTest.java ├── spring-websocket ├── .gitignore ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── springwebsocket │ │ │ ├── SpringWebsocketApplication.java │ │ │ ├── TestController.java │ │ │ ├── j2ee │ │ │ ├── WebSocketConfig.java │ │ │ └── WebSocketServer.java │ │ │ ├── socketio │ │ │ ├── SocketIoConfig.java │ │ │ └── SocketIoHandle.java │ │ │ └── spring │ │ │ ├── SpringSocketConfig.java │ │ │ └── SpringSocketHandle.java │ └── resources │ │ └── templates │ │ ├── J2eeIndex.html │ │ ├── SocketIoIndex.html │ │ └── SpringIndex.html │ └── test │ └── java │ └── com │ └── example │ └── springwebsocket │ └── SpringWebsocketApplicationTests.java └── spring-zookeeper-demo ├── zookeeper-consumer-service ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── example │ │ └── zookeeper │ │ ├── ZookeeperConsumerApplication.java │ │ ├── config │ │ └── WebConfig.java │ │ └── controller │ │ └── RestTemplateController.java │ └── resources │ └── application.yml └── zookeeper-producer-service ├── pom.xml └── src └── main ├── java └── com │ └── example │ └── zookeeper │ ├── ZookeeperProducerApplication.java │ └── controller │ └── HelloController.java └── resources └── application.yml /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | .mvn/** 5 | !**/src/main/** 6 | !**/src/test/** 7 | log/ 8 | logs/ 9 | *.log 10 | mvnw 11 | mvnw.cmd 12 | 13 | ### STS ### 14 | .apt_generated 15 | .classpath 16 | .factorypath 17 | .project 18 | .settings 19 | .springBeans 20 | .sts4-cache 21 | 22 | ### IntelliJ IDEA ### 23 | .idea 24 | *.iws 25 | *.iml 26 | *.ipr 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | build/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Spring Boot Learning Demo 2 | 3 | [![Spring Boot 2.3](https://gitee.com/he-erduo/codes/98pgj1wezby2dntk6qvx082/raw?blob_name=SpringBoot-2.3-blue.svg)](https://spring.io/projects/spring-boot) 4 | [![JDK 1.8](https://gitee.com/he-erduo/codes/98pgj1wezby2dntk6qvx082/raw?blob_name=JDK-1.8-brightgreen.svg)](https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html) 5 | [![Gitee](https://gitee.com/he-erduo/codes/98pgj1wezby2dntk6qvx082/raw?blob_name=Gitee-start-red.svg)](https://gitee.com/he-erduo/spring-boot-learning-demo) 6 | [![Github](https://gitee.com/he-erduo/codes/98pgj1wezby2dntk6qvx082/raw?blob_name=Github-Start+-yellow.svg)](https://github.com/rookie-ricardo/spring-boot-learning-demo) 7 | 8 | 以 Spring Boot 为基础,构建多种Java项目实例,以实用为目的帮助大家快速上手 Spring Boot 开发,积累开发经验。 9 |
10 | 11 | ## Spring Boot Learning List 12 | 13 | * [spring-security-demo](/spring-security-demo):Spring Security + JWT 实现认证与动态鉴权。 14 | 15 | * **相关文章:**[SpringSecurity认证流程解析](https://juejin.im/post/5f01d648e51d45346a3ed1b7) 16 | * **相关文章:**[SpringSecurity动态鉴权解析](https://juejin.im/post/5f01dcb7f265da22a8514d0d) 17 | * **相关文章:**[SpringSecurity启动流程解析](https://juejin.im/post/5f0e75e36fb9a07e5a1c44aa) 18 | * **相关文章:**[SpringSecurity入口详解](https://juejin.cn/post/6960487717452906503) 19 | 20 | * [spring-rabbitmq-demo](/spring-rabbitmq-demo):Spring Boot + RabbitMQ 实现生产者消费者。 21 | 22 | * **相关文章:**[没用过消息队列?一文带你体验RabbitMQ收发消息](https://juejin.im/post/6856571028496351239) 23 | * **相关文章:**[刚体验完RabbitMQ?一文带你SpringBoot+RabbitMQ方式收发消息](https://juejin.im/post/6859152029823008781) 24 | * **相关文章:**[上手了RabbitMQ?再来看看它的交换机(Exchange)吧](https://juejin.im/post/6861959704705237000/) 25 | * **相关文章:**[RabbitMQ高级之如何保证消息可靠性?](https://juejin.im/post/6862875289786662926) 26 | * **相关文章:**[RabbitMQ高级之消息限流与延时队列](https://juejin.im/post/6864360098077081613) 27 | 28 | * [spring-websocket](/spring-websocket):Spring Boot / J2EE + WebSocket 实现常用功能。 29 | 30 | * **相关文章:**[一文搞懂四种 WebSocket 使用方式](https://juejin.cn/post/7095940082187632677) 31 | --- 32 | 33 | 注:项目相关数据库文件都放在demo下的resource下的doc文件下。 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | spring-boot-learning 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | spring-security-demo 13 | spring-rabbitmq-demo 14 | spring-mongodb-demo 15 | spring-eureka-demo/eureka-server 16 | spring-eureka-demo/eureka-server-duplicate 17 | spring-eureka-demo/eureka-producer-service 18 | spring-eureka-demo/eureka-consumer-service 19 | spring-zookeeper-demo/zookeeper-consumer-service 20 | spring-zookeeper-demo/zookeeper-producer-service 21 | 22 | 23 | 24 | 25 | UTF-8 26 | 1.8 27 | ${utf8} 28 | ${utf8} 29 | 1.8 30 | 1.8 31 | 2.3.0.RELEASE 32 | 3.3.0 33 | 5.1.47 34 | 5.1.0 35 | 1.18.10 36 | 1.7 37 | 3.8.6 38 | 3.8.1 39 | Hoxton.SR8 40 | 2.2.1.RELEASE 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-dependencies 48 | ${spring-boot.version} 49 | pom 50 | import 51 | 52 | 53 | 54 | org.springframework.cloud 55 | spring-cloud-dependencies 56 | ${spring.cloud-version} 57 | pom 58 | import 59 | 60 | 61 | 62 | com.alibaba.cloud 63 | spring-cloud-alibaba-dependencies 64 | ${spring.cloud.alibaba-version} 65 | pom 66 | import 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-devtools 75 | runtime 76 | true 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-configuration-processor 81 | true 82 | 83 | 84 | 85 | 86 | cn.hutool 87 | hutool-all 88 | ${hutool.version} 89 | 90 | 91 | 92 | org.projectlombok 93 | lombok 94 | ${lombok.version} 95 | provided 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-compiler-plugin 106 | 107 | ${java.version} 108 | ${java.version} 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | aliyun 118 | aliyun-maven 119 | http://maven.aliyun.com/nexus/content/groups/public/ 120 | 121 | 122 | spring-milestones 123 | Spring Milestones 124 | https://maven.aliyun.com/repository/spring 125 | 126 | 127 | central 128 | maven-central 129 | http://central.maven.org/maven2/ 130 | 131 | 132 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-consumer-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | eureka-consumer-service 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-eureka-client 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-consumer-service/src/main/java/com/example/eureka/ConsumerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | 7 | @EnableEurekaClient 8 | @SpringBootApplication 9 | public class ConsumerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ConsumerApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-consumer-service/src/main/java/com/example/eureka/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka.config; 2 | 3 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @Configuration 9 | public class WebConfig { 10 | 11 | @Bean 12 | @LoadBalanced 13 | public RestTemplate restTemplate(){ 14 | return new RestTemplate(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-consumer-service/src/main/java/com/example/eureka/controller/RestTemplateController.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | @RestController 10 | public class RestTemplateController { 11 | @Autowired 12 | private RestTemplate restTemplate; 13 | private static final String host = "http://eureka-producer-service"; 14 | 15 | @GetMapping("/consumer/rest") 16 | public String test(){ 17 | return restTemplate.getForObject(host+"/producer/hello", String.class); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-consumer-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8005 3 | spring: 4 | application: 5 | name: eureka-consumer-service 6 | eureka: 7 | instance: 8 | instance-id: consumer-service:${server.port} 9 | # 显示IP地址 10 | prefer-ip-address: true 11 | # 客户端心跳发送频率 12 | # lease-renewal-interval-in-seconds: 1 13 | # 服务挂机X秒后自动剔除 14 | # lease-expiration-duration-in-seconds: 5 15 | client: 16 | # APP节点注册 默认true 17 | register-with-eureka: true 18 | # APP节点检索 默认true 19 | fetch-registry: true 20 | service-url: 21 | # 设置Eureka-Service地址 22 | # defaultZone: http://localhost:8001/eureka/ 23 | defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/ 24 | 25 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-producer-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | eureka-producer-service 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-eureka-client 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-producer-service/src/main/java/com/example/eureka/ProducerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6 | 7 | @EnableEurekaClient 8 | @SpringBootApplication 9 | public class ProducerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ProducerApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-producer-service/src/main/java/com/example/eureka/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class HelloController { 9 | @Value("${server.port}") 10 | private String port; 11 | 12 | @GetMapping("/producer/hello") 13 | public String test(){ 14 | return "Hello World : " + port; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-producer-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8003 3 | spring: 4 | application: 5 | name: eureka-producer-service 6 | eureka: 7 | instance: 8 | instance-id: producer-service:${server.port} 9 | # 显示IP地址 10 | prefer-ip-address: true 11 | # 客户端心跳发送频率 12 | # lease-renewal-interval-in-seconds: 1 13 | # 服务挂机X秒后自动剔除 14 | # lease-expiration-duration-in-seconds: 5 15 | client: 16 | # APP节点注册 默认true 17 | register-with-eureka: true 18 | # APP节点检索 默认true 19 | fetch-registry: true 20 | service-url: 21 | # 设置Eureka-Service地址 22 | # defaultZone: http://localhost:8001/eureka/ 23 | defaultZone: http://localhost:8001/eureka/,http://localhost:8002/eureka/ 24 | 25 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server-duplicate/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | eureka-server-duplicate 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-eureka-server 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server-duplicate/src/main/java/com/example/eureka/EurekaDuplicateServer.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @EnableEurekaServer 8 | @SpringBootApplication 9 | public class EurekaDuplicateServer { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaDuplicateServer.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server-duplicate/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8002 3 | eureka: 4 | instance: 5 | # Eureka实例名称 6 | hostname: eureka-server 7 | client: 8 | # 服务端不需要注册自己 9 | register-with-eureka: false 10 | # 服务端不需要检索服务 11 | fetch-registry: false 12 | service-url: 13 | # 设置Eureka-Service的本机地址 客户端访问Eureka服务就访问此地址 14 | # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 15 | # Eureka集群进行互相注册 16 | defaultZone: http://localhost:8001/eureka/ 17 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | eureka-server 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-netflix-eureka-server 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server/src/main/java/com/example/eureka/EurekaServer.java: -------------------------------------------------------------------------------- 1 | package com.example.eureka; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @EnableEurekaServer 8 | @SpringBootApplication 9 | public class EurekaServer { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaServer.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-eureka-demo/eureka-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8001 3 | eureka: 4 | instance: 5 | # Eureka实例名称 6 | hostname: eureka-server 7 | client: 8 | # 服务端不需要注册自己 9 | register-with-eureka: false 10 | # 服务端不需要检索服务 11 | fetch-registry: false 12 | service-url: 13 | # 设置Eureka-Service的本机地址 客户端访问Eureka服务就访问此地址 14 | # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 15 | # Eureka集群进行互相注册 16 | defaultZone: http://localhost:8002/eureka/ 17 | -------------------------------------------------------------------------------- /spring-mongodb-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | spring-boot-learning 6 | org.example 7 | 1.0-SNAPSHOT 8 | 9 | 4.0.0 10 | 11 | 12 | spring-mongodb-demo 13 | 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-mongodb 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-test 24 | test 25 | 26 | 27 | org.junit.vintage 28 | junit-vintage-engine 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/java/com/example/mongodb/SpringMongodbApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringMongodbApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringMongodbApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/java/com/example/mongodb/entity/HistoryGoods.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.UUID; 6 | 7 | @Data 8 | public class HistoryGoods { 9 | private String goodsId = UUID.randomUUID().toString(); 10 | private String goodsTitle = "小米智能手机" + this.goodsId; 11 | private Integer goodsPrice = 249900; 12 | private String goodsPicture = "/data/goods/picture"; 13 | } 14 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/java/com/example/mongodb/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb.entity; 2 | 3 | import lombok.Data; 4 | import org.springframework.data.annotation.Id; 5 | import org.springframework.data.mongodb.core.mapping.Document; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.List; 9 | import java.util.UUID; 10 | 11 | /** 12 | *

13 | * 用户对象 14 | *

15 | * 16 | * @author 和耳朵 17 | * @since 2020-09-01 18 | */ 19 | @Data 20 | @Document(collection = "user") 21 | public class User { 22 | 23 | @Id 24 | private String id = UUID.randomUUID().toString(); 25 | 26 | private String username = "和耳朵"; 27 | 28 | private String password = "123456"; 29 | 30 | private Integer activeStatus = 1; 31 | 32 | private LocalDateTime createTime = LocalDateTime.now(); 33 | 34 | private List goodsList; 35 | } 36 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/java/com/example/mongodb/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb.service; 2 | 3 | import com.example.mongodb.entity.HistoryGoods; 4 | 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | 9 | List getHistoryGoods(String key, int start, int end); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/java/com/example/mongodb/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb.service.impl; 2 | 3 | import com.example.mongodb.entity.HistoryGoods; 4 | import com.example.mongodb.service.UserService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.data.mongodb.core.MongoTemplate; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | public class UserServiceImpl implements UserService { 13 | @Autowired 14 | private MongoTemplate mongoTemplate; 15 | 16 | @Override 17 | public List getHistoryGoods(String key, int start, int end) { 18 | 19 | // db.user.find({"goodsList":{"$elemMatch":{"goodsTitle":/fb/}}},{"goodsList.$":1}).limit(2).pretty() 20 | // 根据SQL写代码 21 | 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | mongodb: 4 | uri: mongodb://192.168.91.129:27017/test -------------------------------------------------------------------------------- /spring-mongodb-demo/src/test/java/com/example/mongodb/MongoMergeTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb; 2 | 3 | import com.example.mongodb.entity.User; 4 | import com.mongodb.client.result.DeleteResult; 5 | import com.mongodb.client.result.UpdateResult; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.data.mongodb.core.MongoTemplate; 10 | import org.springframework.data.mongodb.core.query.Criteria; 11 | import org.springframework.data.mongodb.core.query.Query; 12 | import org.springframework.data.mongodb.core.query.Update; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collection; 16 | 17 | /** 18 | *

19 | * MongoTemplateTest 20 | *

21 | * 22 | * @author 和耳朵 23 | * @since 2020-10-14 24 | */ 25 | @SpringBootTest 26 | public class MongoMergeTest { 27 | @Autowired 28 | private MongoTemplate mongoTemplate; 29 | 30 | /** 31 | * 保存数据 32 | */ 33 | @Test 34 | public void save() { 35 | User user = new User(); 36 | User save = mongoTemplate.insert(user); 37 | System.out.println(save); 38 | } 39 | 40 | /** 41 | * 批量保存数据 42 | */ 43 | @Test 44 | public void saveBatch() { 45 | User user1 = new User(); 46 | User user2 = new User(); 47 | User user3 = new User(); 48 | Collection users = mongoTemplate.insertAll(Arrays.asList(user1, user2, user3)); 49 | System.out.println(users); 50 | } 51 | 52 | /** 53 | * 更新数据1 54 | */ 55 | @Test 56 | public void update1() { 57 | // 指定ID后,可以使用Save方法保存数据 58 | User user = new User(); 59 | user.setId("0f4551e0-d8b4-4bce-b166-bf04e7a50f4f"); 60 | user.setActiveStatus(0); 61 | mongoTemplate.save(user); 62 | System.out.println(user); 63 | } 64 | 65 | /** 66 | * 更新数据2 67 | */ 68 | @Test 69 | public void update2() { 70 | // 指定要更新的范围 71 | Query query = Query.query(Criteria.where("id").is("851fb341-a3bd-4560-bc48-03d5054b4ec1")); 72 | 73 | // 指定要被更新的值 set方法如果key不存在则创建一个新的key 74 | Update update = Update.update("activeStatus", "0").set("test", "hahaha"); 75 | UpdateResult updateResult = mongoTemplate.updateFirst(query, update, User.class); 76 | System.out.println(updateResult); 77 | } 78 | 79 | /** 80 | * 单个删除数据 81 | */ 82 | @Test 83 | public void delete1() { 84 | User user = new User(); 85 | user.setId("0f4551e0-d8b4-4bce-b166-bf04e7a50f4f"); 86 | 87 | DeleteResult deleteResult = mongoTemplate.remove(user); 88 | System.out.println(deleteResult); 89 | } 90 | 91 | /** 92 | * 范围删除数据 93 | */ 94 | @Test 95 | public void delete2() { 96 | // 指定要删除的范围 97 | Query query = Query.query(Criteria.where("activeStatus").is("0")); 98 | 99 | DeleteResult deleteResult = mongoTemplate.remove(query, User.class); 100 | System.out.println(deleteResult); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/test/java/com/example/mongodb/MongoQueryTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb; 2 | 3 | import com.example.mongodb.entity.User; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.data.domain.Sort; 8 | import org.springframework.data.mongodb.core.MongoTemplate; 9 | import org.springframework.data.mongodb.core.query.Criteria; 10 | import org.springframework.data.mongodb.core.query.Query; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | *

16 | * MongoTemplateTest 17 | *

18 | * 19 | * @author 和耳朵 20 | * @since 2020-10-14 21 | */ 22 | @SpringBootTest 23 | public class MongoQueryTest { 24 | @Autowired 25 | private MongoTemplate mongoTemplate; 26 | 27 | /** 28 | * 单查 29 | */ 30 | @Test 31 | public void find() { 32 | User user = mongoTemplate.findById("d3d24091-30fe-4272-ab70-88fa475a62dd", User.class); 33 | System.out.println(user); 34 | } 35 | 36 | /** 37 | * 按条件查询数据 38 | */ 39 | @Test 40 | public void findBy1() { 41 | // 查询的范围 42 | Query query = Query.query(Criteria.where("activeStatus").is(1)); 43 | 44 | List users = mongoTemplate.find(query, User.class); 45 | System.out.println(users); 46 | } 47 | 48 | /** 49 | * 按条件查询数据 50 | */ 51 | @Test 52 | public void findBy2() { 53 | // 查询的范围 (password=256156) or (activeStatus = 1 and username = 和耳朵) 54 | Query query = Query 55 | .query(new Criteria().orOperator(Criteria.where("password").is("256156"), Criteria.where("activeStatus").is(1).and("username").is("和耳朵"))); 56 | 57 | List users = mongoTemplate.find(query, User.class); 58 | System.out.println(users); 59 | } 60 | 61 | /** 62 | * 批量按类型查询数据 63 | */ 64 | @Test 65 | public void findAll() { 66 | List users = mongoTemplate.findAll(User.class); 67 | System.out.println(users); 68 | } 69 | 70 | /** 71 | * 查询数据Limit 72 | */ 73 | @Test 74 | public void findLimit() { 75 | // 查询结果只要取前两条 76 | Query query = Query.query(Criteria.where("activeStatus").is(1)).limit(2); 77 | List users = mongoTemplate.find(query, User.class); 78 | System.out.println(users); 79 | } 80 | 81 | /** 82 | * 查询数据Skip 83 | */ 84 | @Test 85 | public void findSkip() { 86 | // 查询结果跳过前两条 87 | Query query = Query.query(Criteria.where("activeStatus").is(1)).skip(2); 88 | List users = mongoTemplate.find(query, User.class); 89 | System.out.println(users); 90 | } 91 | 92 | /** 93 | * 查询数据排序 94 | */ 95 | @Test 96 | public void findSort() { 97 | // 查询结果进行排序 98 | Query query = Query.query(Criteria.where("activeStatus").is(1)).with(Sort.by("createTime").descending()); 99 | List users = mongoTemplate.find(query, User.class); 100 | System.out.println(users); 101 | } 102 | 103 | /** 104 | * 聚合函数 105 | */ 106 | @Test 107 | public void aggregate() { 108 | // 查询条件 109 | Query query = Query.query(Criteria.where("activeStatus").is(1)).with(Sort.by("createTime").descending()); 110 | // 支持 count sum avg min max first last... 111 | // long count = mongoTemplate.count(query, User.class); 112 | // System.out.println(count); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /spring-mongodb-demo/src/test/java/com/example/mongodb/UserHistoryTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mongodb; 2 | 3 | import com.example.mongodb.entity.HistoryGoods; 4 | import com.example.mongodb.entity.User; 5 | import com.example.mongodb.service.UserService; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.data.mongodb.core.MongoTemplate; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | *

16 | * MongoTemplateTest 17 | *

18 | * 19 | * @author 和耳朵 20 | * @since 2020-10-14 21 | */ 22 | @SpringBootTest 23 | public class UserHistoryTest { 24 | @Autowired 25 | private MongoTemplate mongoTemplate; 26 | @Autowired 27 | private UserService userService; 28 | 29 | /** 30 | * 批量保存数据 6d053675-5fba-4338-9577-02810c3413d1 31 | */ 32 | @Test 33 | public void saveBatch() { 34 | for (int i = 0; i < 2000; i++) { 35 | User user = new User(); 36 | System.out.println(user.getId()); 37 | List goodsList = new ArrayList<>(501); 38 | user.setUsername(user.getUsername() + i); 39 | for (int j = 0; j < 500; j++) { 40 | HistoryGoods goods = new HistoryGoods(); 41 | goodsList.add(goods); 42 | } 43 | user.setGoodsList(goodsList); 44 | mongoTemplate.save(user); 45 | } 46 | } 47 | 48 | /** 49 | * 查询历史浏览商品数据 50 | */ 51 | @Test 52 | public void getHistoryGoods() { 53 | List historyGoods = userService.getHistoryGoods("小米", 10, 10); 54 | System.out.println(historyGoods); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-rabbitmq-demo 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-amqp 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-json 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | org.junit.vintage 32 | junit-vintage-engine 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/SpringRabbitmqApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringRabbitmqApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringRabbitmqApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitConsumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.rabbitmq.prototype.Producer; 6 | import org.springframework.amqp.core.Message; 7 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | *

12 | * 13 | *

14 | * 15 | * @author 和耳朵 16 | * @since 2020-07-29 21:01 17 | */ 18 | @Slf4j 19 | @Component("rabbitConsumer") 20 | public class RabbitConsumer { 21 | 22 | @RabbitListener(queues = Producer.QUEUE_NAME) 23 | public void onMessage(Message message, Channel channel) throws Exception { 24 | System.out.println("Message content : " + message); 25 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 26 | System.out.println("消息已确认"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitDirectConsumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.amqp.core.Message; 6 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | *

11 | * 12 | *

13 | * 14 | * @author 和耳朵 15 | * @since 2020-08-17 21:01 16 | */ 17 | @Slf4j 18 | @Component("rabbitDirectConsumer") 19 | public class RabbitDirectConsumer { 20 | @RabbitListener(queues = "directQueue1") 21 | public void onMessage1(Message message, Channel channel) throws Exception { 22 | log.info("Message content : " + message); 23 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 24 | log.info("消息已确认"); 25 | } 26 | 27 | @RabbitListener(queues = "directQueue2") 28 | public void onMessage2(Message message, Channel channel) throws Exception { 29 | log.info("Message content : " + message); 30 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 31 | log.info("消息已确认"); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitFanoutConsumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.rabbitmq.auto.entity.Client; 6 | import org.example.rabbitmq.prototype.Producer; 7 | import org.springframework.amqp.core.Message; 8 | import org.springframework.amqp.rabbit.annotation.RabbitHandler; 9 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 10 | import org.springframework.amqp.support.AmqpHeaders; 11 | import org.springframework.messaging.handler.annotation.Headers; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Map; 15 | 16 | /** 17 | *

18 | * 19 | *

20 | * 21 | * @author 和耳朵 22 | * @since 2020-08-13 21:01 23 | */ 24 | @Slf4j 25 | @Component("rabbitFanoutConsumer") 26 | public class RabbitFanoutConsumer { 27 | @RabbitListener(queues = "fanout1") 28 | public void onMessage1(Message message, Channel channel) throws Exception { 29 | log.info("Message content : " + message); 30 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 31 | log.info("消息已确认"); 32 | } 33 | 34 | @RabbitListener(queues = "fanout2") 35 | public void onMessage2(Message message, Channel channel) throws Exception { 36 | log.info("Message content : " + message); 37 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 38 | log.info("消息已确认"); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitJsonConsumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.rabbitmq.auto.entity.Client; 6 | import org.springframework.amqp.rabbit.annotation.RabbitHandler; 7 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 8 | import org.springframework.amqp.support.AmqpHeaders; 9 | import org.springframework.messaging.handler.annotation.Headers; 10 | import org.springframework.stereotype.Component; 11 | 12 | import java.util.Map; 13 | 14 | /** 15 | *

16 | * 17 | *

18 | * 19 | * @author 和耳朵 20 | * @since 2020-07-29 21:01 21 | */ 22 | @Slf4j 23 | @Component("rabbitJsonConsumer") 24 | @RabbitListener(queues = RabbitJsonConsumer.JSON_QUEUE) 25 | public class RabbitJsonConsumer { 26 | public static final String JSON_QUEUE = "erduo_json"; 27 | 28 | @RabbitHandler 29 | public void onMessage(Client client, @Headers Map headers, Channel channel) throws Exception { 30 | System.out.println("Message content : " + client); 31 | System.out.println("Message headers : " + headers); 32 | channel.basicAck((Long) headers.get(AmqpHeaders.DELIVERY_TAG),false); 33 | System.out.println("消息已确认"); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitProduce.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.rabbitmq.auto.entity.Client; 5 | import org.example.rabbitmq.auto.entity.User; 6 | import org.example.rabbitmq.prototype.Producer; 7 | import org.springframework.amqp.core.Message; 8 | import org.springframework.amqp.core.MessageProperties; 9 | import org.springframework.amqp.core.MessagePropertiesBuilder; 10 | import org.springframework.amqp.rabbit.connection.CorrelationData; 11 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.nio.charset.StandardCharsets; 16 | import java.time.LocalDateTime; 17 | import java.util.UUID; 18 | 19 | /** 20 | *

21 | * 22 | *

23 | * 24 | * @author 和耳朵 25 | * @since 2020-07-29 21:01 26 | */ 27 | @Slf4j 28 | @Component("rabbitProduce") 29 | public class RabbitProduce { 30 | @Autowired 31 | private RabbitTemplate rabbitTemplate; 32 | 33 | public void send() { 34 | String message = "Hello 我是作者和耳朵,欢迎关注我。" + LocalDateTime.now().toString(); 35 | 36 | System.out.println("Message content : " + message); 37 | 38 | // 指定消息类型 39 | MessageProperties props = MessagePropertiesBuilder.newInstance() 40 | .setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN).build(); 41 | 42 | rabbitTemplate.send(Producer.QUEUE_NAME,new Message(message.getBytes(StandardCharsets.UTF_8),props)); 43 | System.out.println("消息发送完毕。"); 44 | } 45 | 46 | public void convertAndSend() { 47 | User user = new User(); 48 | 49 | System.out.println("Message content : " + user); 50 | 51 | rabbitTemplate.convertAndSend(Producer.QUEUE_NAME,user); 52 | System.out.println("消息发送完毕。"); 53 | } 54 | 55 | public void sendObject() { 56 | Client client = new Client(); 57 | 58 | System.out.println("Message content : " + client); 59 | 60 | rabbitTemplate.convertAndSend(RabbitJsonConsumer.JSON_QUEUE,client); 61 | System.out.println("消息发送完毕。"); 62 | } 63 | 64 | public void sendFanout() { 65 | Client client = new Client(); 66 | 67 | // 应读者要求,以后代码打印的地方都会改成log方式,这是一种良好的编程习惯,用System.out.println一般是不推荐的。 68 | log.info("Message content : " + client); 69 | 70 | rabbitTemplate.convertAndSend("fanoutExchange",null,client); 71 | System.out.println("消息发送完毕。"); 72 | } 73 | 74 | public void sendDirect() { 75 | Client client = new Client(); 76 | 77 | log.info("Message content : " + client); 78 | 79 | rabbitTemplate.convertAndSend("directExchange","sms",client); 80 | System.out.println("消息发送完毕。"); 81 | } 82 | 83 | public void sendTopic() { 84 | Client client = new Client(); 85 | 86 | log.info("Message content : " + client); 87 | 88 | rabbitTemplate.convertAndSend("topicExchange","sms.liantong",client); 89 | System.out.println("消息发送完毕。"); 90 | } 91 | 92 | public void sendAndConfirm() { 93 | User user = new User(); 94 | 95 | log.info("Message content : " + user); 96 | 97 | CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); 98 | rabbitTemplate.convertAndSend(Producer.QUEUE_NAME,user,correlationData); 99 | log.info("消息发送完毕。"); 100 | 101 | rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback(){ 102 | @Override 103 | public void confirm(CorrelationData correlationData, boolean ack, String cause) { 104 | log.info("CorrelationData content : " + correlationData); 105 | log.info("Ack status : " + ack); 106 | log.info("Cause content : " + cause); 107 | if(ack){ 108 | log.info("消息成功发送,订单入库,更改订单状态"); 109 | }else{ 110 | log.info("消息发送失败:"+correlationData+", 出现异常:"+cause); 111 | } 112 | } 113 | }); 114 | } 115 | 116 | public void sendAndReturn() { 117 | User user = new User(); 118 | 119 | log.info("Message content : " + user); 120 | 121 | rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> { 122 | log.info("被退回的消息为:{}", message); 123 | log.info("replyCode:{}", replyCode); 124 | log.info("replyText:{}", replyText); 125 | log.info("exchange:{}", exchange); 126 | log.info("routingKey:{}", routingKey); 127 | }); 128 | 129 | rabbitTemplate.convertAndSend("fail", user); 130 | log.info("消息发送完毕。"); 131 | } 132 | 133 | public void sendTtl() { 134 | String message = "Hello 我是作者和耳朵,欢迎关注我。" + LocalDateTime.now().toString(); 135 | 136 | System.out.println("Message content : " + message); 137 | 138 | // 设置过期3s 139 | MessageProperties props = MessagePropertiesBuilder.newInstance() 140 | .setExpiration("3000").build(); 141 | 142 | rabbitTemplate.send(Producer.QUEUE_NAME, new Message(message.getBytes(StandardCharsets.UTF_8), props)); 143 | System.out.println("消息发送完毕。"); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/component/RabbitTopicConsumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.amqp.core.Message; 6 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | *

11 | * 12 | *

13 | * 14 | * @author 和耳朵 15 | * @since 2020-08-17 22:01 16 | */ 17 | @Slf4j 18 | @Component("rabbitTopicConsumer") 19 | public class RabbitTopicConsumer { 20 | @RabbitListener(queues = "topicQueue1") 21 | public void onMessage1(Message message, Channel channel) throws Exception { 22 | log.info("Message content : " + message); 23 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 24 | log.info("消息已确认"); 25 | } 26 | 27 | @RabbitListener(queues = "topicQueue2") 28 | public void onMessage2(Message message, Channel channel) throws Exception { 29 | log.info("Message content : " + message); 30 | channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); 31 | log.info("消息已确认"); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/config/JacksonConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.SerializationFeature; 8 | import com.fasterxml.jackson.databind.module.SimpleModule; 9 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 10 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 11 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 12 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 13 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; 14 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 15 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 16 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 18 | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; 19 | import org.springframework.context.annotation.Bean; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.context.annotation.Primary; 22 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 23 | 24 | import java.math.BigInteger; 25 | import java.time.LocalDate; 26 | import java.time.LocalDateTime; 27 | import java.time.LocalTime; 28 | import java.time.ZoneId; 29 | import java.time.format.DateTimeFormatter; 30 | import java.util.Locale; 31 | import java.util.TimeZone; 32 | 33 | /** 34 | *

35 | * 针对Jackson做的个性化配置 36 | *

37 | * 38 | * @author rookie.zhang 39 | * @since 2020-03-13 40 | */ 41 | @Configuration 42 | public class JacksonConfig { 43 | 44 | @Bean 45 | public Jackson2ObjectMapperBuilderCustomizer customizer() { 46 | return builder -> { 47 | builder.locale(Locale.CHINA); 48 | builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault())); 49 | builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); 50 | 51 | JavaTimeModule javaTimeModule = new JavaTimeModule(); 52 | javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 53 | javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 54 | javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); 55 | javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 56 | javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 57 | javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); 58 | 59 | builder.modules(javaTimeModule); 60 | }; 61 | } 62 | 63 | 64 | @Bean 65 | @Primary 66 | @ConditionalOnMissingBean(ObjectMapper.class) 67 | public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) 68 | { 69 | ObjectMapper objectMapper = builder.createXmlMapper(false).build(); 70 | 71 | // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化 72 | // Include.Include.ALWAYS 默认 73 | // Include.NON_DEFAULT 属性为默认值不序列化 74 | // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的 75 | // Include.NON_NULL 属性为NULL 不序列化 76 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 77 | // 允许出现特殊字符和转义符 78 | objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); 79 | // 允许出现单引号 80 | objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 81 | objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 82 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 83 | 84 | SimpleModule simpleModule = new SimpleModule(); 85 | /** 86 | * 将Long,BigInteger序列化的时候,转化为String 87 | */ 88 | simpleModule.addSerializer(Long.class, ToStringSerializer.instance); 89 | simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); 90 | simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance); 91 | 92 | objectMapper.registerModule(simpleModule); 93 | 94 | return objectMapper; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/config/RabbitmqConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.amqp.core.*; 5 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 6 | import org.springframework.amqp.support.converter.MessageConverter; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | /** 15 | *

16 | * 17 | *

18 | * 19 | * @author 和耳朵 20 | * @since 2020-07-29 21:01 21 | */ 22 | @Configuration 23 | public class RabbitmqConfig { 24 | @Autowired 25 | private ObjectMapper jacksonObjectMapper; 26 | 27 | @Bean 28 | public Queue erduo() { 29 | // 其三个参数:durable exclusive autoDelete 30 | // 一般只设置一下持久化即可 31 | return new Queue("erduo",true); 32 | } 33 | 34 | @Bean 35 | public Queue erduoJson() { 36 | // 其三个参数:durable exclusive autoDelete 37 | // 一般只设置一下持久化即可 38 | return new Queue("erduo_json",true); 39 | } 40 | 41 | // 扇形交换机示例 42 | @Bean 43 | public Queue fanout1() { 44 | return new Queue("fanout1"); 45 | } 46 | 47 | @Bean 48 | public Queue fanout2() { 49 | return new Queue("fanout2"); 50 | } 51 | 52 | @Bean 53 | public FanoutExchange fanoutExchange() { 54 | // 三个构造参数:name durable autoDelete 55 | return new FanoutExchange("fanoutExchange", false, false); 56 | } 57 | 58 | @Bean 59 | public Binding binding1() { 60 | return BindingBuilder.bind(fanout1()).to(fanoutExchange()); 61 | } 62 | 63 | @Bean 64 | public Binding binding2() { 65 | return BindingBuilder.bind(fanout2()).to(fanoutExchange()); 66 | } 67 | 68 | // 直接交换机示例 69 | @Bean 70 | public Queue directQueue1() { 71 | return new Queue("directQueue1"); 72 | } 73 | 74 | @Bean 75 | public Queue directQueue2() { 76 | return new Queue("directQueue2"); 77 | } 78 | 79 | @Bean 80 | public DirectExchange directExchange() { 81 | // 三个构造参数:name durable autoDelete 82 | return new DirectExchange("directExchange", false, false); 83 | } 84 | 85 | @Bean 86 | public Binding directBinding1() { 87 | return BindingBuilder.bind(directQueue1()).to(directExchange()).with("sms"); 88 | } 89 | 90 | @Bean 91 | public Binding directBinding2() { 92 | return BindingBuilder.bind(directQueue2()).to(directExchange()).with("mail"); 93 | } 94 | 95 | // 主题交换机示例 96 | @Bean 97 | public Queue topicQueue1() { 98 | return new Queue("topicQueue1"); 99 | } 100 | 101 | @Bean 102 | public Queue topicQueue2() { 103 | return new Queue("topicQueue2"); 104 | } 105 | 106 | @Bean 107 | public TopicExchange topicExchange() { 108 | // 三个构造参数:name durable autoDelete 109 | return new TopicExchange("topicExchange", false, false); 110 | } 111 | 112 | @Bean 113 | public Binding topicBinding1() { 114 | return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("sms.*"); 115 | } 116 | 117 | @Bean 118 | public Binding topicBinding2() { 119 | return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("mail.#"); 120 | } 121 | 122 | // TTL队列示例 123 | @Bean 124 | public Queue ttlQueue() { 125 | Map arguments = new HashMap<>(); 126 | // 设置3s过期 127 | arguments.put("x-message-ttl", 3000); 128 | return new Queue("topicQueue1", false, false, false, arguments); 129 | } 130 | 131 | // DLX队列示例 132 | @Bean 133 | public Queue dlxQueue() { 134 | Map arguments = new HashMap<>(); 135 | // 指定消息死亡后发送到ExchangeName="dlx.exchange"的交换机去 136 | arguments.put("x-dead-letter-exchange","dlx.exchange"); 137 | return new Queue("topicQueue1", false, false, false, arguments); 138 | } 139 | 140 | @Bean 141 | public MessageConverter jackson2JsonMessageConverter() { 142 | return new Jackson2JsonMessageConverter(jacksonObjectMapper); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/entity/Client.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.time.LocalDateTime; 7 | import java.util.UUID; 8 | 9 | @Data 10 | public class Client{ 11 | private String id = UUID.randomUUID().toString(); 12 | private String name = "和耳朵"; 13 | private Integer age = 22; 14 | private LocalDateTime crateTime = LocalDateTime.now(); 15 | } 16 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/auto/entity/User.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.time.LocalDateTime; 7 | import java.util.UUID; 8 | 9 | @Data 10 | public class User implements Serializable { 11 | private String id = UUID.randomUUID().toString(); 12 | private String name = "和耳朵"; 13 | private Integer age = 22; 14 | private LocalDateTime crateTime = LocalDateTime.now(); 15 | } 16 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/prototype/Consumer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.prototype; 2 | 3 | 4 | import com.rabbitmq.client.*; 5 | 6 | import java.io.IOException; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.concurrent.TimeoutException; 9 | 10 | public class Consumer { 11 | 12 | public void consumer() throws IOException, TimeoutException { 13 | // 创建连接工厂 14 | ConnectionFactory connectionFactory = new ConnectionFactory(); 15 | // 连接到本地server 16 | connectionFactory.setHost("127.0.0.1"); 17 | // 通过连接工厂创建连接 18 | Connection connection = connectionFactory.newConnection(); 19 | // 通过连接创建通道 20 | Channel channel = connection.createChannel(); 21 | // 创建消费者,阻塞接收消息 22 | com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) { 23 | @Override 24 | public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 25 | System.out.println("-------------------------------------------"); 26 | System.out.println("consumerTag : " + consumerTag); 27 | System.out.println("exchangeName : " + envelope.getExchange()); 28 | System.out.println("routingKey : " + envelope.getRoutingKey()); 29 | String msg = new String(body, StandardCharsets.UTF_8); 30 | System.out.println("消息内容 : " + msg); 31 | 32 | // 消息确认 33 | channel.basicAck(envelope.getDeliveryTag(), false); 34 | System.out.println("消息已确认"); 35 | } 36 | }; 37 | // 启动消费者消费指定队列 38 | channel.basicConsume(Producer.QUEUE_NAME, consumer); 39 | // channel.close(); 40 | // connection.close(); 41 | } 42 | 43 | public static void main(String[] args) throws IOException, TimeoutException { 44 | Consumer consumer = new Consumer(); 45 | consumer.consumer(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/java/org/example/rabbitmq/prototype/Producer.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.prototype; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import com.rabbitmq.client.Connection; 5 | import com.rabbitmq.client.ConnectionFactory; 6 | 7 | import java.io.IOException; 8 | import java.nio.charset.StandardCharsets; 9 | import java.time.LocalDateTime; 10 | import java.util.concurrent.TimeoutException; 11 | 12 | public class Producer { 13 | 14 | public static final String QUEUE_NAME = "erduo"; 15 | 16 | public void producer() throws IOException, TimeoutException { 17 | // 创建连接工厂 18 | ConnectionFactory connectionFactory = new ConnectionFactory(); 19 | // 连接到本地server 20 | connectionFactory.setHost("127.0.0.1"); 21 | // 通过连接工厂创建连接 22 | Connection connection = connectionFactory.newConnection(); 23 | // 通过连接创建通道 24 | Channel channel = connection.createChannel(); 25 | // 创建一个名为耳朵的队列,该队列非持久(RabbitMQ重启后会消失)、非独占(非仅用于此链接)、非自动删除(服务器将不再使用的队列删除) 26 | channel.queueDeclare(QUEUE_NAME, false, false, false, null); 27 | String msg = "hello, 我是耳朵。" + LocalDateTime.now().toString(); 28 | // 发布消息 29 | // 四个参数为:指定路由器,指定key,指定参数,和二进制数据内容 30 | channel.basicPublish("", QUEUE_NAME, null, msg.getBytes(StandardCharsets.UTF_8)); 31 | System.out.println("生产者发送消息结束,发送内容为:" + msg); 32 | channel.close(); 33 | connection.close(); 34 | } 35 | 36 | public static void main(String[] args) throws IOException, TimeoutException { 37 | Producer producer = new Producer(); 38 | producer.producer(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | servlet: 3 | # 请求数据转码UTF8 4 | encoding: 5 | enabled: true 6 | charset: UTF-8 7 | force: true 8 | logging: 9 | level: 10 | web: debug 11 | org: 12 | example: 13 | rabbitmq: debug 14 | 15 | spring: 16 | rabbitmq: 17 | addresses: 127.0.0.1 18 | host: 5672 19 | username: guest 20 | password: guest 21 | virtual-host: / 22 | # 打开消息确认机制 23 | publisher-confirm-type: correlated 24 | # 打开消息返回 25 | publisher-returns: true 26 | template: 27 | mandatory: true 28 | # 手动确认消息 29 | listener: 30 | simple: 31 | acknowledge-mode: manual 32 | prefetch: 2 -------------------------------------------------------------------------------- /spring-rabbitmq-demo/src/test/java/org/example/rabbitmq/auto/component/RabbitProduceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.rabbitmq.auto.component; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | public class RabbitProduceTest { 9 | @Autowired 10 | private RabbitProduce rabbitProduce; 11 | 12 | @Test 13 | public void sendSimpleMessage() { 14 | rabbitProduce.send(); 15 | rabbitProduce.convertAndSend(); 16 | } 17 | 18 | @Test 19 | public void sendJSONMessage() { 20 | rabbitProduce.sendObject(); 21 | } 22 | 23 | @Test 24 | public void sendFanoutMessage() { 25 | rabbitProduce.sendFanout(); 26 | } 27 | 28 | @Test 29 | public void sendDirectMessage() { 30 | rabbitProduce.sendDirect(); 31 | } 32 | 33 | @Test 34 | public void sendTopicMessage() { 35 | rabbitProduce.sendTopic(); 36 | } 37 | 38 | @Test 39 | public void sendAndConfirm() throws InterruptedException { 40 | rabbitProduce.sendAndConfirm(); 41 | Thread.sleep(3000); 42 | } 43 | 44 | @Test 45 | public void sendAndReturn() throws InterruptedException { 46 | rabbitProduce.sendAndReturn(); 47 | Thread.sleep(3000); 48 | } 49 | } -------------------------------------------------------------------------------- /spring-security-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-security-demo 13 | 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-security 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-validation 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-cache 34 | 35 | 36 | 37 | com.github.ben-manes.caffeine 38 | caffeine 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | 50 | 51 | 52 | 53 | org.springframework.security 54 | spring-security-test 55 | test 56 | 57 | 58 | 59 | 60 | 61 | com.baomidou 62 | mybatis-plus-boot-starter 63 | ${mybatis.plus.version} 64 | 65 | 66 | com.baomidou 67 | mybatis-plus-generator 68 | ${mybatis.plus.version} 69 | 70 | 71 | org.apache.velocity 72 | velocity 73 | ${velocity.version} 74 | provided 75 | 76 | 77 | mysql 78 | mysql-connector-java 79 | ${mysql.version} 80 | 81 | 82 | 83 | 84 | 85 | io.jsonwebtoken 86 | jjwt 87 | 0.9.0 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | src/main/resources 99 | true 100 | 101 | 102 | 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-maven-plugin 107 | ${spring-boot.version} 108 | 109 | org.example.security.SpringSecurityApplication 110 | 111 | 112 | 113 | 114 | repackage 115 | 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-compiler-plugin 123 | ${maven-compiler-plugin.version} 124 | 125 | ${java.version} 126 | ${java.version} 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/SpringSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.security; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @MapperScan("org.example.**.mapper") 8 | @SpringBootApplication 9 | public class SpringSecurityApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(SpringSecurityApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/api/AuthController.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.api; 2 | 3 | import org.example.security.auth.bo.ApiResult; 4 | import org.example.security.auth.bo.LoginInfo; 5 | import org.example.security.auth.provider.JwtProvider; 6 | import org.example.security.auth.service.AuthService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestBody; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.validation.Valid; 15 | 16 | /** 17 | *

18 | * 认证 19 | *

20 | * 21 | * @author 和耳朵 22 | * @since 2020-06-30 23 | */ 24 | @RestController 25 | @RequestMapping("/api/auth") 26 | public class AuthController { 27 | 28 | @Autowired 29 | private AuthService authService; 30 | @Autowired 31 | private JwtProvider jwtProvider; 32 | 33 | /** 34 | * 登录方法 35 | *

36 | * loginAccount:user 37 | * password:123456 38 | * 39 | * @param loginInfo 40 | * @return ApiResult 41 | */ 42 | @PostMapping("/login") 43 | public ApiResult login(@Valid @RequestBody LoginInfo loginInfo) { 44 | return authService.login(loginInfo.getLoginAccount(), loginInfo.getPassword()); 45 | } 46 | 47 | @PostMapping("/logout") 48 | public ApiResult logout() { 49 | return authService.logout(); 50 | } 51 | 52 | @PostMapping("/refresh") 53 | public ApiResult refreshToken(HttpServletRequest request) { 54 | return authService.refreshToken(jwtProvider.getToken(request)); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/api/TestController.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.api; 2 | 3 | import org.example.security.auth.bo.ApiResult; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class TestController { 9 | 10 | @GetMapping("/api/anon") 11 | public ApiResult test01() { 12 | return ApiResult.ok("匿名访问成功"); 13 | } 14 | 15 | @GetMapping("/api/user") 16 | public ApiResult test02() { 17 | return ApiResult.ok("USER用户访问成功"); 18 | } 19 | 20 | @GetMapping("/api/admin") 21 | public ApiResult test03() { 22 | return ApiResult.ok("管理员用户访问成功"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/bo/AccessToken.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.bo; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | @Data 9 | @Builder 10 | public class AccessToken { 11 | private String loginAccount; 12 | private String token; 13 | private Date expirationTime; 14 | } 15 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/bo/ApiPage.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.bo; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | *

10 | * Api分页工具类 11 | *

12 | * 13 | * @author 和耳朵 14 | * @since 2020-06-05 15 | */ 16 | @JsonIgnoreProperties(value = {"records", "total", "size", "current", "records", "searchCount", "orders"}) 17 | public class ApiPage extends Page { 18 | 19 | /** 20 | * 总数 21 | */ 22 | private long totalCount = 0; 23 | /** 24 | * 每页显示条数,默认 10 25 | */ 26 | private long pageSize = 10; 27 | /** 28 | * 当前页 29 | */ 30 | private long currentPage = 1; 31 | /** 32 | * 查询数据列表 33 | */ 34 | private List data; 35 | 36 | public ApiPage() { 37 | super(); 38 | } 39 | 40 | public ApiPage(long current, long size) { 41 | super(current, size); 42 | } 43 | 44 | public ApiPage(long current, long size, long total) { 45 | super(current, size, total); 46 | } 47 | 48 | public ApiPage(long current, long size, boolean isSearchCount) { 49 | super(current, size, isSearchCount); 50 | } 51 | 52 | 53 | public ApiPage(long current, long size, long total, boolean isSearchCount) { 54 | super(current, size, total, isSearchCount); 55 | } 56 | 57 | public long getTotalCount() { 58 | return super.getTotal(); 59 | } 60 | 61 | public long getPageSize() { 62 | return super.getSize(); 63 | } 64 | 65 | public long getCurrentPage() { 66 | return super.getCurrent(); 67 | } 68 | 69 | public List getData() { 70 | return super.getRecords(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/bo/ApiResult.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.bo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import org.example.security.auth.constant.ApiStatus; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | public class ApiResult implements Serializable { 18 | 19 | private static final Map map = new HashMap<>(1); 20 | private int code; 21 | private String msg; 22 | private Object data; 23 | // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 24 | private LocalDateTime timestamp; 25 | 26 | public static ApiResult ok() { 27 | return ok(ApiStatus.OK.getMsg(), null); 28 | } 29 | 30 | 31 | public static ApiResult ok(T data) { 32 | return ok(ApiStatus.OK.getMsg(), data); 33 | } 34 | 35 | 36 | public static ApiResult ok(String msg, T data) { 37 | 38 | if (msg != null && data instanceof String) { 39 | String value = (String) data; 40 | 41 | map.clear(); 42 | map.put(msg, value); 43 | 44 | ApiResult apiResult = new ApiResult(); 45 | apiResult.setCode(ApiStatus.OK.getCode()); 46 | apiResult.setMsg(ApiStatus.OK.getMsg()); 47 | apiResult.setData(map); 48 | apiResult.setTimestamp(LocalDateTime.now()); 49 | return apiResult; 50 | } 51 | 52 | ApiResult apiResult = new ApiResult(); 53 | apiResult.setCode(ApiStatus.OK.getCode()); 54 | apiResult.setMsg(msg); 55 | apiResult.setData(data); 56 | apiResult.setTimestamp(LocalDateTime.now()); 57 | return apiResult; 58 | } 59 | 60 | 61 | public static ApiResult fail(String msg) { 62 | return fail(msg, null); 63 | } 64 | 65 | public static ApiResult fail(String msg, T data) { 66 | ApiResult apiResult = new ApiResult(); 67 | apiResult.setCode(ApiStatus.FAIL.getCode()); 68 | apiResult.setMsg(msg); 69 | apiResult.setData(data); 70 | apiResult.setTimestamp(LocalDateTime.now()); 71 | return apiResult; 72 | } 73 | 74 | public static ApiResult error(String msg) { 75 | return error(msg, null); 76 | } 77 | 78 | public static ApiResult error(String msg, T data) { 79 | ApiResult apiResult = new ApiResult(); 80 | apiResult.setCode(ApiStatus.ERROR.getCode()); 81 | apiResult.setMsg(msg); 82 | apiResult.setData(data); 83 | apiResult.setTimestamp(LocalDateTime.now()); 84 | return apiResult; 85 | } 86 | 87 | public static ApiResult instance(ApiStatus apiStatus) { 88 | ApiResult apiResult = new ApiResult(); 89 | apiResult.setCode(apiStatus.getCode()); 90 | apiResult.setMsg(apiStatus.getMsg()); 91 | apiResult.setData(null); 92 | apiResult.setTimestamp(LocalDateTime.now()); 93 | return apiResult; 94 | } 95 | 96 | public boolean isFail() { 97 | return this.code != ApiStatus.OK.getCode(); 98 | } 99 | 100 | public boolean isSuccess() { 101 | return this.code == ApiStatus.OK.getCode(); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/bo/LoginInfo.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.bo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | @Data 8 | public class LoginInfo { 9 | 10 | @NotBlank(message = "登陆用户名为空") 11 | private String loginAccount; 12 | 13 | @NotBlank(message = "登陆密码为空") 14 | private String password; 15 | } 16 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/bo/PermissionInfoBO.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.bo; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | *

7 | * 8 | *

9 | * 10 | * @author 和耳朵 11 | * @since 2020-07-05 19:05 12 | */ 13 | @Data 14 | public class PermissionInfoBO { 15 | 16 | private String id; 17 | 18 | private String permissionName; 19 | 20 | private String permissionUri; 21 | 22 | private String permissionMethod; 23 | 24 | private String roleName; 25 | 26 | private String roleCode; 27 | } 28 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.cache; 2 | 3 | import org.example.security.auth.constant.CacheName; 4 | 5 | /** 6 | *

7 | * Cache接口 8 | *

9 | * 10 | * @author 和耳朵 11 | * @since 2020-06-30 12 | */ 13 | public interface Cache { 14 | 15 | T get(CacheName cacheName, String key, Class clazz); 16 | 17 | void put(CacheName cacheName, String key, Object value); 18 | 19 | void remove(CacheName cacheName, String key); 20 | } 21 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/cache/CaffeineCache.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.cache; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.security.auth.constant.CacheName; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.cache.CacheManager; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.Objects; 10 | 11 | /** 12 | *

13 | * CaffeineCache实现类 14 | *

15 | * 16 | * @author 和耳朵 17 | * @since 2020-06-30 18 | */ 19 | @Slf4j 20 | @Service("caffeineCache") 21 | public class CaffeineCache implements Cache { 22 | @Autowired 23 | private CacheManager caffeineCacheManager; 24 | 25 | 26 | @Override 27 | public T get(CacheName cacheName, String key, Class clazz) { 28 | log.debug("{} get -> cacheName [{}], key [{}], class type [{}]", this.getClass().getName(), cacheName, key, clazz.getName()); 29 | return Objects.requireNonNull(caffeineCacheManager.getCache(cacheName.getCacheName())).get(key, clazz); 30 | } 31 | 32 | @Override 33 | public void put(CacheName cacheName, String key, Object value) { 34 | log.debug("{} put -> cacheName [{}], key [{}], value [{}]", this.getClass().getName(), cacheName, key, value); 35 | Objects.requireNonNull(caffeineCacheManager.getCache(cacheName.getCacheName())).put(key, value); 36 | } 37 | 38 | @Override 39 | public void remove(CacheName cacheName, String key) { 40 | log.debug("{} remove -> cacheName [{}], key [{}]", this.getClass().getName(), cacheName, key); 41 | Objects.requireNonNull(caffeineCacheManager.getCache(cacheName.getCacheName())).evict(key); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/component/AccessDecisionProcessor.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.component; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.security.auth.bo.PermissionInfoBO; 5 | import org.example.security.auth.cache.Cache; 6 | import org.example.security.auth.constant.CacheName; 7 | import org.example.security.auth.entity.UserDetail; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.security.access.AccessDecisionVoter; 10 | import org.springframework.security.access.ConfigAttribute; 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.web.FilterInvocation; 13 | 14 | import java.util.Collection; 15 | import java.util.List; 16 | 17 | @Slf4j 18 | public class AccessDecisionProcessor implements AccessDecisionVoter { 19 | @Autowired 20 | private Cache caffeineCache; 21 | 22 | @Override 23 | public int vote(Authentication authentication, FilterInvocation object, Collection attributes) { 24 | assert authentication != null; 25 | assert object != null; 26 | 27 | // 拿到当前请求uri 28 | String requestUrl = object.getRequestUrl(); 29 | String method = object.getRequest().getMethod(); 30 | log.debug("进入自定义鉴权投票器,URI : {} {}", method, requestUrl); 31 | 32 | String key = requestUrl + ":" + method; 33 | // 如果没有缓存中没有此权限也就是未保护此API,弃权 34 | PermissionInfoBO permission = caffeineCache.get(CacheName.PERMISSION, key, PermissionInfoBO.class); 35 | if (permission == null) { 36 | return ACCESS_ABSTAIN; 37 | } 38 | 39 | // 拿到当前用户所具有的权限 40 | List roles = ((UserDetail) authentication.getPrincipal()).getRoles(); 41 | if (roles.contains(permission.getRoleCode())) { 42 | return ACCESS_GRANTED; 43 | } else { 44 | return ACCESS_DENIED; 45 | } 46 | } 47 | 48 | @Override 49 | public boolean supports(ConfigAttribute attribute) { 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean supports(Class clazz) { 55 | return true; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/component/InitProcessor.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.component; 2 | 3 | 4 | import org.example.security.auth.bo.PermissionInfoBO; 5 | import org.example.security.auth.cache.Cache; 6 | import org.example.security.auth.constant.CacheName; 7 | import org.example.security.auth.service.PermissionService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import javax.annotation.PostConstruct; 12 | import java.util.List; 13 | 14 | @Component 15 | public class InitProcessor { 16 | @Autowired 17 | private PermissionService permissionService; 18 | @Autowired 19 | private Cache caffeineCache; 20 | 21 | @PostConstruct 22 | public void init() { 23 | List permissionInfoList = permissionService.listPermissionInfoBO(); 24 | permissionInfoList.forEach(permissionInfo -> { 25 | caffeineCache.put(CacheName.PERMISSION, permissionInfo.getPermissionUri() + ":" + permissionInfo.getPermissionMethod(), permissionInfo); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/component/JwtAuthenticationTokenFilter.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.component; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.security.auth.cache.Cache; 6 | import org.example.security.auth.constant.CacheName; 7 | import org.example.security.auth.entity.UserDetail; 8 | import org.example.security.auth.properties.JwtProperties; 9 | import org.example.security.auth.provider.JwtProvider; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.context.SecurityContextHolder; 13 | import org.springframework.web.filter.OncePerRequestFilter; 14 | 15 | import javax.servlet.FilterChain; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.http.HttpServletRequest; 18 | import javax.servlet.http.HttpServletResponse; 19 | import java.io.IOException; 20 | 21 | /** 22 | *

23 | * JWT登录过滤器 24 | *

25 | *

26 | * 拿到请求头中的token解析出其中的用户信息, 27 | * 将用户信息传给下一条过滤器, 28 | * 拿到上下文对象赋值到上下文。 29 | *

30 | * 31 | * @author 和耳朵 32 | * @since 2020-06-30 33 | */ 34 | @Slf4j 35 | public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { 36 | 37 | @Autowired 38 | private JwtProvider jwtProvider; 39 | @Autowired 40 | private JwtProperties jwtProperties; 41 | @Autowired 42 | private Cache caffeineCache; 43 | 44 | @Override 45 | protected void doFilterInternal(HttpServletRequest request, 46 | HttpServletResponse response, 47 | FilterChain chain) throws ServletException, IOException { 48 | log.info("JWT过滤器通过校验请求头token进行自动登录..."); 49 | 50 | // 拿到Authorization请求头内的信息 51 | String authToken = jwtProvider.getToken(request); 52 | 53 | // 判断一下内容是否为空 54 | if (StrUtil.isNotEmpty(authToken) && authToken.startsWith(jwtProperties.getTokenPrefix())) { 55 | // 去掉token前缀(Bearer ),拿到真实token 56 | authToken = authToken.substring(jwtProperties.getTokenPrefix().length()); 57 | 58 | // 拿到token里面的登录账号 59 | String loginAccount = jwtProvider.getSubjectFromToken(authToken); 60 | 61 | if (StrUtil.isNotEmpty(loginAccount) && SecurityContextHolder.getContext().getAuthentication() == null) { 62 | // 查询用户 63 | UserDetail userDetails = caffeineCache.get(CacheName.USER, loginAccount, UserDetail.class); 64 | 65 | // 拿到用户信息后验证用户信息与token 66 | if (userDetails != null && jwtProvider.validateToken(authToken, userDetails)) { 67 | 68 | // 组装authentication对象,构造参数是Principal Credentials 与 Authorities 69 | // 后面的拦截器里面会用到 grantedAuthorities 方法 70 | UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()); 71 | 72 | // 将authentication信息放入到上下文对象中 73 | SecurityContextHolder.getContext().setAuthentication(authentication); 74 | 75 | log.info("JWT过滤器通过校验请求头token自动登录成功, user : {}", userDetails.getUsername()); 76 | } 77 | } 78 | } 79 | 80 | chain.doFilter(request, response); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/component/RestAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.component; 2 | 3 | import org.example.security.auth.bo.ApiResult; 4 | import org.example.security.auth.util.JacksonUtil; 5 | import org.springframework.security.core.AuthenticationException; 6 | import org.springframework.security.web.AuthenticationEntryPoint; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | /** 14 | *

15 | * 用户未登录异常处理类 16 | *

17 | * 当用户尝试访问需要权限才能的REST资源而不提供Token或者Token错误或者过期时, 18 | * 将调用此方法发送401响应以及错误信息 19 | * 20 | * @author 和耳朵 21 | * @since 2020-06-30 22 | */ 23 | public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { 24 | 25 | @Override 26 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 27 | response.setHeader("Cache-Control", "no-cache"); 28 | response.setCharacterEncoding("UTF-8"); 29 | response.setContentType("application/json"); 30 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 31 | response.getWriter().println(JacksonUtil.toJsonString(ApiResult.fail(authException.getMessage()))); 32 | response.getWriter().flush(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/component/RestfulAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.component; 2 | 3 | import org.example.security.auth.bo.ApiResult; 4 | import org.example.security.auth.constant.ApiStatus; 5 | import org.example.security.auth.util.JacksonUtil; 6 | import org.springframework.security.access.AccessDeniedException; 7 | import org.springframework.security.web.access.AccessDeniedHandler; 8 | 9 | import javax.servlet.ServletException; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | /** 15 | *

16 | * 权限不足异常处理类 17 | *

18 | * 当用户尝试访问需要权限才能的REST资源而权限不足的时候, 19 | * 将调用此方法发送403响应以及错误信息 20 | * 21 | * @author 和耳朵 22 | * @since 2020-06-30 23 | */ 24 | public class RestfulAccessDeniedHandler implements AccessDeniedHandler { 25 | 26 | @Override 27 | public void handle(HttpServletRequest request, 28 | HttpServletResponse response, 29 | AccessDeniedException e) throws IOException, ServletException { 30 | 31 | response.setHeader("Cache-Control", "no-cache"); 32 | response.setCharacterEncoding("UTF-8"); 33 | response.setContentType("application/json"); 34 | response.setStatus(HttpServletResponse.SC_FORBIDDEN); 35 | response.getWriter().println(JacksonUtil.toJsonString(ApiResult.instance(ApiStatus.NOT_PERMISSION))); 36 | response.getWriter().flush(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/config/JacksonConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.SerializationFeature; 8 | import com.fasterxml.jackson.databind.module.SimpleModule; 9 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 10 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 11 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 12 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 13 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; 14 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 15 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 16 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; 17 | import org.example.security.auth.util.JacksonUtil; 18 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 19 | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import org.springframework.context.annotation.Primary; 23 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 24 | 25 | import java.math.BigInteger; 26 | import java.time.LocalDate; 27 | import java.time.LocalDateTime; 28 | import java.time.LocalTime; 29 | import java.time.ZoneId; 30 | import java.time.format.DateTimeFormatter; 31 | import java.util.Locale; 32 | import java.util.TimeZone; 33 | 34 | /** 35 | *

36 | * 针对Jackson做的个性化配置 37 | *

38 | * 39 | * @author rookie.zhang 40 | * @since 2020-03-13 41 | */ 42 | @Configuration 43 | public class JacksonConfig { 44 | 45 | @Bean 46 | public Jackson2ObjectMapperBuilderCustomizer customizer() { 47 | return builder -> { 48 | builder.locale(Locale.CHINA); 49 | builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault())); 50 | builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); 51 | 52 | JavaTimeModule javaTimeModule = new JavaTimeModule(); 53 | javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 54 | javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 55 | javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); 56 | javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 57 | javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); 58 | javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); 59 | 60 | builder.modules(javaTimeModule); 61 | }; 62 | } 63 | 64 | 65 | @Bean 66 | @Primary 67 | @ConditionalOnMissingBean(ObjectMapper.class) 68 | public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) 69 | { 70 | ObjectMapper objectMapper = builder.createXmlMapper(false).build(); 71 | 72 | // 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化 73 | // Include.Include.ALWAYS 默认 74 | // Include.NON_DEFAULT 属性为默认值不序列化 75 | // Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的 76 | // Include.NON_NULL 属性为NULL 不序列化 77 | objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); 78 | // 允许出现特殊字符和转义符 79 | objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); 80 | // 允许出现单引号 81 | objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 82 | objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 83 | objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 84 | 85 | SimpleModule simpleModule = new SimpleModule(); 86 | /** 87 | * 将Long,BigInteger序列化的时候,转化为String 88 | */ 89 | simpleModule.addSerializer(Long.class, ToStringSerializer.instance); 90 | simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); 91 | simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance); 92 | 93 | objectMapper.registerModule(simpleModule); 94 | 95 | // 将工具类中的 objectMapper 换为 Spring 中的 objectMapper 96 | JacksonUtil.objectMapper = objectMapper; 97 | return objectMapper; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/config/MyConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.config; 2 | 3 | import com.github.benmanes.caffeine.cache.CacheLoader; 4 | import com.github.benmanes.caffeine.cache.Caffeine; 5 | import org.example.security.auth.constant.CacheName; 6 | import org.example.security.auth.properties.JwtProperties; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.cache.CacheManager; 9 | import org.springframework.cache.caffeine.CaffeineCacheManager; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.web.cors.CorsConfiguration; 13 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 14 | import org.springframework.web.filter.CorsFilter; 15 | 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | *

20 | * 配置文件 21 | *

22 | * 23 | * @author 和耳朵 24 | * @since 2020-06-30 25 | */ 26 | @Configuration 27 | public class MyConfig { 28 | @Autowired 29 | private JwtProperties jwtProperties; 30 | 31 | @Bean 32 | public CorsFilter corsFilter() { 33 | final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); 34 | final CorsConfiguration corsConfiguration = new CorsConfiguration(); 35 | /*是否允许请求带有验证信息*/ 36 | corsConfiguration.setAllowCredentials(true); 37 | /*允许访问的客户端域名*/ 38 | corsConfiguration.addAllowedOrigin("*"); 39 | /*允许服务端访问的客户端请求头*/ 40 | corsConfiguration.addAllowedHeader("*"); 41 | /*允许访问的方法名,GET POST等*/ 42 | corsConfiguration.addAllowedMethod("*"); 43 | urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); 44 | return new CorsFilter(urlBasedCorsConfigurationSource); 45 | } 46 | 47 | /** 48 | * 创建基于Caffeine的Cache Manager 49 | * 50 | * @return 51 | */ 52 | @Bean("caffeineCacheManager") 53 | public CacheManager caffeineCacheManager() { 54 | CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager(); 55 | Caffeine caffeine = Caffeine.newBuilder() 56 | // 设置初始容量 57 | .initialCapacity(5) 58 | // 失效策略 写入后按时间失效 59 | .refreshAfterWrite(jwtProperties.getExpirationTime(), TimeUnit.SECONDS); 60 | caffeineCacheManager.setCaffeine(caffeine); 61 | caffeineCacheManager.setCacheLoader(cacheLoader()); 62 | caffeineCacheManager.setCacheNames(CacheName.getCacheNames()); 63 | return caffeineCacheManager; 64 | } 65 | 66 | @Bean 67 | public CacheLoader cacheLoader() { 68 | return new CacheLoader() { 69 | @Override 70 | public Object load(Object key) throws Exception { 71 | return null; 72 | } 73 | 74 | // 达到配置文件中的refreshAfterWrite所指定的时候回处罚这个事件方法 75 | @Override 76 | public Object reload(Object key, Object oldValue) throws Exception { 77 | return oldValue; 78 | } 79 | }; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/config/SpringSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.config; 2 | 3 | import org.example.security.auth.component.AccessDecisionProcessor; 4 | import org.example.security.auth.component.JwtAuthenticationTokenFilter; 5 | import org.example.security.auth.component.RestAuthenticationEntryPoint; 6 | import org.example.security.auth.component.RestfulAccessDeniedHandler; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.access.AccessDecisionManager; 10 | import org.springframework.security.access.AccessDecisionVoter; 11 | import org.springframework.security.access.vote.UnanimousBased; 12 | import org.springframework.security.authentication.AuthenticationManager; 13 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 15 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 16 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 17 | import org.springframework.security.config.http.SessionCreationPolicy; 18 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 19 | import org.springframework.security.crypto.password.PasswordEncoder; 20 | import org.springframework.security.web.FilterInvocation; 21 | import org.springframework.security.web.access.expression.WebExpressionVoter; 22 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 23 | 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | /** 28 | *

29 | * SpringSecurity配置文件 30 | *

31 | * 32 | * @author 和耳朵 33 | * @since 2020-06-30 34 | */ 35 | @EnableGlobalMethodSecurity(prePostEnabled = true) 36 | @EnableWebSecurity 37 | public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { 38 | 39 | /** 40 | * 自定义访问控制,默认是所有访问都要经过认证。 41 | * 42 | * @param http 43 | * @throws Exception 44 | */ 45 | @Override 46 | protected void configure(HttpSecurity http) throws Exception { 47 | 48 | http.authorizeRequests() 49 | // 放行所有OPTIONS请求 50 | .antMatchers(HttpMethod.OPTIONS).permitAll() 51 | // 放行登录方法 52 | .antMatchers("/api/auth/login").permitAll() 53 | .antMatchers("/api/anon").permitAll() 54 | // 其他请求都需要认证后才能访问 55 | .anyRequest().authenticated() 56 | // 使用自定义的 accessDecisionManager 57 | .accessDecisionManager(accessDecisionManager()) 58 | .and() 59 | // 添加未登录与权限不足异常处理器 60 | .exceptionHandling() 61 | .accessDeniedHandler(restfulAccessDeniedHandler()) 62 | .authenticationEntryPoint(restAuthenticationEntryPoint()) 63 | .and() 64 | // 将自定义的JWT过滤器放到过滤链中 65 | .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class) 66 | // 打开Spring Security的跨域 67 | .cors() 68 | .and() 69 | // 关闭CSRF 70 | .csrf().disable() 71 | // 关闭Session机制 72 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 73 | } 74 | 75 | @Bean 76 | public PasswordEncoder passwordEncoder() { 77 | return new BCryptPasswordEncoder(); 78 | } 79 | 80 | @Bean 81 | public RestfulAccessDeniedHandler restfulAccessDeniedHandler() { 82 | return new RestfulAccessDeniedHandler(); 83 | } 84 | 85 | @Bean 86 | public RestAuthenticationEntryPoint restAuthenticationEntryPoint() { 87 | return new RestAuthenticationEntryPoint(); 88 | } 89 | 90 | @Bean 91 | public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() { 92 | return new JwtAuthenticationTokenFilter(); 93 | } 94 | 95 | @Bean 96 | public AuthenticationManager authenticationManager() throws Exception { 97 | return super.authenticationManager(); 98 | } 99 | 100 | @Bean 101 | public AccessDecisionVoter accessDecisionProcessor() { 102 | return new AccessDecisionProcessor(); 103 | } 104 | 105 | @Bean 106 | public AccessDecisionManager accessDecisionManager() { 107 | // 构造一个新的AccessDecisionManager 放入两个投票器 108 | List> decisionVoters = Arrays.asList(new WebExpressionVoter(), accessDecisionProcessor()); 109 | return new UnanimousBased(decisionVoters); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/constant/ApiStatus.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.constant; 2 | 3 | public enum ApiStatus { 4 | 5 | /** 6 | * 业务请求成功统一编码 7 | */ 8 | OK(200, "请求成功"), 9 | 10 | /** 11 | * 业务请求失败统一编码 12 | */ 13 | FAIL(400, "请求失败"), 14 | 15 | /** 16 | * 请求非法统一编码 17 | */ 18 | UNAUTHORIZED(401, "非法访问"), 19 | 20 | /** 21 | * 权限不足统一编码 22 | */ 23 | NOT_PERMISSION(403, "无权访问"), 24 | 25 | /** 26 | * 资源不存在统一编码 27 | */ 28 | NOT_FOUND(404, "请求资源不存在"), 29 | 30 | /** 31 | * 业务服务异常统一编码 32 | */ 33 | ERROR(500, "服务发生异常,请联系管理员。"); 34 | 35 | 36 | private final int code; 37 | private final String msg; 38 | 39 | ApiStatus(int code, String msg) { 40 | this.code = code; 41 | this.msg = msg; 42 | } 43 | 44 | public int getCode() { 45 | return code; 46 | } 47 | 48 | public String getMsg() { 49 | return msg; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/constant/CacheName.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.constant; 2 | 3 | import lombok.AllArgsConstructor; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @AllArgsConstructor 9 | public enum CacheName { 10 | 11 | USER("USER"), 12 | PERMISSION("PERMISSION"); 13 | 14 | private final String cacheName; 15 | 16 | public static List getCacheNames() { 17 | List cacheNameList = new ArrayList<>(CacheName.values().length); 18 | CacheName[] values = CacheName.values(); 19 | for (int i = 0; i < CacheName.values().length; i++) { 20 | cacheNameList.add(values[i].cacheName); 21 | } 22 | return cacheNameList; 23 | } 24 | 25 | public String getCacheName() { 26 | return cacheName; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/constant/RoleEnum.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.constant; 2 | 3 | import lombok.AllArgsConstructor; 4 | 5 | @AllArgsConstructor 6 | public enum RoleEnum { 7 | 8 | USER("USER"), 9 | ADMIN("ADMIN"); 10 | 11 | private final String value; 12 | 13 | public String getValue() { 14 | return this.value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/entity/PermissionInfo.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | *

13 | * 用户对象 14 | *

15 | * 16 | * @author 和耳朵 17 | * @since 2020-07-05 18 | */ 19 | @Data 20 | @EqualsAndHashCode(callSuper = false) 21 | @Accessors(chain = true) 22 | public class PermissionInfo implements Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | @TableId(value = "id") 27 | private String id; 28 | 29 | private String permissionName; 30 | 31 | private String permissionUri; 32 | 33 | private String permissionMethod; 34 | 35 | private Integer activeStatus; 36 | 37 | private LocalDateTime createTime; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/entity/RoleInfo.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | *

13 | * 14 | *

15 | * 16 | * @author 和耳朵 17 | * @since 2020-06-30 18 | */ 19 | @Data 20 | @EqualsAndHashCode(callSuper = false) 21 | @Accessors(chain = true) 22 | public class RoleInfo implements Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | @TableId(value = "id") 27 | private String id; 28 | 29 | private String roleName; 30 | 31 | private String roleCode; 32 | 33 | private String roleRemark; 34 | 35 | private Integer activeStatus; 36 | 37 | private LocalDateTime createTime; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/entity/UserDetail.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.entity; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | 8 | import java.io.Serializable; 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | import java.util.List; 12 | 13 | @Data 14 | public class UserDetail implements Serializable, UserDetails { 15 | private static final long serialVersionUID = 1L; 16 | 17 | private UserInfo userInfo; 18 | private List roleInfoList; 19 | private Collection grantedAuthorities; 20 | private List roles; 21 | 22 | public String getUserId() { 23 | return this.userInfo.getId(); 24 | } 25 | 26 | @Override 27 | public Collection getAuthorities() { 28 | if (grantedAuthorities != null) return this.grantedAuthorities; 29 | List grantedAuthorities = new ArrayList<>(); 30 | List authorities = new ArrayList<>(); 31 | roleInfoList.forEach(role -> { 32 | authorities.add(role.getRoleCode()); 33 | grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleCode())); 34 | }); 35 | this.grantedAuthorities = grantedAuthorities; 36 | this.roles = authorities; 37 | return this.grantedAuthorities; 38 | } 39 | 40 | @Override 41 | public String getPassword() { 42 | return this.userInfo.getPassword(); 43 | } 44 | 45 | @Override 46 | public String getUsername() { 47 | return this.userInfo.getUsername(); 48 | } 49 | 50 | /** 51 | * 账户是否没过期 52 | * 53 | * @return boolean 54 | */ 55 | @Override 56 | public boolean isAccountNonExpired() { 57 | return true; 58 | } 59 | 60 | /** 61 | * 账户是否没被锁定 62 | * 63 | * @return boolean 64 | */ 65 | @Override 66 | public boolean isAccountNonLocked() { 67 | return true; 68 | } 69 | 70 | /** 71 | * 账户凭据是否没过期 72 | * 73 | * @return boolean 74 | */ 75 | @Override 76 | public boolean isCredentialsNonExpired() { 77 | return true; 78 | } 79 | 80 | /** 81 | * 账户是否启用 82 | * 83 | * @return boolean 84 | */ 85 | @Override 86 | public boolean isEnabled() { 87 | return true; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/entity/UserInfo.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableId; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.experimental.Accessors; 7 | 8 | import java.io.Serializable; 9 | import java.time.LocalDateTime; 10 | 11 | /** 12 | *

13 | * 用户对象 14 | *

15 | * 16 | * @author 和耳朵 17 | * @since 2020-06-30 18 | */ 19 | @Data 20 | @EqualsAndHashCode(callSuper = false) 21 | @Accessors(chain = true) 22 | public class UserInfo implements Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | @TableId(value = "id") 27 | private String id; 28 | 29 | private String username; 30 | 31 | private String password; 32 | 33 | private Integer activeStatus; 34 | 35 | private LocalDateTime createTime; 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/mapper/PermissionMapper.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.example.security.auth.bo.PermissionInfoBO; 5 | import org.example.security.auth.entity.PermissionInfo; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | *

11 | * Mapper 接口 12 | *

13 | * 14 | * @author 和耳朵 15 | * @since 2020-06-30 16 | */ 17 | public interface PermissionMapper extends BaseMapper { 18 | 19 | List listPermissionInfoBO(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/mapper/RoleInfoMapper.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.example.security.auth.entity.RoleInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | *

10 | * Mapper 接口 11 | *

12 | * 13 | * @author 和耳朵 14 | * @since 2020-06-30 15 | */ 16 | public interface RoleInfoMapper extends BaseMapper { 17 | 18 | List listRoleByUserId(String userId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import org.example.security.auth.entity.UserInfo; 5 | 6 | /** 7 | *

8 | * User Mapper 接口 9 | *

10 | * 11 | * @author 和耳朵 12 | * @since 2020-06-30 13 | */ 14 | public interface UserMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/properties/JwtProperties.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Data 8 | @Component 9 | @ConfigurationProperties(prefix = "jwt") 10 | public class JwtProperties { 11 | 12 | /** 13 | * 密匙 14 | */ 15 | private String apiSecretKey = "JWT_SECRET_KEY"; 16 | 17 | /** 18 | * 过期时间-默认半个小时 19 | */ 20 | private Long expirationTime = 1800L; 21 | 22 | /** 23 | * 默认存放token的请求头 24 | */ 25 | private String requestHeader = "Authorization"; 26 | 27 | /** 28 | * 默认token前缀 29 | */ 30 | private String tokenPrefix = "Bearer "; 31 | } 32 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/provider/AuthProvider.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.provider; 2 | 3 | import org.example.security.auth.entity.UserDetail; 4 | import org.example.security.auth.entity.UserInfo; 5 | import org.springframework.security.core.context.SecurityContextHolder; 6 | 7 | import java.util.List; 8 | 9 | public class AuthProvider { 10 | public static UserDetail getUserDetail() { 11 | return (UserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 12 | } 13 | 14 | public static UserInfo getUserInfo() { 15 | return ((UserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserInfo(); 16 | } 17 | 18 | public static String getLoginAccount() { 19 | return ((UserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername(); 20 | } 21 | 22 | public static String getUserId() { 23 | return ((UserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserId(); 24 | } 25 | 26 | public static List getAuthorities() { 27 | return ((UserDetail) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getRoles(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/provider/JwtProvider.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.provider; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import io.jsonwebtoken.Claims; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.example.security.auth.bo.AccessToken; 9 | import org.example.security.auth.properties.JwtProperties; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.stereotype.Component; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.util.Date; 16 | 17 | /** 18 | *

19 | * JWT组件 20 | *

21 | *

22 | * 一个完整的JwtToken由三部分组成:头部+负载信息+签名 23 | * header 存放JwtToken签名的算法 | token的类型:{"alg": "HS512","typ": "JWT"} 24 | * payload 主要存放用户名、创建时间、生成时间:{"sub":"wang","created":1489079981393,"exp":1489684781} 25 | * signature 生成算法:HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret) 26 | *

27 | * Created by 和耳朵 28 | */ 29 | @Slf4j 30 | @Component 31 | public class JwtProvider { 32 | @Autowired 33 | private JwtProperties jwtProperties; 34 | 35 | /** 36 | * 从请求中拿到token 37 | */ 38 | public String getToken(HttpServletRequest request) { 39 | return request.getHeader(jwtProperties.getRequestHeader()); 40 | } 41 | 42 | /** 43 | * 根据用户信息生成token 44 | */ 45 | public AccessToken createToken(UserDetails userDetails) { 46 | return createToken(userDetails.getUsername()); 47 | } 48 | 49 | /** 50 | * 生成token 51 | * 参数是我们想放入token中的字符串 52 | */ 53 | public AccessToken createToken(String subject) { 54 | // 当前时间 55 | final Date now = new Date(); 56 | // 过期时间 57 | final Date expirationDate = new Date(now.getTime() + jwtProperties.getExpirationTime() * 1000); 58 | 59 | String token = jwtProperties.getTokenPrefix() + Jwts.builder() 60 | .setSubject(subject) 61 | .setIssuedAt(now) 62 | .setExpiration(expirationDate) 63 | .signWith(SignatureAlgorithm.HS512, jwtProperties.getApiSecretKey()) 64 | .compact(); 65 | return AccessToken.builder().loginAccount(subject).token(token).expirationTime(expirationDate).build(); 66 | } 67 | 68 | /** 69 | * 验证token是否还有效 70 | *

71 | * 反解析出token中信息,然后与参数中的信息比较,再校验过期时间 72 | * 73 | * @param token 客户端传入的token 74 | * @param userDetails 从数据库中查询出来的用户信息 75 | */ 76 | public boolean validateToken(String token, UserDetails userDetails) { 77 | Claims claims = getClaimsFromToken(token); 78 | return claims.getSubject().equals(userDetails.getUsername()) && !isTokenExpired(claims); 79 | } 80 | 81 | 82 | /** 83 | * 刷新token 84 | * 过滤器会对请求进行验证,所以这里可以不必验证 85 | * 86 | * @param oldToken 带tokenHead的token 87 | */ 88 | public AccessToken refreshToken(String oldToken) { 89 | String token = oldToken.substring(jwtProperties.getTokenPrefix().length()); 90 | 91 | // token反解析 92 | Claims claims = getClaimsFromToken(token); 93 | 94 | //如果token在30分钟之内刚刷新过,返回原token 95 | if (tokenRefreshJustBefore(claims)) { 96 | return AccessToken.builder().loginAccount(claims.getSubject()).token(oldToken).expirationTime(claims.getExpiration()).build(); 97 | } else { 98 | return createToken(claims.getSubject()); 99 | } 100 | } 101 | 102 | /** 103 | * 判断token在指定时间内是否刚刚刷新过 104 | */ 105 | private boolean tokenRefreshJustBefore(Claims claims) { 106 | Date refreshDate = new Date(); 107 | //刷新时间在创建时间的指定时间内 108 | if (refreshDate.after(claims.getExpiration()) && refreshDate.before(DateUtil.offsetSecond(claims.getExpiration(), 1800))) { 109 | return true; 110 | } 111 | return false; 112 | } 113 | 114 | /** 115 | * 从token中拿到负载信息 116 | */ 117 | private Claims getClaimsFromToken(String token) { 118 | Claims claims = null; 119 | try { 120 | claims = Jwts.parser() 121 | .setSigningKey(jwtProperties.getApiSecretKey()) 122 | .parseClaimsJws(token) 123 | .getBody(); 124 | } catch (Exception e) { 125 | log.error("JWT反解析失败, token已过期或不正确, token : {}", token); 126 | } 127 | return claims; 128 | } 129 | 130 | 131 | /** 132 | * 从token中获取主题 133 | */ 134 | public String getSubjectFromToken(String token) { 135 | Claims claims = getClaimsFromToken(token); 136 | if (claims != null) { 137 | return claims.getSubject(); 138 | } else { 139 | return null; 140 | } 141 | } 142 | 143 | 144 | /** 145 | * 判断token是否已经过期 146 | */ 147 | private boolean isTokenExpired(Claims claims) { 148 | return claims.getExpiration().before(new Date()); 149 | } 150 | 151 | 152 | } 153 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/AuthService.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service; 2 | 3 | import org.example.security.auth.bo.ApiResult; 4 | 5 | public interface AuthService { 6 | 7 | ApiResult login(String loginAccount, String password); 8 | 9 | ApiResult logout(); 10 | 11 | ApiResult refreshToken(String token); 12 | } 13 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/PermissionService.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.example.security.auth.bo.PermissionInfoBO; 5 | import org.example.security.auth.entity.PermissionInfo; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | *

11 | * 服务类 12 | *

13 | * 14 | * @author 和耳朵 15 | * @since 2020-06-30 16 | */ 17 | public interface PermissionService extends IService { 18 | 19 | List listPermissionInfoBO(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/RoleInfoService.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.example.security.auth.entity.RoleInfo; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | *

10 | * 服务类 11 | *

12 | * 13 | * @author 和耳朵 14 | * @since 2020-06-30 15 | */ 16 | public interface RoleInfoService extends IService { 17 | 18 | List listRoleByUserId(String userId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/UserService.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service; 2 | 3 | import com.baomidou.mybatisplus.extension.service.IService; 4 | import org.example.security.auth.entity.UserInfo; 5 | 6 | /** 7 | *

8 | * User服务类 9 | *

10 | * 11 | * @author 和耳朵 12 | * @since 2020-06-30 13 | */ 14 | public interface UserService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/impl/AuthServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service.impl; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.example.security.auth.bo.AccessToken; 5 | import org.example.security.auth.bo.ApiResult; 6 | import org.example.security.auth.cache.Cache; 7 | import org.example.security.auth.constant.CacheName; 8 | import org.example.security.auth.entity.UserDetail; 9 | import org.example.security.auth.provider.AuthProvider; 10 | import org.example.security.auth.provider.JwtProvider; 11 | import org.example.security.auth.service.AuthService; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.security.authentication.AuthenticationManager; 14 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 15 | import org.springframework.security.core.Authentication; 16 | import org.springframework.security.core.context.SecurityContextHolder; 17 | import org.springframework.security.core.userdetails.UserDetails; 18 | import org.springframework.stereotype.Service; 19 | 20 | @Slf4j 21 | @Service 22 | public class AuthServiceImpl implements AuthService { 23 | @Autowired 24 | private AuthenticationManager authenticationManager; 25 | @Autowired 26 | private JwtProvider jwtProvider; 27 | @Autowired 28 | private Cache caffeineCache; 29 | 30 | 31 | @Override 32 | public ApiResult login(String loginAccount, String password) { 33 | 34 | log.debug("进入login方法"); 35 | // 1 创建UsernamePasswordAuthenticationToken 36 | UsernamePasswordAuthenticationToken usernameAuthentication = new UsernamePasswordAuthenticationToken(loginAccount, password); 37 | // 2 认证 38 | Authentication authentication = this.authenticationManager.authenticate(usernameAuthentication); 39 | // 3 保存认证信息 40 | SecurityContextHolder.getContext().setAuthentication(authentication); 41 | // 4 生成自定义token 42 | AccessToken accessToken = jwtProvider.createToken((UserDetails) authentication.getPrincipal()); 43 | 44 | UserDetail userDetail = (UserDetail) authentication.getPrincipal(); 45 | // 放入缓存 46 | caffeineCache.put(CacheName.USER, userDetail.getUsername(), userDetail); 47 | return ApiResult.ok(accessToken); 48 | } 49 | 50 | @Override 51 | public ApiResult logout() { 52 | caffeineCache.remove(CacheName.USER, AuthProvider.getLoginAccount()); 53 | SecurityContextHolder.clearContext(); 54 | return ApiResult.ok(); 55 | } 56 | 57 | @Override 58 | public ApiResult refreshToken(String token) { 59 | AccessToken accessToken = jwtProvider.refreshToken(token); 60 | UserDetail userDetail = caffeineCache.get(CacheName.USER, accessToken.getLoginAccount(), UserDetail.class); 61 | caffeineCache.put(CacheName.USER, accessToken.getLoginAccount(), userDetail); 62 | return ApiResult.ok(accessToken); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/impl/CustomUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.security.auth.entity.RoleInfo; 6 | import org.example.security.auth.entity.UserDetail; 7 | import org.example.security.auth.entity.UserInfo; 8 | import org.example.security.auth.service.RoleInfoService; 9 | import org.example.security.auth.service.UserService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 14 | import org.springframework.stereotype.Service; 15 | 16 | import java.util.List; 17 | 18 | @Slf4j 19 | @Service("userDetailsService") 20 | public class CustomUserDetailsService implements UserDetailsService { 21 | @Autowired 22 | private UserService userService; 23 | @Autowired 24 | private RoleInfoService roleInfoService; 25 | 26 | @Override 27 | public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { 28 | log.debug("开始登陆验证,用户名为: {}", s); 29 | 30 | // 根据用户名验证用户 31 | QueryWrapper queryWrapper = new QueryWrapper<>(); 32 | queryWrapper.lambda().eq(UserInfo::getUsername, s); 33 | UserInfo userInfo = userService.getOne(queryWrapper); 34 | if (userInfo == null) { 35 | throw new UsernameNotFoundException("用户名不存在,登陆失败。"); 36 | } 37 | 38 | // 构建UserDetail对象 39 | UserDetail userDetail = new UserDetail(); 40 | userDetail.setUserInfo(userInfo); 41 | List roleInfoList = roleInfoService.listRoleByUserId(userInfo.getId()); 42 | userDetail.setRoleInfoList(roleInfoList); 43 | return userDetail; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/impl/PermissionServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import org.example.security.auth.bo.PermissionInfoBO; 5 | import org.example.security.auth.entity.PermissionInfo; 6 | import org.example.security.auth.mapper.PermissionMapper; 7 | import org.example.security.auth.service.PermissionService; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | *

14 | * 服务实现类 15 | *

16 | * 17 | * @author 和耳朵 18 | * @since 2020-06-30 19 | */ 20 | @Service 21 | public class PermissionServiceImpl extends ServiceImpl implements PermissionService { 22 | 23 | 24 | @Override 25 | public List listPermissionInfoBO() { 26 | return getBaseMapper().listPermissionInfoBO(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/impl/RoleInfoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service.impl; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 5 | import org.example.security.auth.entity.RoleInfo; 6 | import org.example.security.auth.mapper.RoleInfoMapper; 7 | import org.example.security.auth.service.RoleInfoService; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | *

14 | * 服务实现类 15 | *

16 | * 17 | * @author 和耳朵 18 | * @since 2020-06-30 19 | */ 20 | @Service 21 | public class RoleInfoServiceImpl extends ServiceImpl implements RoleInfoService { 22 | 23 | @Override 24 | public List listRoleByUserId(String userId) { 25 | if (StrUtil.isEmpty(userId)) { 26 | return null; 27 | } 28 | return getBaseMapper().listRoleByUserId(userId); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.service.impl; 2 | 3 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.example.security.auth.entity.UserInfo; 6 | import org.example.security.auth.mapper.UserMapper; 7 | import org.example.security.auth.service.UserService; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | *

12 | * User服务实现类 13 | *

14 | * 15 | * @author 和耳朵 16 | * @since 2020-06-30 17 | */ 18 | @Slf4j 19 | @Service 20 | public class UserServiceImpl extends ServiceImpl implements UserService { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/java/org/example/security/auth/util/JacksonUtil.java: -------------------------------------------------------------------------------- 1 | package org.example.security.auth.util; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.JavaType; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import java.util.Collection; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | /** 13 | *

14 | * 封装了Jackson的json工具类,用于代替JSONObject 15 | *

16 | * 17 | * @author 和耳朵 18 | * @since 2020-06-30 19 | */ 20 | 21 | @Slf4j 22 | public class JacksonUtil { 23 | 24 | public static ObjectMapper objectMapper = new ObjectMapper(); 25 | 26 | 27 | /** 28 | * Java对象转JSON字符串 29 | * 30 | * @param object 31 | * @return 32 | */ 33 | public static String toJsonString(Object object) { 34 | try { 35 | return objectMapper.writeValueAsString(object); 36 | } catch (JsonProcessingException e) { 37 | log.error("The JacksonUtil toJsonString is error : \n", e); 38 | throw new RuntimeException(); 39 | } 40 | } 41 | 42 | /** 43 | * Java对象转JSON字符串 - 美化输出 44 | * 45 | * @param object 46 | * @return 47 | */ 48 | public static String toJsonStringWithPretty(Object object) { 49 | try { 50 | return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); 51 | } catch (JsonProcessingException e) { 52 | log.error("The JacksonUtil toJsonString is error : \n", e); 53 | throw new RuntimeException(); 54 | } 55 | } 56 | 57 | 58 | /** 59 | * Java对象转byte数组 60 | * 61 | * @param object 62 | * @return 63 | */ 64 | public static byte[] toJsonBytes(Object object) { 65 | try { 66 | return objectMapper.writeValueAsBytes(object); 67 | } catch (JsonProcessingException e) { 68 | log.error("The JacksonUtil toJsonBytes is error : \n", e); 69 | throw new RuntimeException(); 70 | } 71 | } 72 | 73 | 74 | /** 75 | * JSON字符串转对象 76 | * 77 | * @param json 78 | * @param clazz 79 | * @param 80 | * @return 81 | */ 82 | public static T parseObject(String json, Class clazz) { 83 | try { 84 | return objectMapper.readValue(json, clazz); 85 | } catch (Exception e) { 86 | log.error("The JacksonUtil parseObject is error, json str is {}, class name is {} \n", json, clazz.getName(), e); 87 | throw new RuntimeException(); 88 | } 89 | } 90 | 91 | /** 92 | * JSON字符串转List集合 93 | * 94 | * @param json 95 | * @param elementClasses 96 | * @param 97 | * @return 98 | */ 99 | @SafeVarargs 100 | public static List parseList(String json, Class... elementClasses) { 101 | try { 102 | return objectMapper.readValue(json, getCollectionType(objectMapper, List.class, elementClasses)); 103 | } catch (Exception e) { 104 | log.error("The JacksonUtil parseList is error, json str is {}, element class name is {} \n", 105 | json, elementClasses.getClass().getName(), e); 106 | throw new RuntimeException(); 107 | } 108 | } 109 | 110 | 111 | /** 112 | * JSON字符串转Set集合 113 | * 114 | * @param json 115 | * @param elementClasses 116 | * @param 117 | * @return 118 | */ 119 | @SafeVarargs 120 | public static Set parseSet(String json, Class... elementClasses) { 121 | try { 122 | return objectMapper.readValue(json, getCollectionType(objectMapper, Set.class, elementClasses)); 123 | } catch (Exception e) { 124 | log.error("The JacksonUtil parseSet is error, json str is {}, element class name is {} \n", 125 | json, elementClasses.getClass().getName(), e); 126 | throw new RuntimeException(); 127 | } 128 | } 129 | 130 | /** 131 | * JSON字符串转Collection集合 132 | * 133 | * @param json 134 | * @param elementClasses 135 | * @param 136 | * @return 137 | */ 138 | @SafeVarargs 139 | public static Collection parseCollection(String json, Class... elementClasses) { 140 | try { 141 | return objectMapper.readValue(json, getCollectionType(objectMapper, Collection.class, elementClasses)); 142 | } catch (Exception e) { 143 | log.error("The JacksonUtil parseCollection is error, json str is {}, element class name is {} \n", 144 | json, elementClasses.getClass().getName(), e); 145 | throw new RuntimeException(); 146 | } 147 | } 148 | 149 | /** 150 | * 获取泛型的Collection Type 151 | * 152 | * @param collectionClass 泛型的Collection 153 | * @param elementClasses 元素类 154 | * @return JavaType Java类型 155 | * @since 1.0 156 | */ 157 | public static JavaType getCollectionType(ObjectMapper mapper, Class collectionClass, Class... elementClasses) { 158 | return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | servlet: 3 | # 请求数据转码UTF8 4 | encoding: 5 | enabled: true 6 | charset: UTF-8 7 | force: true 8 | 9 | ############################# spring config start ############################ 10 | 11 | spring: 12 | mvc: 13 | # 打印 web-log 14 | log-request-details: true 15 | # 配置 HikariCP 数据源 16 | datasource: 17 | type: com.zaxxer.hikari.HikariDataSource # 数据源类型:HikariCP 18 | hikari: 19 | connection-timeout: 30000 # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQL异常 20 | minimum-idle: 10 # 最小连接数 21 | maximum-pool-size: 20 # 最大连接数 22 | auto-commit: true # 自动提交 23 | idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟 24 | pool-name: DateSourceHikariCP # 连接池名字 25 | max-lifetime: 1800000 # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟 26 | connection-test-query: SELECT 1 27 | 28 | # 配置数据库连接 29 | url: jdbc:mysql://localhost:3306/spring-boot-learning?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true 30 | username: root 31 | password: 256156 32 | driver-class-name: com.mysql.jdbc.Driver 33 | 34 | ############################# spring config end ############################ 35 | 36 | ############################### mybatis-plus start ################################# 37 | mybatis-plus: 38 | # 检查XML映射文件 39 | check-config-location: true 40 | configuration: 41 | # 驼峰命名 42 | map-underscore-to-camel-case: true 43 | global-config: 44 | db-config: 45 | # 设置逻辑删除 46 | logic-delete-value: 0 47 | logic-not-delete-value: 1 48 | mapper-locations: classpath*:mapper/**/*Mapper.xml 49 | logging: 50 | level: 51 | web: debug 52 | org: 53 | example: 54 | security: debug 55 | 56 | ################################ mybatis-plus end ################################## -------------------------------------------------------------------------------- /spring-security-demo/src/main/resources/doc/spring-boot-learning.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : 阿里云 5 | Source Server Type : MySQL 6 | Source Server Version : 50560 7 | Source Host : 47.102.159.163:3306 8 | Source Schema : spring-boot-learning 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50560 12 | File Encoding : 65001 13 | 14 | Date: 08/07/2020 09:54:15 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for permission_info 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `permission_info`; 24 | CREATE TABLE `permission_info` ( 25 | `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '权限表主键', 26 | `permission_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限名称', 27 | `permission_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限URI', 28 | `permission_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限方法类型', 29 | `active_status` tinyint(4) NOT NULL COMMENT '逻辑删除', 30 | `create_time` datetime NOT NULL COMMENT '权限创建时间', 31 | PRIMARY KEY (`id`) USING BTREE 32 | ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT; 33 | 34 | -- ---------------------------- 35 | -- Records of permission_info 36 | -- ---------------------------- 37 | INSERT INTO `permission_info` VALUES (1, 'USER用户测试', '/api/user', 'GET', 1, '2020-07-05 18:13:47'); 38 | INSERT INTO `permission_info` VALUES (2, 'ADMIN测试', '/api/admin', 'GET', 1, '2020-07-05 18:14:11'); 39 | 40 | -- ---------------------------- 41 | -- Table structure for role_info 42 | -- ---------------------------- 43 | DROP TABLE IF EXISTS `role_info`; 44 | CREATE TABLE `role_info` ( 45 | `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '角色表主键', 46 | `role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色名称', 47 | `role_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色编码', 48 | `role_remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '角色备注', 49 | `active_status` tinyint(4) NOT NULL COMMENT '逻辑删除', 50 | `create_time` datetime NOT NULL COMMENT '角色创建时间', 51 | PRIMARY KEY (`id`) USING BTREE 52 | ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT; 53 | 54 | -- ---------------------------- 55 | -- Records of role_info 56 | -- ---------------------------- 57 | INSERT INTO `role_info` VALUES (1, 'USER', 'USER', '普通用户级别', 1, '2020-07-02 10:34:37'); 58 | INSERT INTO `role_info` VALUES (2, 'ADMIN', 'ADMIN', '超级管理员', 1, '2020-07-05 18:12:57'); 59 | 60 | -- ---------------------------- 61 | -- Table structure for role_permission_link 62 | -- ---------------------------- 63 | DROP TABLE IF EXISTS `role_permission_link`; 64 | CREATE TABLE `role_permission_link` ( 65 | `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '角色与权限关联表', 66 | `role_id` bigint(20) NOT NULL COMMENT '角色表主键', 67 | `permission_id` bigint(20) NOT NULL COMMENT '权限表主键', 68 | `create_time` datetime NOT NULL COMMENT '创建时间 ', 69 | PRIMARY KEY (`id`) USING BTREE 70 | ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT; 71 | 72 | -- ---------------------------- 73 | -- Records of role_permission_link 74 | -- ---------------------------- 75 | INSERT INTO `role_permission_link` VALUES (1, 1, 1, '2020-07-05 18:14:25'); 76 | INSERT INTO `role_permission_link` VALUES (2, 2, 2, '2020-07-05 18:14:31'); 77 | 78 | -- ---------------------------- 79 | -- Table structure for user_info 80 | -- ---------------------------- 81 | DROP TABLE IF EXISTS `user_info`; 82 | CREATE TABLE `user_info` ( 83 | `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '用户表主键', 84 | `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名称', 85 | `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户密码', 86 | `active_status` tinyint(4) NOT NULL COMMENT '逻辑删除', 87 | `create_time` datetime NOT NULL COMMENT '创建时间', 88 | PRIMARY KEY (`id`) USING BTREE 89 | ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT; 90 | 91 | -- ---------------------------- 92 | -- Records of user_info 93 | -- ---------------------------- 94 | INSERT INTO `user_info` VALUES (1, 'user', '$2a$10$ms3nmhX6O3J09oQDJlwC7eywfmuLkKiGKadvIno.SIBStlhXtvwGW', 1, '2020-07-02 10:34:07'); 95 | 96 | -- ---------------------------- 97 | -- Table structure for user_role_link 98 | -- ---------------------------- 99 | DROP TABLE IF EXISTS `user_role_link`; 100 | CREATE TABLE `user_role_link` ( 101 | `id` int(20) NOT NULL AUTO_INCREMENT COMMENT '用户与角色关联表主键', 102 | `user_id` bigint(20) NOT NULL COMMENT '用户表主键', 103 | `role_id` bigint(20) NOT NULL COMMENT '角色表主键', 104 | `create_time` datetime NOT NULL COMMENT '关联创建时间', 105 | PRIMARY KEY (`id`) USING BTREE 106 | ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT; 107 | 108 | -- ---------------------------- 109 | -- Records of user_role_link 110 | -- ---------------------------- 111 | INSERT INTO `user_role_link` VALUES (1, 1, 1, '2020-07-02 10:34:53'); 112 | 113 | SET FOREIGN_KEY_CHECKS = 1; 114 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/resources/mapper/PermissionMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /spring-security-demo/src/main/resources/mapper/RoleInfoMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | id, role_name, role_code, role_remark, active_status, create_time 18 | 19 | 20 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /spring-security-demo/src/test/java/org/example/security/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.example.security; 2 | 3 | import org.example.security.auth.entity.UserInfo; 4 | import org.example.security.auth.service.UserService; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.security.crypto.password.PasswordEncoder; 9 | 10 | import java.time.LocalDateTime; 11 | 12 | @SpringBootTest 13 | public class UserServiceTest { 14 | @Autowired 15 | private UserService userService; 16 | @Autowired 17 | private PasswordEncoder passwordEncoder; 18 | 19 | @Test 20 | public void insertData() { 21 | UserInfo userInfo = new UserInfo(); 22 | userInfo.setUsername("test"); 23 | userInfo.setPassword(passwordEncoder.encode("123456")); 24 | userInfo.setActiveStatus(1); 25 | userInfo.setCreateTime(LocalDateTime.now()); 26 | 27 | boolean save = userService.save(userInfo); 28 | if (save) { 29 | System.out.println("插入成功"); 30 | } 31 | } 32 | 33 | @Test 34 | public void encode() { 35 | System.out.println(passwordEncoder.encode("123456")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-websocket/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-websocket/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.0-SNAPSHOT 9 | 10 | 11 | com.example 12 | spring-websocket 13 | 0.0.1-SNAPSHOT 14 | spring-websocket 15 | spring-websocket 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-websocket 27 | 28 | 29 | com.corundumstudio.socketio 30 | netty-socketio 31 | 1.7.19 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-devtools 36 | runtime 37 | true 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-test 42 | test 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | spring-milestones 57 | Spring Milestones 58 | https://repo.spring.io/milestone 59 | 60 | false 61 | 62 | 63 | 64 | spring-snapshots 65 | Spring Snapshots 66 | https://repo.spring.io/snapshot 67 | 68 | false 69 | 70 | 71 | 72 | 73 | 74 | spring-milestones 75 | Spring Milestones 76 | https://repo.spring.io/milestone 77 | 78 | false 79 | 80 | 81 | 82 | spring-snapshots 83 | Spring Snapshots 84 | https://repo.spring.io/snapshot 85 | 86 | false 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/SpringWebsocketApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringWebsocketApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringWebsocketApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/TestController.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class TestController { 8 | 9 | @GetMapping("/test") 10 | public String test() throws InterruptedException { 11 | Thread.sleep(100); 12 | return "hello"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/j2ee/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.j2ee; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.server.standard.ServerEndpointExporter; 6 | 7 | @Configuration 8 | public class WebSocketConfig { 9 | 10 | @Bean 11 | public ServerEndpointExporter serverEndpointExporter() { 12 | return new ServerEndpointExporter(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/j2ee/WebSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.j2ee; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.websocket.*; 6 | import javax.websocket.server.PathParam; 7 | import javax.websocket.server.ServerEndpoint; 8 | import java.io.IOException; 9 | 10 | @Component 11 | @ServerEndpoint("/j2ee-ws/{msg}") 12 | public class WebSocketServer { 13 | public WebSocketServer() { 14 | System.out.println("初始化WebSocketServer"); 15 | } 16 | 17 | //建立连接成功调用 18 | @OnOpen 19 | public void onOpen(Session session, @PathParam(value = "msg") String msg){ 20 | System.out.println("WebSocketServer 收到连接: " + session.getId() + ", 当前消息:" + msg); 21 | } 22 | 23 | //收到客户端信息 24 | @OnMessage 25 | public void onMessage(Session session, String message) throws IOException { 26 | message = "WebSocketServer 收到连接:" + session.getId() + ",已收到消息:" + message; 27 | System.out.println(message); 28 | session.getBasicRemote().sendText(message); 29 | } 30 | 31 | //连接关闭 32 | @OnClose 33 | public void onclose(Session session){ 34 | 35 | System.out.println("连接关闭"); 36 | } 37 | 38 | //错误时调用 39 | @OnError 40 | public void onError(Session session, Throwable throwable){ 41 | 42 | System.out.println("发生错误"); 43 | throwable.printStackTrace(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/socketio/SocketIoConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.socketio; 2 | 3 | import com.corundumstudio.socketio.SocketIOServer; 4 | import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class SocketIoConfig { 10 | 11 | @Bean 12 | public SocketIOServer socketIOServer() { 13 | com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); 14 | 15 | config.setHostname("127.0.0.1"); 16 | config.setPort(8001); 17 | config.setContext("/socketio-ws"); 18 | SocketIOServer server = new SocketIOServer(config); 19 | server.start(); 20 | return server; 21 | } 22 | 23 | @Bean 24 | public SpringAnnotationScanner springAnnotationScanner() { 25 | return new SpringAnnotationScanner(socketIOServer()); 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/socketio/SocketIoHandle.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.socketio; 2 | 3 | import com.corundumstudio.socketio.AckRequest; 4 | import com.corundumstudio.socketio.SocketIOClient; 5 | import com.corundumstudio.socketio.annotation.OnConnect; 6 | import com.corundumstudio.socketio.annotation.OnDisconnect; 7 | import com.corundumstudio.socketio.annotation.OnEvent; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | public class SocketIoHandle { 12 | 13 | /** 14 | * 客户端连上socket服务器时执行此事件 15 | * @param client 16 | */ 17 | @OnConnect 18 | public void onConnect(SocketIOClient client) { 19 | System.out.println("SocketIoHandle 收到连接:" + client.getSessionId()); 20 | } 21 | 22 | /** 23 | * 客户端断开socket服务器时执行此事件 24 | * @param client 25 | */ 26 | @OnDisconnect 27 | public void onDisconnect(SocketIOClient client) { 28 | System.out.println("当前链接关闭:" + client.getSessionId()); 29 | } 30 | 31 | @OnEvent( value = "onMsg") 32 | public void onMessage(SocketIOClient client, AckRequest request, Object data) { 33 | System.out.println("SocketIoHandle 收到消息:" + data); 34 | request.isAckRequested(); 35 | client.sendEvent("chatMsg", "我是 NettySocketIO 后端服务,已收到连接:" + client.getSessionId()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/spring/SpringSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.spring; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.socket.config.annotation.EnableWebSocket; 6 | import org.springframework.web.socket.config.annotation.WebSocketConfigurer; 7 | import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; 8 | 9 | @Configuration 10 | @EnableWebSocket 11 | public class SpringSocketConfig implements WebSocketConfigurer { 12 | 13 | @Autowired 14 | private SpringSocketHandle springSocketHandle; 15 | 16 | @Override 17 | public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 18 | registry.addHandler(springSocketHandle, "/spring-ws").setAllowedOrigins("*"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spring-websocket/src/main/java/com/example/springwebsocket/spring/SpringSocketHandle.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket.spring; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.socket.*; 5 | 6 | @Component 7 | public class SpringSocketHandle implements WebSocketHandler { 8 | 9 | @Override 10 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { 11 | System.out.println("SpringSocketHandle, 收到新的连接: " + session.getId()); 12 | } 13 | 14 | @Override 15 | public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { 16 | String msg = "SpringSocketHandle, 连接:" + session.getId() + ",已收到消息。"; 17 | System.out.println(msg); 18 | session.sendMessage(new TextMessage(msg)); 19 | } 20 | 21 | @Override 22 | public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { 23 | System.out.println("WS 连接发生错误"); 24 | } 25 | 26 | @Override 27 | public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { 28 | System.out.println("WS 关闭连接"); 29 | } 30 | 31 | // 支持分片消息 32 | @Override 33 | public boolean supportsPartialMessages() { 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spring-websocket/src/main/resources/templates/J2eeIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket 6 | 7 | 8 | 9 | 71 | 72 |
73 |

Hello Socket

74 |
75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 | 83 |
84 | 85 |
86 | 87 | 88 | 89 | 194 | -------------------------------------------------------------------------------- /spring-websocket/src/main/resources/templates/SocketIoIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket 6 | 7 | 8 | 70 | 71 |
72 |

Hello Socket

73 |
74 |
75 | 76 |
77 | 78 |
79 | 80 | 81 | 82 |
83 | 84 |
85 | 86 | 87 | 196 | -------------------------------------------------------------------------------- /spring-websocket/src/main/resources/templates/SpringIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket 6 | 7 | 8 | 9 | 71 | 72 |
73 |

Hello Socket

74 |
75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 | 83 |
84 | 85 |
86 | 87 | 88 | 89 | 194 | -------------------------------------------------------------------------------- /spring-websocket/src/test/java/com/example/springwebsocket/SpringWebsocketApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.springwebsocket; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringWebsocketApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-consumer-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | zookeeper-consumer-service 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-zookeeper-discovery 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-consumer-service/src/main/java/com/example/zookeeper/ZookeeperConsumerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.zookeeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @EnableDiscoveryClient 8 | @SpringBootApplication 9 | public class ZookeeperConsumerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ZookeeperConsumerApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-consumer-service/src/main/java/com/example/zookeeper/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.zookeeper.config; 2 | 3 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @Configuration 9 | public class WebConfig { 10 | 11 | @Bean 12 | @LoadBalanced 13 | public RestTemplate restTemplate(){ 14 | return new RestTemplate(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-consumer-service/src/main/java/com/example/zookeeper/controller/RestTemplateController.java: -------------------------------------------------------------------------------- 1 | package com.example.zookeeper.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | import org.springframework.web.client.RestTemplate; 7 | 8 | @RestController 9 | public class RestTemplateController { 10 | @Autowired 11 | private RestTemplate restTemplate; 12 | private static final String host = "http://zookeeper-producer-service"; 13 | 14 | @GetMapping("/consumer/rest") 15 | public String test(){ 16 | return restTemplate.getForObject(host+"/producer/hello", String.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-consumer-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8008 3 | 4 | spring: 5 | application: 6 | name: zookeeper-consumer-service 7 | # 配置 ZooKeeper 注册中心 8 | cloud: 9 | zookeeper: 10 | discovery: 11 | enabled: true # 如果不想使用 ZooKeeper 进行服务注册和发现,设置为 false 即可 12 | connect-string: 192.168.10.101:2181,192.168.10.102:2181,192.168.10.103:2181 -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-producer-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-learning 7 | org.example 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 4.0.0 12 | 13 | zookeeper-producer-service 14 | 15 | 16 | 17 | org.springframework.cloud 18 | spring-cloud-starter-zookeeper-discovery 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-actuator 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-producer-service/src/main/java/com/example/zookeeper/ZookeeperProducerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.zookeeper; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | @EnableDiscoveryClient 8 | @SpringBootApplication 9 | public class ZookeeperProducerApplication { 10 | public static void main(String[] args) { 11 | SpringApplication.run(ZookeeperProducerApplication.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-producer-service/src/main/java/com/example/zookeeper/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.example.zookeeper.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class HelloController { 9 | @Value("${server.port}") 10 | private String port; 11 | 12 | @GetMapping("/producer/hello") 13 | public String test(){ 14 | return "Hello World : " + port; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-zookeeper-demo/zookeeper-producer-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8006 3 | 4 | spring: 5 | application: 6 | name: zookeeper-producer-service 7 | # 配置 ZooKeeper 注册中心 8 | cloud: 9 | zookeeper: 10 | discovery: 11 | enabled: true # 如果不想使用 ZooKeeper 进行服务注册和发现,设置为 false 即可 12 | connect-string: 192.168.10.101:2181,192.168.10.102:2181,192.168.10.103:2181 --------------------------------------------------------------------------------