Series Article Directory
Build the SpringCloud alibaba project (1. Build the parent project, public library, and gateway)
Build the SpringCloud alibaba project (2. Build the microservice authentication sub-project store-authority-service)
Article Directory
- Series Article Directory
- 1, overview
- 2. Overall architecture process
- 3. Explanation of technical terms
- 4. Technical details
-
- 4.1. Build parent project
-
- 4.1.1. Choose to build the Maven project
- 4.1.2. Modify the parent project file
- 4.1.3. Modify the parent project pom.xml configuration
-
- 4.1.3.1, add springboot support
- 4.1.3.2. Modify the JDK version, encoding, and springboot version configuration
- 4.1.3.3, add Spring Cloud, Spring Cloud alibaba version specification configuration
- 4.1.3.4, increase lombok dependency configuration
- 4.1.3.5, add build configuration, the whole project is packaged with maven
- 4.2. Build public subprojects
- 4.3. Build the gateway subproject
- 4.3. Build microservice authentication subproject store-authority-service
- 4.4. Build a microservice provider sub-project store-user-service
- 4.5. Build the microservice caller subproject store-api
- summary
1, overview
This chapter explains how to build the SpringCloud Alibaba project in the form of a parent-child project.
- Parent project specification Springboot version, SpringCloud version, SpringCloud alibaba version;
- Sub-projects include public method library (public DTO, http unified return class (enumeration code, etc.)), Gateway gateway (integrated with Sentinel), unified interface layer of intranet microservices, intranet microservice publishers, intranet microservice callers, etc.;
2. Overall architecture process
2.1. Technical Structure Components
This framework selection is an upgrade and replacement of Spring Cloud Netflix framework components.
Spring Cloud alibaba components include: Nacos (discovery, config), Sentinel
Spring Cloud components include: OpenFeign+ LoadBalancer, Sleuth, Gateway
Other components: RabbitMQ, redis, mybatis-plus, knife4j, JWT, ShardingSphere-jdbc
3. Explanation of technical terms
For example:
- Nacos discovery: The full English name is Dynamic Naming and Configuration Service. It is an open source project developed by the Alibaba team using the Java language. It has registration center, service registration, and discovery functions.
- Nacos config: Provides key/value storage for storing configuration and other metadata, and provides server-side and client-side support for externalized configuration in distributed systems.
- Sentinel: An open source project developed by the Alibaba middleware team, it is a lightweight and highly available traffic control component for distributed microservice architecture
- Sleuth: It can restore a distributed request to a call link, perform logging, performance monitoring, and display the calling status of a distributed request in a centralized manner.
- OpenFeign: The full name is Spring Cloud OpenFeign, which is a declarative service call and load balancing component officially launched by Spring. We can call remote services like calling local methods, and we don't feel that this is a remote call at all, combined with LoadBalancer.
- RabbitMQ: RabbitMQ is a producer and consumer model as a whole, mainly responsible for receiving, storing and forwarding messages. It is often used for system decoupling and peak shaving, and is common in scenarios such as microservices and business activities.
- mybatis-plus: database operation tool;
- knife4j: A document management tool based on swagger upgrades.
- JWT: JSON Web Token (JWT) is currently the most popular cross-domain authentication solution. Based on the JSON development standard, user information is encrypted into the token, and the server does not save any user information.
- Apache ShardingSphere: It is a distributed database ecosystem, which can convert any database into a distributed database, and enhance the original database through data fragmentation, elastic scaling, encryption and other capabilities
4. Technical details
4.1. Build parent project
4.1.1. Choose to build the Maven project
4.1.2. Modify the parent project file
- Delete the src directory and all files under it
- keep pom.xml
4.1.3. Modify the parent project pom.xml configuration
4.1.3.1, add springboot support
<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. Modify the JDK version, encoding, and springboot version configuration
<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, add Spring Cloud, Spring Cloud alibaba version specification configuration
<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, increase lombok dependency configuration
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
4.1.3.5, add build configuration, the whole project is packaged with 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. Build public subprojects
4.2.1. Add subproject store-common
4.2.2. Modify files
- delete test folder
- pom.xml remove junit
4.2.3. Add a unified return class
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. Add token auxiliary class
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. Build the gateway subproject
4.3.1. Modify pom.xml configuration
Modify the parent project to point to
<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. Modify application.yml configuration
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. Gateway solves cross-domain problems
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. The gateway adds authentication function 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. Add gateway login authentication to intercept 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. Build microservice authentication subproject store-authority-service
4.4. Build a microservice provider sub-project store-user-service
4.5. Build the microservice caller subproject store-api
summary
Through the above operations, a microservice architecture can be easily built, and expansion is very easy.
The parent-child project build is available, and it is being updated continuously...