新增商城端网关模块
创建商城端网关 newbee-mall-cloud-gateway-mall
在设计网关层时,笔者觉得创建一个网关服务模块即可,但是考虑到后期可能会做不同的优化和不同的逻辑处理,毕竟新蜂商城包含后台管理系统和商城端,而这两个系统分别对应不同的页面流程和不同的用户体系,所以创建了两个单独的网关服务模块。在之前的编码中已经创建了 newbee-mall-cloud-gateway-admin
,在该代码目录中新增路由配置和鉴权过滤器用于后台管理系统相关接口的转发和鉴权。现在,开始改造商城端的接口,需要再创建一个网关服务 newbee-mall-cloud-gateway-mall
。
本节的源代码是在 newbee-mall-cloud-dev-step10
工程的基础上改造的,将工程命名为 newbee-mall-cloud-dev-step11
。
创建过程并不复杂,由于代码量不大,因此直接在工程中复制 newbee-mall-cloud-gateway-admin
即可,并将复制的项目改名为 newbee-mall-cloud-gateway-mall
。
打开 newbee-mall-cloud-gateway-mall
工程中的 pom.xml
文件,修改依赖配置信息,主要修改 artifactId
和 name
两个属性,修改后的代码如下:
<modelVersion>4.0.0</modelVersion>
<groupId>ltd.newbee.cloud</groupId>
<artifactId>newbee-mall-cloud-gateway-mall</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>newbee-mall-cloud-gateway-mall</name>
<description>商城前端网关模块</description>
在工程的 pom.xml
主文件中增加该模块的配置,将该模块纳入实战项目,代码如下:
<modules>
<module>newbee-mall-cloud-recommend-service</module>
<module>newbee-mall-cloud-goods-service</module>
<module>newbee-mall-cloud-user-service</module>
<module>newbee-mall-cloud-gateway-mall</module>
<module>newbee-mall-cloud-gateway-admin</module>
<module>newbee-mall-cloud-common</module>
</modules>
将项目主类 NewBeeMallCloudAdminGatewayApplication
修改为 NewBeeMallCloudMallGatewayApplication
,之后打开配置文件 application.properties
,修改端口号和微服务名称,删除后台管理系统相关接口的路由配置,增加商城端用户微服务的路由配置,最终的配置文件代码如下:
server.port=29110
# 微服务名称
spring.application.name=newbee-mall-cloud-gateway-mall
# Nacos 地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# Nacos 登录用户名(默认为 nacos,在生产环境中一定要修改)
spring.cloud.nacos.username=nacos
# Nacos 登录密码(默认为 nacos,在生产环境中一定要修改)
spring.cloud.nacos.password=nacos
# 网关开启微服务注册与被服务发现
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
# 用户微服务的路由配置
spring.cloud.gateway.routes[0].id=user-service-route
spring.cloud.gateway.routes[0].uri=lb://newbee-mall-cloud-user-service
spring.cloud.gateway.routes[0].order=1
spring.cloud.gateway.routes[0].predicates[0]=Path=/users/mall/**
## Redis 配置
# Redis 数据库索引(默认为 0)
spring.redis.database=13
# Redis 服务器地址
spring.redis.host=127.0.0.1
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 服务器连接密码
spring.redis.password=123456789
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
与后台管理系统的网关模块一样,商城端网关也需要进行基础的用户鉴权操作。在网关层进行统一鉴权,这样就能够避免无正确身份标识的请求直接进入微服务实例中。如果请求头中有正确的身份标识,则放行,让后方的微服务实例进行请求处理;如果没有正确的身份标识,则直接在网关层响应一个错误提示。具体的编码实现在前文中已经介绍过,使用 Spring Cloud Gateway
的全局过滤器。
由于使用复制过来的代码,因此直接将 ValidTokenGlobalFilter
类改名为 ValidMallUserTokenGlobalFilter
类,在这个类中编写对商城用户的网关层鉴权逻辑,代码如下:
package ltd.gateway.cloud.newbee.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ltd.common.cloud.newbee.dto.Result;
import ltd.common.cloud.newbee.dto.ResultGenerator;
import ltd.common.cloud.newbee.pojo.AdminUserToken;
import ltd.common.cloud.newbee.pojo.MallUserToken;
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.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
@Component
public class ValidMallUserTokenGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private RedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 登录注册接口,直接放行
if ((exchange.getRequest().getURI().getPath().equals("/users/mall/login") || exchange.getRequest().getURI().getPath().equals("/users/mall/register"))) {
return chain.filter(exchange);
}
HttpHeaders headers = exchange.getRequest().getHeaders();
if (headers == null || headers.isEmpty()) {
// 返回错误提示
return wrapErrorResponse(exchange, chain);
}
String token = headers.getFirst("token");
if (!StringUtils.hasText(token)) {
// 返回错误提示
return wrapErrorResponse(exchange, chain);
}
ValueOperations<String, MallUserToken> opsForMallUserToken = redisTemplate.opsForValue();
MallUserToken tokenObject = opsForMallUserToken.get(token);
if (tokenObject == null) {
// 返回错误提示
return wrapErrorResponse(exchange, chain);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
Mono<Void> wrapErrorResponse(ServerWebExchange exchange, GatewayFilterChain chain) {
Result result = ResultGenerator.genErrorResult(416, "无权限访问");
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.valueToTree(result);
byte[] bytes = resultNode.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer dataBuffer = exchange.getResponse().bufferFactory().
wrap(bytes);
exchange.getResponse().setStatusCode(HttpStatus.OK);
return exchange.getResponse().writeWith(Flux.just(dataBuffer));
}
}
主要的代码逻辑就在 filter()
方法中,处理步骤如下。
-
判断请求路径,如果是登录接口或注册接口,则直接放行;如果不是登录接口或注册接口,则进行后续处理。
-
获取请求头对象,如果为空,则直接返回错误提示,不会将请求转发到后方的微服务实例中。
-
如果请求头不为空,则取出其中的
token
值。如果该值不存在,则直接返回错误提示,不会将请求转发到后方的微服务实例中。 -
根据
token
值查询Redis
数据库中是否存在对应的数据。如果Redis
数据库中不存在对应的数据,则直接返回错误提示,不会将请求转发到后方的微服务实例中。 -
如果
Redis
数据库中存在对应的数据,则表示对商城用户的鉴权成功,将请求转发到后方的微服务实例中去处理请求。
至此,商城端网关层鉴权功能所需的基本配置与功能代码已经完备,接下来进行功能测试。
商城端网关功能测试
编码完成后,准备好数据库和表就可以启动项目了。当然,在项目启动前需要分别启动 Nacos Server
和 Redis Server
,之后依次启动 newbee-mall-cloud-goods-web
工程和 newbee-mall-cloud-gateway-mall
工程下的主类。启动成功后,就可以测试通过网关访问商城端接口的功能与网关层鉴权功能了。
笔者使用 Postman
工具进行接口请求和功能测试,由于通过网关层访问,因此这里的请求地址需要修改,用户微服务的端口号为 29000
,商城端网关的端口号为 29110
。
在 Postman
工具的地址栏中输入如下网址: http://localhost:29110/users/mall/detail。
选择请求方法为 GET
,测试结果如图 6-10 所示。

这里通过直接访问网关层地址来获取商城用户的信息。因为没有在请求头中添加 token
值,所以网关层的 token
过滤器直接拦截了这个请求,并返回错误提示。这个请求根本没有进入用户微服务实例,商城端网关层鉴权生效了。
上述测试过程演示的是鉴权失败的情况,接下来通过请求登录接口获取正确的 token
值,演示一下鉴权成功的情况。
在地址栏中输入如下网址: http://localhost:29110/users/mall/detail。
设置请求方法为 GET
,并且添加一个请求头参数,Key
为 “token”,Value
为之前登录成功后获取的 token
值,相关请求配置及测试结果如图 6-11 所示。
最终成功获取了正确的数据,商城端网关的路由配置正常且网关层鉴权正常,功能测试完成。当然,读者在测试时也可以选择 debug
模式启动两个项目,并打上几个断点,发起请求后看一下代码的执行步骤,会理解得更透彻一些。这里主要引入了商城用户相关功能模块,以及对商城用户登录后的 token
值处理和商城端网关鉴权。后续章节将继续完善该实战项目,主要涉及商城端的功能模块,因此需要实现与商城用户相关的代码。功能及编码都不复杂,很多知识点都在管理员用户功能模块改造时介绍过了,如果有问题,可以参考管理员用户的功能改造来理解。希望读者能够根据笔者提供的开发步骤顺利地完成本节的项目改造。

接下来继续讲解微服务架构下商城端接口的编码改造,主要包括商城首页数据的接口、商城分类页面的接口、商品列表和商品详情页面的接口。它们的源代码是在 newbee-mall-cloud-dev-step11
工程的基础上改造的,笔者根据开发步骤整理了3份源代码文件,分别是 newbee-mall-cloud-dev-step12
、newbee-mall-cloud-dev-step13
和 newbee-mall-cloud-dev-step13-2
。