故事背景
摸鱼监督者要做个新微服务架构要实现 1、简单的鉴权及IP限定 2、全局及自定义过滤器扩展
网上看了些文章作为参考,如果侵权请告知及时删除 SpringCloud-路由网关Gateway自定义GatewayFilterFactory 只是本着学习及分享并无恶意^ _ ^
项目架构及版本
JDK 17 SpringCloud 2021.0.0 SpringBoot 2.6.3 NACOS Server 2.0.3 (主要实现配置+发现服务)
简单服务
只是一个可以正常访问的controller 如果有特别的配置就扔到NACOS上
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath />
</parent>
<groupId>com.ap.demo</groupId>
<artifactId>qwer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>qwer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
App.java
@SpringBootApplication
@EnableDiscoveryClient
public class QwerApplication {
public static void main(String[] args) {
SpringApplication.run(QwerApplication.class, args);
}
}
Controller
@RestController
@RequestMapping("/hub")
@Slf4j
public class APController {
@GetMapping("/say")
public String sayHello(HttpServletResponse rsp) {
log.info("say service was call.");
rsp.addHeader("ap", "andy");
return "Hello";
}
@GetMapping("/hi")
public Object test(HttpServletResponse rsp) {
rsp.addHeader("ap", "yanLao");
return "hi";
}
}
Bootstrap.yml
spring:
application:
name: qwer
cloud:
inetutils:
preferred-networks:
- 192.
nacos:
config:
server-addr: 192.168.20.111:8848
namespace: develop
group: develop
file-extension: yaml
name: qwer
discovery:
server-addr: 192.168.20.111:8848
namespace: develop
group: develop
server:
port: 8090
GateWay 网关
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath />
</parent>
<groupId>com.ap.demo</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
</dependencies>
<build>
<finalName>gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
bootstrap.yml
spring:
application:
name: gateway
cloud:
inetutils:
preferred-networks:
- 192.
nacos:
config:
server-addr: 192.168.20.111:8848
namespace: develop
group: develop
name: gateway
file-extension: yml
extension-configs:
- data-id: ap-gateway-user.yml
group: DEFAULT_GROUP
refresh: true
discovery:
server-addr: 192.168.20.111:8848
namespace: develop
group: develop
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: qwer
uri: lb://qwer
predicates:
- Path=/hub/**
filters:
- APCustom
ap-gateway-user.yml 用户密码
ap:
gateway:
api:
users:
- test:test123456
gatewayapplication
@SpringBootApplication
@EnableDiscoveryClient
@EnableEncryptableProperties
@ConfigurationPropertiesScan("com.ap.gateway.config")
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
apiUserDetail
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Data
@Service
@Slf4j
@ConfigurationProperties(prefix="ap.gateway.api")
public class ApiUserDetailsService implements ReactiveUserDetailsService {
private static final String SPLITER = ":";
private List<String> users;
private Map<String, UserDetails> userMap;
@PostConstruct
public void init() {
this.userMap = new HashMap<>();
users.forEach(user -> {
String[] userPasswd = user.split(SPLITER);
if (userPasswd.length != 2) {
return;
}
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
UserDetails userDetails = User.builder()
.passwordEncoder(encoder::encode)
.username(userPasswd[0])
.password(userPasswd[1])
.roles("USER")
.build();
this.userMap.put(userDetails.getUsername(), userDetails);
});
}
@Override
public Mono<UserDetails> findByUsername(String username) {
UserDetails result = this.userMap.get(username);
if (result != null) {
log.debug("User found for {}", username);
return Mono.just(User.withUserDetails(result).build());
} else {
log.warn("User not found for {}", username);
return Mono.empty();
}
}
}
GatewaySecurityConfiguration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@Configuration
@EnableWebFluxSecurity
public class GatewaySecurityConfiguration {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf()
.disable()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.httpBasic();
return http.build();
}
}
全局过滤器
无需配置 直接生效
@Component
@Slf4j
public class CustomGlobalGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
HttpHeaders req = exchange.getRequest().getHeaders();
log.info("ap: ", req.get("ap");
HttpHeaders resp = exchange.getResponse().getHeaders();
log.info("ap: ", resp.get("ap");
}));
}
@Override
public int getOrder() {
return 0;
}
}
自定义过滤器
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class APCustomGatewayFilterFactory extends AbstractGatewayFilterFactory<APCustomGatewayFilterFactory .Config> {
public static class Config{
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("data");
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
String data = config.getData();
}));
}
};
}
}
由于时间关系,只能将代码放上来先,还有些理论及一些坑还没整理,希望可以帮助更多热爱学习分享的朋友。也有些因为敏感字眼进行了修改,所以有问题请随时留言,有时间会来修改整理的,谢谢各位 @_@
|