系列文章目录
构建SpringCloud alibaba项目(一、构建父工程、公共库、网关)
构建SpringCloud alibaba项目(二、构建微服务鉴权子工程store-authority-service)
文章目录
- 系列文章目录
- 1、概要
- 2、整体架构流程
- 2.1、技术结构组成部分
- 3、技术名词解释
- 4、技术细节
- 4.1、构建父工程
- 4.1.1、选择构建Maven项目
- 4.1.2、修改父工程文件
- 4.1.3、修改父工程pom.xml配置
- 4.1.3.1、添加springboot支持
- 4.1.3.2、修改JDK版本、编码、springboot版本配置
- 4.1.3.3、添加Spring Cloud、Spring Cloud alibaba版本规范配置
- 4.1.3.4、增加lombok依赖配置
- 4.1.3.5、增加build配置,整个工程使用maven打包
- 4.2、构建公共子工程
- 4.2.1、添加子工程store-common
- 4.2.2、修改文件
- 4.2.3、添加统一返回类
- 4.2.4、添加token辅助类
- 4.3、构建网关子工程
- 4.3.1、修改pom.xml配置
- 4.3.2、修改application.yml配置
- 4.3.3、网关解决跨域问题
- 4.3.4、网关增加鉴权功能AuthService
- 4.3.5、增加网关登录验证拦截GatewayWebFilter
- 4.3、构建微服务鉴权子工程store-authority-service
- 4.4、构建微服务提供服务方子工程store-user-service
- 4.5、构建微服务调用方子工程store-api
- 小结
1、概要
本章节讲解如何构建SpringCloud alibaba项目,以父子工程形式搭建。
- 父工程规范Springboot版本、SpringCloud版本、SpringCloud alibaba版本;
- 子工程包括公共方法库(公共的DTO、http统一返回类(枚举code码等)),Gateway网关(集成Sentinel)、内网微服务统一接口层、内网微服务发布方、内网微服务调用方等;
2、整体架构流程
2.1、技术结构组成部分
此次框架选型是对Spring Cloud Netflix 框架组件的升级与替换。
Spring Cloud alibaba组件包括:Nacos (discovery、config)、Sentinel
Spring Cloud 组件包括:OpenFeign+ LoadBalancer 、Sleuth、Gateway
其他组件:RabbitMQ 、redis、mybatis-plus、knife4j、JWT、ShardingSphere-jdbc
3、技术名词解释
例如:
- Nacos discovery: 英文全称为 Dynamic Naming and Configuration Service,是一个由阿里巴巴团队使用 Java 语言开发的开源项目,有注册中心、服务注册、发现功能。
- Nacos config: 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。
- Sentinel:是由阿里巴巴中间件团队开发的开源项目,是一种面向分布式微服务架构的轻量级高可用流量控制组件
- Sleuth:可以将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。
- OpenFeign: 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件。我们可以像调用本地方法一样来调用远程服务,而完全感觉不到这是在进行远程调用,结合LoadBalancer 一起使用。
- RabbitMQ:RabbitMQ整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。常用于系统解耦、削峰使用,常见于微服务、业务活动等场景。
- mybatis-plus:数据库操作工具;
- knife4j:基于swagger升级的文档管理工具。
- JWT:JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案,基于JSON的开发标准,用户信息加密到token里,服务器不保存任何用户信息。
- Apache ShardingSphere : 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强
4、技术细节
4.1、构建父工程
4.1.1、选择构建Maven项目
4.1.2、修改父工程文件
- 删除src目录及底下的所有文件
- 保留pom.xml
4.1.3、修改父工程pom.xml配置
4.1.3.1、添加springboot支持
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kelvin</groupId>
<artifactId>onlinestore</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
4.1.3.2、修改JDK版本、编码、springboot版本配置
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.10</spring-boot.version>
</properties>
4.1.3.3、添加Spring Cloud、Spring Cloud alibaba版本规范配置
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.4.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
4.1.3.4、增加lombok依赖配置
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
4.1.3.5、增加build配置,整个工程使用maven打包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
4.2、构建公共子工程
4.2.1、添加子工程store-common
4.2.2、修改文件
- 删除test文件夹
- pom.xml去掉junit
4.2.3、添加统一返回类
package com.kelvin.common.http;
/***
* @title HttpStatusInfoInterface
* @desctption HTTP状态信息接口
* @author Administrator
* @create 2023/5/18 10:28
**/
public interface HttpStatusInfoInterface {
int getCode();
String getMessage();
}
package com.kelvin.common.http;
/***
* @title HttpStatusEnum
* @desctption Http状态码
* @author Administrator
* @create 2023/5/18 10:30
**/
public enum HttpStatusEnum implements HttpStatusInfoInterface{
//定义状态枚举值
SUCCESS(200 , "成功!"),
NOROLE(300 , "权限不足!"),
USER_TOKEN_NOT_EXISTS(301 , "无效的token信息!"),
PRODUCT_STOCK_NOT_ENOUGH(302 , "商品的库存不足!"),
BODY_NOT_MATCH(400 , "数据格式不匹配!"),
NOT_FOUND(404 , "访问资源不存在!"),
FLOW_LIMIT(490 , "接口流量已超出,限制访问!"),
INTERNAM_SERVER_ERROR(500 , "服务器内部错误!"),
SERVER_BUSY(503 , "服务器正忙,请稍后再试!"),
REQUEST_METHOD_SUPPORT_ERROR(10001 , "当前请求方法不支持!"),
REQUEST_DATA_NULL(10002 , "当前请求参数为空!"),
USER_NOT_EXISTS(10003 , "该用户不存在!"),
USER_INVALID(10004 , "当前登录信息已失效,请重新登录!"),
PASSWORD_ERROR(10005 , "密码错误!"),
USER_NAME_LOCK(10006 , "该账号已被锁定!")
;
//状态码
private int code;
//提示信息
private String message;
HttpStatusEnum(int code, String message) {
this.code = code;
this.message = message;
}
@Override
public int getCode() {
return this.code;
}
@Override
public String getMessage() {
return this.message;
}
}
package com.kelvin.common.http;
import lombok.Data;
/***
* @title ResultDTO
* @desctption 控制器的统一返回类
* @author Administrator
* @create 2023/5/16 9:48
**/
@Data
public class ResultDTO<T> {
private Integer code ;
private String message;
private T data;
public ResultDTO() {
}
public ResultDTO(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
}
package com.kelvin.common.http;
/***
* @title HttpResultGenerator
* @desctption <TODO description class purpose>
* @author Administrator
* @create 2023/5/18 10:43
**/
public class HttpResultGenerator {
//正常返回时调用方法
public static ResultDTO success(Object data) {
return new ResultDTO(HttpStatusEnum.SUCCESS.getCode() , "接口调用成功!" , data);
}
//失败时调用方法(入参是异常枚举)
public static ResultDTO fail(HttpStatusEnum httpStatusEnum) {
return new ResultDTO(httpStatusEnum.getCode() , httpStatusEnum.getMessage() , null);
}
//失败时调用方法(提供给GlobalExceptionHandler类使用)
public static ResultDTO fail(int code , String message) {
return new ResultDTO(code , message , null);
}
}
4.2.4、添加token辅助类
package com.kelvin.common.dto;
import lombok.Data;
/***
* @title TokenDTO
* @desctption <TODO description class purpose>
* @author Administrator
* @create 2023/6/8 10:01
**/
@Data
public class TokenDTO {
private String token;
}
4.3、构建网关子工程
4.3.1、修改pom.xml配置
修改父工程指向
<parent>
<groupId>com.kelvin</groupId>
<artifactId>onlinestore</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.kelvin</groupId>
<artifactId>store-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</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>
4.3.2、修改application.yml配置
server:
port: 80
spring:
main:
web-application-type: reactive
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos server 的地址
gateway: #网关路由配置
routes:
#将 drp-user-service 提供的服务隐藏起来,不暴露给客户端,只给客户端暴露 API 网关的地址 80
- id: user-api_routh #路由 id,没有固定规则,但唯一,建议与服务名对应
uri: lb://user-api #匹配后提供服务的路由地址
predicates:
#以下是断言条件,必选全部符合条件
- Path=/user/** #断言,路径匹配 注意:Path 中 P 为大写
- Method=GET,POST #只能时 GET 请求时,才能访问
metadata:
connect-timeout: 10
#单位毫秒
response-timeout: 10000
- id: authority-service_routh #路由 id,没有固定规则,但唯一,建议与服务名对应
uri: lb://auth-service #匹配后提供服务的路由地址
predicates:
#以下是断言条件,必选全部符合条件
- Path=/auth/** #断言,路径匹配 注意:Path 中 P 为大写
- Method=GET,POST #只能时 GET 请求时,才能访问
metadata:
connect-timeout: 10
#单位毫秒
response-timeout: 10000
sentinel:
transport:
#配置 Sentinel dashboard 地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#开启除了controller层其他链路调用
web-context-unify: false
4.3.3、网关解决跨域问题
package com.kelvin.storegateway.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.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
@Configuration
public class WebConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowedHeaders(Arrays.asList("content-type","token"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public CorsWebFilter corsWebFilter(CorsConfigurationSource corsConfigurationSource) {
return new CorsWebFilter(corsConfigurationSource);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
}
4.3.4、网关增加鉴权功能AuthService
package com.kelvin.storegateway.service;
import com.kelvin.common.dto.TokenDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
/***
* @title AuthService
* @desctption <TODO description class purpose>
* @author Administrator
* @create 2023/6/8 10:04
**/
@Component
@FeignClient(value = "auth-service")
public interface AuthService {
@PostMapping("auth/isTokenExpiration")
public Boolean validateToken(TokenDTO token);
}
4.3.5、增加网关登录验证拦截GatewayWebFilter
package com.kelvin.storegateway.filter;
import com.kelvin.common.dto.TokenDTO;
import com.kelvin.storegateway.service.AuthService;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.stream.Collectors;
/***
* @title DrfGlobalFilter
* @desctption 登录验证
* @author Administrator
* @create 2023/5/15 14:17
**/
@Component
public class GatewayWebFilter implements GlobalFilter {
@Resource
private AuthService authService;
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//如果登录请求,不用验证token
String path = request.getURI().getPath();
if(!path.contains("login")) {
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst("token");
if(StringUtils.isEmpty(token)) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
} else {
TokenDTO tokenDTO = new TokenDTO();
tokenDTO.setToken(token);
//token验证不通过,返回给前端401
Boolean aBoolean = authService.validateToken(tokenDTO);
if(aBoolean){
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
}
return chain.filter(exchange);
}
}
4.3、构建微服务鉴权子工程store-authority-service
4.4、构建微服务提供服务方子工程store-user-service
4.5、构建微服务调用方子工程store-api
小结
通过上述操作可以轻松搭建出微服务架构,扩展十分容易。
父子工程构建已可用,持续更新中…