⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 zhanjq.blog.csdn.net/article/details/127574020 「共饮一杯无」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文主要内容是通过SpringCloud 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>oauth2-demo</artifactId>
<groupId>com.zjq</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>ms-gateway</artifactId>

<dependencies>
<!-- spring cloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- commons 公共项目 -->
<dependency>
<groupId>com.zjq</groupId>
<artifactId>commons</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 和 webflux 冲突 -->
<exclusions>
<exclusion>
<groupId>com.battcn</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 自定义的元数据依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

</project>

网关服务的yml配置内容如下:

server:
port: 80

spring:
application:
name: ms-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启配置注册中心进行路由功能
lower-case-service-id: true # 将服务名称转小写
routes:
- id: ms-users
uri: lb://ms-users
predicates:
- Path=/users/**
filters:
- StripPrefix=1

- id: ms-oauth2-server
uri: lb://ms-oauth2-server
predicates:
- Path=/auth/**
filters:
- StripPrefix=1

secure:
ignore:
urls: # 配置白名单路径
- /actuator/**
- /auth/oauth/**
- /users/signin

# 配置 Eureka Server 注册中心
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:7000/eureka/

logging:
pattern:
console: '%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'

请求白名单配置

加载配置文件中的配置,注入到spring容器中。

secure:
ignore:
urls: # 配置白名单路径
- /actuator/**
- /auth/oauth/**
- /users/signin
/**
* 网关白名单配置
* @author zjq
*/
@Data
@Component
@ConfigurationProperties(prefix = "secure.ignore")
public class IgnoreUrlsConfig {

private List<String> urls;

}

异常处理和rest请求配置

异常处理在全局过滤器中会有用到,代码如下:

@Component
public class HandleException {

@Resource
private ObjectMapper objectMapper;

public Mono<Void> writeError(ServerWebExchange exchange, String error) {
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResultInfo resultInfo = ResultInfoUtil.buildError(ApiConstant.NO_LOGIN_CODE, ApiConstant.NO_LOGIN_MESSAGE, request.getURI().getPath());
String resultInfoJson = null;
DataBuffer buffer = null;
try {
resultInfoJson = objectMapper.writeValueAsString(resultInfo);
buffer = response.bufferFactory().wrap(resultInfoJson.getBytes(Charset.forName("UTF-8")));
} catch (JsonProcessingException ex) {
ex.printStackTrace();
}

return response.writeWith(Mono.just(buffer));
}

}

申请授权和认证过程中需要远程调用其他接口,所以我们引入rest请求配置,代码如下:

/**
* REST请求配置
* @author zjq
*/
@Configuration
public class RestTemplateConfiguration {

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

}

全局过滤器配置

配置好了白名单,我们需要在网关过滤器中使用该白名单配置,放行对应的白名单,网关过滤器需要实现全局过滤器接口org.springframework.cloud.gateway.filter.GlobalFilter和过滤器顺序接口org.springframework.core.Ordered相关代码如下:

/**
* 网关全局过滤器
* @author zjq
*/
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

@Resource
private IgnoreUrlsConfig ignoreUrlsConfig;
@Resource
private RestTemplate restTemplate;
@Resource
private HandleException handleException;

/**
* 身份校验处理
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 判断当前的请求是否在白名单中
AntPathMatcher pathMatcher = new AntPathMatcher();
boolean flag = false;
String path = exchange.getRequest().getURI().getPath();
for (String url : ignoreUrlsConfig.getUrls()) {
if (pathMatcher.match(url, path)) {
flag = true;
break;
}
}
// 白名单放行
if (flag) {
return chain.filter(exchange);
}
// 获取 access_token
String access_token = exchange.getRequest().getQueryParams().getFirst("access_token");
// 判断 access_token 是否为空
if (StringUtils.isBlank(access_token)) {
return handleException.writeError(exchange, "请登录");
}
// 校验 token 是否有效
String checkTokenUrl = "http://ms-oauth2-server/oauth/check_token?token=".concat(access_token);
try {
// 发送远程请求,验证 token
ResponseEntity<String> entity = restTemplate.getForEntity(checkTokenUrl, String.class);
// token 无效的业务逻辑处理
if (entity.getStatusCode() != HttpStatus.OK) {
return handleException.writeError(exchange,
"Token was not recognised, token: ".concat(access_token));
}
if (StringUtils.isBlank(entity.getBody())) {
return handleException.writeError(exchange,
"This token is invalid: ".concat(access_token));
}
} catch (Exception e) {
return handleException.writeError(exchange,
"Token was not recognised, token: ".concat(access_token));
}
// 放行
return chain.filter(exchange);
}

/**
* 网关过滤器的排序,数字越小优先级越高
*
* @return
*/
@Override
public int getOrder() {
return 0;
}

}

测试验证

登录:

获取当前登录用户信息:

退出登录:

文章目录
  1. 1. 配置文件
  2. 2. 请求白名单配置
  3. 3. 异常处理和rest请求配置
  4. 4. 全局过滤器配置
  5. 5. 测试验证