网关介绍:
通俗点讲,如果我们有三个项目a(端口为8801),b(8802),c(8803)都要给外网访问那么我们势必要暴露这三个接口,项目一多暴露的端口就会多,个人认为会有一部分的安全问题。
还有,如果我们已经有了一个域名(备案完全),外网访问这三个项目,那么就需要通过域名解析出三个可用的子域名,我们需要登录到阿里云或者腾讯云的域名控制台去解析,解析完成后,如果我们使用的是宝塔面板,我们或许还需要给这三个域名去申请证书,免费的证书有效期是3个月左右,域名(项目)很多的情况下,一般不会自动续签证书,需要我们手动去续签,我们每天都要去面板上看哪个临近过期然后续签,这样很麻烦。
我们如果有了网关,这样放开端口就只需要放开这个网关项目的端口,有域名的去情况下,我们只需要解析一个子域名即可
话不多说,直接上代码:
父级项目(domain):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mj</groupId>
<artifactId>domain</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<modules>
<module>../center</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<!--夫项目 管理依赖版本-->
<dependencyManagement>
<dependencies>
<!--cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>
<!--这里配置添加自己项目启动类的全限定名-->
<!-- com.mj.CenterStarter-->
</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
主项目(geteway):
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mj</groupId>
<artifactId>domain</artifactId>
<version>1.0</version>
<relativePath>../domain/pom.xml</relativePath>
</parent>
<groupId>com.mj</groupId>
<artifactId>center</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!-- <version>2.2.2.RELEASE</version>-->
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>com.alibaba.nacos</groupId>-->
<!-- <artifactId>nacos-client</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.nacos</groupId>-->
<!-- <artifactId>nacos-client</artifactId>-->
<!-- <version>1.4.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.24</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--使用热部署出现中文乱码解决方案-->
<configuration>
<fork>true</fork>
<!--增加jvm参数-->
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<!--指定入口类-->
<mainClass>com.mj.CenterStater</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yml文件:
server:
port: 1225
spring:
application:
name: center-juyou
cloud:
gateway:
routes:
- id: mysql # 路由标示,必须唯一
uri: http://127.0.0.1:8281 # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/mysql/** # 路径断言,判断路径是否以/mysql开头,如果是则符合,跳转路由
filters:
- StripPrefix=1 # 去掉一层路径
# 如http://127.0.0.1:1225/msyql/controller 就会跳转至 http://127.0.0.1:8281/controller
- id: main
uri: http://127.0.0.1:7895
predicates:
- Path=/main/**
filters:
- StripPrefix=1
config文件夹中的两个配置文件:
package com.mj.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfigurations {
@Bean
public CorsResponseHeaderFilter corsResponseHeaderFilter() {
return new CorsResponseHeaderFilter();
}
@Bean
public CorsWebFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", buildCorsConfiguration());
CorsWebFilter corsWebFilter = new CorsWebFilter(source, new DefaultCorsProcessor() {
@Override
protected boolean handleInternal(ServerWebExchange exchange, CorsConfiguration config,
boolean preFlightRequest)
{
// 预留扩展点
// if (exchange.getRequest().getMethod() == HttpMethod.OPTIONS) {
return super.handleInternal(exchange, config, preFlightRequest);
// }
// return true;
}
});
return corsWebFilter;
}
private CorsConfiguration buildCorsConfiguration() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
// corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS);
// corsConfiguration.addAllowedMethod(HttpMethod.POST);
// corsConfiguration.addAllowedMethod(HttpMethod.GET);
// corsConfiguration.addAllowedMethod(HttpMethod.PUT);
// corsConfiguration.addAllowedMethod(HttpMethod.DELETE);
// corsConfiguration.addAllowedMethod(HttpMethod.PATCH);
//允许所有的请求方式
corsConfiguration.addAllowedMethod("*");
// corsConfiguration.addAllowedHeader("origin");
// corsConfiguration.addAllowedHeader("content-type");
// corsConfiguration.addAllowedHeader("accept");
// corsConfiguration.addAllowedHeader("x-requested-with");
// corsConfiguration.addAllowedHeader("Referer");
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.USER_AGENT);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.TOKEN);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.REFRESH_TOKEN);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.OS);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_APP_KEY);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_DEVICE_ID);
// corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_TOKEN);
// 允许所有的请求头
corsConfiguration.addAllowedHeader("*");
corsConfiguration.setMaxAge(7200L);
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
}
package com.mj.config;
import java.util.ArrayList;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
// 指定此过滤器位于NettyWriteResponseFilter之后
// 即待处理完响应体后接着处理响应头
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
@Override
@SuppressWarnings("serial")
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.defer(() -> {
exchange.getResponse().getHeaders().entrySet().stream()
.filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
.filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)))
.forEach(kv ->
{
kv.setValue(new ArrayList<String>() {{add(kv.getValue().get(0));}});
});
return chain.filter(exchange);
}));
}
}
filter文件夹中的一个文件:
package com.mj.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
@Component
@Order(0) //数字越小 优先级越高
@CrossOrigin()
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange ex, GatewayFilterChain chain) {
ServerHttpRequest request = ex.getRequest();
String path = request.getURI().getPath();
InetSocketAddress remoteAddress =
request.getRemoteAddress();
InetAddress address = remoteAddress.getAddress();
String hostAddress = address.getHostAddress();
// 获取当前时间
LocalDateTime currentDateTime = LocalDateTime.now();
// 定义日期时间格式s
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化当前时间
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("log:["+formattedDateTime+"]\t访问路径:"+path+"\t客户端ip+"+hostAddress);
//以某某某结尾
// if (path.endsWith("login.html") ||
// path.endsWith("register.html")||
// path.endsWith("js")||
// path.endsWith("css")||
// path.endsWith("login")||
// path.endsWith("register")||
// path.endsWith(".map")){
// return chain.filter(ex);
// }
if (true){
return chain.filter(ex);
}
ex.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return ex.getResponse().setComplete();
}
}
启动类文件:
package com.mj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.CrossOrigin;
@SpringBootApplication
@EnableDiscoveryClient //开启服务注册与发现
@CrossOrigin //允许跨域访问
public class CenterStater {
public static void main(String[] args) {
SpringApplication.run(CenterStater.class);
}
}