初始化提交
This commit is contained in:
31
tulingmall-gateway/.gitignore
vendored
Normal file
31
tulingmall-gateway/.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
148
tulingmall-gateway/pom.xml
Normal file
148
tulingmall-gateway/pom.xml
Normal file
@@ -0,0 +1,148 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>tulingmall-gateway</artifactId>
|
||||
<name>tulingmall-gateway</name>
|
||||
<description>商城网关</description>
|
||||
|
||||
<parent>
|
||||
<groupId>com.tuling</groupId>
|
||||
<artifactId>tuling-mall</artifactId>
|
||||
<version>0.0.5</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.tuling</groupId>
|
||||
<artifactId>tulingmall-common</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--加入nacos的依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--添加jwt相关的包-->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.10.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.10.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.10.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--添加Sentinel的依赖-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.12.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.3.2.RELEASE</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- <plugin>-->
|
||||
<!-- <groupId>com.spotify</groupId>-->
|
||||
<!-- <artifactId>docker-maven-plugin</artifactId>-->
|
||||
<!-- <version>${docker.maven.plugin.version}</version>-->
|
||||
<!-- <executions>-->
|
||||
<!-- <execution>-->
|
||||
<!-- <id>build-image</id>-->
|
||||
<!-- <phase>package</phase>-->
|
||||
<!-- <goals>-->
|
||||
<!-- <goal>build</goal>-->
|
||||
<!-- </goals>-->
|
||||
<!-- </execution>-->
|
||||
<!-- </executions>-->
|
||||
<!-- <configuration>-->
|
||||
<!-- <imageName>mall/${project.artifactId}:${project.version}</imageName>-->
|
||||
<!-- <dockerHost>${docker.host}</dockerHost>-->
|
||||
<!-- <baseImage>java:8</baseImage>-->
|
||||
<!-- <entryPoint>["java", "-jar", "-Dspring.profiles.active=prod","/${project.build.finalName}.jar"]</entryPoint>-->
|
||||
<!-- <resources>-->
|
||||
<!-- <resource>-->
|
||||
<!-- <targetPath>/</targetPath>-->
|
||||
<!-- <directory>${project.build.directory}</directory>-->
|
||||
<!-- <include>${project.build.finalName}.jar</include>-->
|
||||
<!-- </resource>-->
|
||||
<!-- </resources>-->
|
||||
<!-- </configuration>-->
|
||||
<!-- </plugin>-->
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.tuling.tulingmall.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.client.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 根据RestTemplate特性自己改造
|
||||
* Created by smlz on 2019/11/19.
|
||||
*/
|
||||
@Slf4j
|
||||
public class TulingRestTemplate extends RestTemplate {
|
||||
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
public TulingRestTemplate(DiscoveryClient discoveryClient) {
|
||||
this.discoveryClient = discoveryClient;
|
||||
}
|
||||
|
||||
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
|
||||
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
|
||||
|
||||
Assert.notNull(url, "URI is required");
|
||||
Assert.notNull(method, "HttpMethod is required");
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
//判断url的拦截路径,然后去redis(作为注册中心)获取地址随机选取一个
|
||||
log.info("请求的url路径为:{}",url);
|
||||
url = replaceUrl(url);
|
||||
log.info("替换后的路径:{}",url);
|
||||
ClientHttpRequest request = createRequest(url, method);
|
||||
if (requestCallback != null) {
|
||||
requestCallback.doWithRequest(request);
|
||||
}
|
||||
response = request.execute();
|
||||
handleResponse(url, method, response);
|
||||
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
String resource = url.toString();
|
||||
String query = url.getRawQuery();
|
||||
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
|
||||
throw new ResourceAccessException("I/O error on " + method.name() +
|
||||
" request for \"" + resource + "\": " + ex.getMessage(), ex);
|
||||
} finally {
|
||||
if (response != null) {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 把服务实例名称替换为ip:端口
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private URI replaceUrl(URI url){
|
||||
//解析我们的微服务的名称
|
||||
String sourceUrl = url.toString();
|
||||
String [] httpUrl = sourceUrl.split("//");
|
||||
int index = httpUrl[1].replaceFirst("/","@").indexOf("@");
|
||||
String serviceName = httpUrl[1].substring(0,index);
|
||||
|
||||
//通过微服务的名称去nacos服务端获取 对应的实例列表
|
||||
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances(serviceName);
|
||||
if(serviceInstanceList.isEmpty()) {
|
||||
throw new RuntimeException("没有可用的微服务实例列表:"+serviceName);
|
||||
}
|
||||
|
||||
//采取随机的获取一个
|
||||
Random random = new Random();
|
||||
Integer randomIndex = random.nextInt(serviceInstanceList.size());
|
||||
log.info("随机下标:{}",randomIndex);
|
||||
String serviceIp = serviceInstanceList.get(randomIndex).getUri().toString();
|
||||
log.info("随机选举的服务IP:{}",serviceIp);
|
||||
String targetSource = httpUrl[1].replace(serviceName,serviceIp);
|
||||
try {
|
||||
return new URI(targetSource);
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.tuling.tulingmall;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication
|
||||
public class MallGatewayApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MallGatewayApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.tuling.tulingmall.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
/**
|
||||
* 允许跨域请求
|
||||
* @return
|
||||
* bean
|
||||
*/
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true); // 允许cookies跨域
|
||||
config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
|
||||
config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
|
||||
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
|
||||
config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.tuling.tulingmall.config;
|
||||
|
||||
import com.tuling.tulingmall.Component.TulingRestTemplate;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by smlz on 2019/12/26.
|
||||
*/
|
||||
@Configuration
|
||||
public class RibbonConfig {
|
||||
|
||||
/**
|
||||
* 方法实现说明:原生的RestTemplate +@LB不行 因为在
|
||||
* InitializingBean方法执行前我们的RestTemplate还没有被增强
|
||||
* 需要自己改写RestTemplate
|
||||
* @author:smlz
|
||||
* @return:
|
||||
* @exception:
|
||||
* @date:2020/1/22 14:28
|
||||
*/
|
||||
// @Bean
|
||||
// public TulingRestTemplate restTemplate(DiscoveryClient discoveryClient) {
|
||||
// return new TulingRestTemplate(discoveryClient);
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* 手动注入loadBalancerInterceptor拦截器,实现负载均衡功能
|
||||
* @param loadBalancerInterceptor
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate(LoadBalancerInterceptor loadBalancerInterceptor){
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
List<ClientHttpRequestInterceptor> list = new ArrayList();
|
||||
list.add(loadBalancerInterceptor);
|
||||
restTemplate.setInterceptors(list);
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.tuling.tulingmall.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
|
||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.*;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Slf4j
|
||||
public class CustomErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
|
||||
|
||||
@Autowired
|
||||
private GateWayExceptionHandlerAdvice gateWayExceptionHandlerAdvice;
|
||||
|
||||
/**
|
||||
* Create a new {@code DefaultErrorWebExceptionHandler} instance.
|
||||
*
|
||||
* @param errorAttributes the error attributes
|
||||
* @param resourceProperties the resources configuration properties
|
||||
* @param errorProperties the error configuration properties
|
||||
* @param applicationContext the current application context
|
||||
*/
|
||||
public CustomErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
|
||||
ErrorProperties errorProperties, ApplicationContext applicationContext) {
|
||||
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
|
||||
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
|
||||
Throwable throwable = getError(request);
|
||||
return ServerResponse.status(HttpStatus.OK)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(BodyInserters.fromObject(gateWayExceptionHandlerAdvice.handle(throwable)));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.tuling.tulingmall.exception;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.reactive.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 这里通过观察网关 ErrorWebFluxAutoConfiguration 的配置
|
||||
* 我们只需要 把该该配置类copy下来,然后定义errorWebExceptionHandler的逻辑
|
||||
* 我们需要写一个自定义的异常处理器
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass(WebFluxConfigurer.class)
|
||||
@AutoConfigureBefore(WebFluxAutoConfiguration.class)
|
||||
@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
|
||||
public class ExceptionAutoConfiguration {
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ResourceProperties resourceProperties;
|
||||
|
||||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
private ServerCodecConfigurer serverCodecConfigurer;
|
||||
|
||||
public ExceptionAutoConfiguration(ServerProperties serverProperties,
|
||||
ResourceProperties resourceProperties,
|
||||
ObjectProvider<List<ViewResolver>> viewResolversProvider,
|
||||
ServerCodecConfigurer serverCodecConfigurer,
|
||||
ApplicationContext applicationContext) {
|
||||
this.serverProperties = serverProperties;
|
||||
this.applicationContext = applicationContext;
|
||||
this.resourceProperties = resourceProperties;
|
||||
this.viewResolvers = viewResolversProvider
|
||||
.getIfAvailable(() -> Collections.emptyList());
|
||||
this.serverCodecConfigurer = serverCodecConfigurer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
|
||||
DefaultErrorWebExceptionHandler exceptionHandler = new CustomErrorWebExceptionHandler(
|
||||
errorAttributes, this.resourceProperties,
|
||||
this.serverProperties.getError(), this.applicationContext);
|
||||
exceptionHandler.setViewResolvers(this.viewResolvers);
|
||||
exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
|
||||
exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
|
||||
return exceptionHandler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.tuling.tulingmall.exception;
|
||||
|
||||
|
||||
|
||||
import com.tuling.tulingmall.common.api.CommonResult;
|
||||
import com.tuling.tulingmall.common.exception.GateWayException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GateWayExceptionHandlerAdvice {
|
||||
|
||||
@ExceptionHandler(value = {GateWayException.class})
|
||||
public CommonResult handle(GateWayException ex) {
|
||||
log.error("网关异常code:{},msg:{}", ex.getCode(),ex.getMessage());
|
||||
return CommonResult.failed(ex.getCode(),ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = {Throwable.class})
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public CommonResult handle(Throwable throwable) {
|
||||
|
||||
if(throwable instanceof GateWayException) {
|
||||
return handle((GateWayException) throwable);
|
||||
}else {
|
||||
return CommonResult.failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.tuling.tulingmall.exception;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.DefaultBlockRequestHandler;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.tuling.tulingmall.common.api.CommonResult;
|
||||
import com.tuling.tulingmall.common.api.ResultCode;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
|
||||
|
||||
/**
|
||||
* Created by smlz on 2020/2/20.
|
||||
*/
|
||||
@Component
|
||||
public class GatewayBlockExceptionHandler extends DefaultBlockRequestHandler {
|
||||
|
||||
private static final String DEFAULT_BLOCK_MSG_PREFIX = "Blocked by Sentinel: ";
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {
|
||||
if (acceptsHtml(exchange)) {
|
||||
return htmlErrorResponse(ex);
|
||||
}
|
||||
// JSON result by default.
|
||||
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(fromObject(buildErrorResult(ex)));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> htmlErrorResponse(Throwable ex) {
|
||||
|
||||
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
|
||||
.contentType(MediaType.TEXT_PLAIN)
|
||||
.syncBody(new String(JSON.toJSONString(buildErrorResult(ex))));
|
||||
}
|
||||
|
||||
private CommonResult buildErrorResult(Throwable ex) {
|
||||
if(ex instanceof ParamFlowException) {
|
||||
return CommonResult.failed(ResultCode.TOMANY_REQUEST_ERROR);
|
||||
}else if (ex instanceof DegradeException) {
|
||||
return CommonResult.failed(ResultCode.BACKGROUD_DEGRADE_ERROR);
|
||||
}else{
|
||||
return CommonResult.failed(ResultCode.BAD_GATEWAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference from {@code DefaultErrorWebExceptionHandler} of Spring Boot.
|
||||
*/
|
||||
private boolean acceptsHtml(ServerWebExchange exchange) {
|
||||
try {
|
||||
List<MediaType> acceptedMediaTypes = exchange.getRequest().getHeaders().getAccept();
|
||||
acceptedMediaTypes.remove(MediaType.ALL);
|
||||
MediaType.sortBySpecificityAndQuality(acceptedMediaTypes);
|
||||
return acceptedMediaTypes.stream()
|
||||
.anyMatch(MediaType.TEXT_HTML::isCompatibleWith);
|
||||
} catch (InvalidMediaTypeException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ErrorResult {
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
ErrorResult(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.tuling.tulingmall.filter;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.tuling.tulingmall.Component.TulingRestTemplate;
|
||||
import com.tuling.tulingmall.common.api.ResultCode;
|
||||
import com.tuling.tulingmall.common.exception.GateWayException;
|
||||
import com.tuling.tulingmall.properties.NotAuthUrlProperties;
|
||||
import com.tuling.tulingmall.utils.JwtUtils;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 认证过滤器,根据url判断用户请求是要经过认证 才能访问
|
||||
* Created by smlz on 2019/12/17.
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@EnableConfigurationProperties(value = NotAuthUrlProperties.class)
|
||||
public class AuthorizationFilter implements GlobalFilter,Ordered,InitializingBean {
|
||||
|
||||
// @Autowired
|
||||
// private TulingRestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
/**
|
||||
* 请求各个微服务 不需要用户认证的URL
|
||||
*/
|
||||
@Autowired
|
||||
private NotAuthUrlProperties notAuthUrlProperties;
|
||||
|
||||
/**
|
||||
* jwt的公钥,需要网关启动,远程调用认证中心去获取公钥
|
||||
*/
|
||||
private PublicKey publicKey;
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
|
||||
String currentUrl = exchange.getRequest().getURI().getPath();
|
||||
|
||||
//1:不需要认证的url
|
||||
if(shouldSkip(currentUrl)) {
|
||||
//log.info("跳过认证的URL:{}",currentUrl);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
//log.info("需要认证的URL:{}",currentUrl);
|
||||
|
||||
//第一步:解析出我们Authorization的请求头 value为: “bearer XXXXXXXXXXXXXX”
|
||||
String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
|
||||
|
||||
//第二步:判断Authorization的请求头是否为空
|
||||
if(StringUtils.isEmpty(authHeader)) {
|
||||
log.warn("需要认证的url,请求头为空");
|
||||
throw new GateWayException(ResultCode.AUTHORIZATION_HEADER_IS_EMPTY);
|
||||
}
|
||||
|
||||
//第三步 校验我们的jwt 若jwt不对或者超时都会抛出异常
|
||||
Claims claims = JwtUtils.validateJwtToken(authHeader,publicKey);
|
||||
|
||||
//第四步 把从jwt中解析出来的 用户登陆信息存储到请求头中
|
||||
ServerWebExchange webExchange = wrapHeader(exchange,claims);
|
||||
|
||||
return chain.filter(webExchange);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法实现说明:把我们从jwt解析出来的用户信息存储到请求中
|
||||
* @author:smlz
|
||||
* @param serverWebExchange
|
||||
* @param claims
|
||||
* @return: ServerWebExchange
|
||||
* @exception:
|
||||
* @date:2020/1/22 12:12
|
||||
*/
|
||||
private ServerWebExchange wrapHeader(ServerWebExchange serverWebExchange,Claims claims) {
|
||||
|
||||
String loginUserInfo = JSON.toJSONString(claims);
|
||||
|
||||
//log.info("jwt的用户信息:{}",loginUserInfo);
|
||||
|
||||
String memberId = claims.get("additionalInfo",Map.class).get("memberId").toString();
|
||||
|
||||
String nickName = claims.get("additionalInfo",Map.class).get("nickName").toString();
|
||||
|
||||
//向headers中放文件,记得build
|
||||
ServerHttpRequest request = serverWebExchange.getRequest().mutate()
|
||||
.header("username",claims.get("user_name",String.class))
|
||||
.header("memberId",memberId)
|
||||
.header("nickName",nickName)
|
||||
.build();
|
||||
|
||||
//将现在的request 变成 change对象
|
||||
return serverWebExchange.mutate().request(request).build();
|
||||
}
|
||||
|
||||
|
||||
/* private boolean hasPremisson(Claims claims,String currentUrl) {
|
||||
boolean hasPremisson = false;
|
||||
//登陆用户的权限集合判断
|
||||
List<String> premessionList = claims.get("authorities",List.class);
|
||||
for (String url: premessionList) {
|
||||
if(currentUrl.contains(url)) {
|
||||
hasPremisson = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hasPremisson){
|
||||
log.warn("权限不足");
|
||||
throw new GateWayException(SystemErrorType.FORBIDDEN);
|
||||
}
|
||||
|
||||
return hasPremisson;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 方法实现说明:不需要授权的路径
|
||||
* @author:smlz
|
||||
* @param currentUrl 当前请求路径
|
||||
* @return:
|
||||
* @exception:
|
||||
* @date:2019/12/26 13:49
|
||||
*/
|
||||
private boolean shouldSkip(String currentUrl) {
|
||||
//路径匹配器(简介SpringMvc拦截器的匹配器)
|
||||
//比如/oauth/** 可以匹配/oauth/token /oauth/check_token等
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
for(String skipPath:notAuthUrlProperties.getShouldSkipUrls()) {
|
||||
if(pathMatcher.match(skipPath,currentUrl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法实现说明:网关服务启动 生成公钥
|
||||
* @author:smlz
|
||||
* @return:
|
||||
* @exception:
|
||||
* @date:2020/1/22 11:58
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
//初始化公钥
|
||||
this.publicKey = JwtUtils.genPulicKey(restTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.tuling.tulingmall.properties;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
|
||||
/**
|
||||
* @vlog: 高于生活,源于生活
|
||||
* @desc: 类的描述:网关跳过认证的配置类
|
||||
* @author: smlz
|
||||
* @createDate: 2020/1/22 10:56
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("tuling.gateway")
|
||||
public class NotAuthUrlProperties {
|
||||
|
||||
private LinkedHashSet<String> shouldSkipUrls;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.tuling.tulingmall.utils;
|
||||
|
||||
|
||||
import com.tuling.tulingmall.common.api.ResultCode;
|
||||
import com.tuling.tulingmall.common.exception.GateWayException;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @vlog: 高于生活,源于生活
|
||||
* @desc: 类的描述:Jwt操作类
|
||||
* @author: smlz
|
||||
* @createDate: 2020/1/22 11:29
|
||||
* @version: 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class JwtUtils {
|
||||
|
||||
/**
|
||||
* 认证服务器许可我们的网关的clientId(需要在oauth_client_details表中配置)
|
||||
*/
|
||||
private static final String CLIENT_ID = "api-gateway";
|
||||
|
||||
/**
|
||||
* 认证服务器许可我们的网关的client_secret(需要在oauth_client_details表中配置)
|
||||
*/
|
||||
private static final String CLIENT_SECRET = "tlmall";
|
||||
|
||||
/**
|
||||
* 认证服务器暴露的获取token_key的地址
|
||||
*/
|
||||
private static final String AUTH_TOKEN_KEY_URL = "http://tulingmall-authcenter/oauth/token_key";
|
||||
|
||||
/**
|
||||
* 请求头中的 token的开始
|
||||
*/
|
||||
private static final String AUTH_HEADER = "bearer ";
|
||||
|
||||
/**
|
||||
* 方法实现说明: 通过远程调用获取认证服务器颁发jwt的解析的key
|
||||
* @author:smlz
|
||||
* @param restTemplate 远程调用的操作类
|
||||
* @return: tokenKey 解析jwt的tokenKey
|
||||
* @exception:
|
||||
* @date:2020/1/22 11:31
|
||||
*/
|
||||
private static String getTokenKeyByRemoteCall(RestTemplate restTemplate) throws GateWayException {
|
||||
|
||||
//第一步:封装请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
headers.setBasicAuth(CLIENT_ID,CLIENT_SECRET);
|
||||
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(null, headers);
|
||||
|
||||
//第二步:远程调用获取token_key
|
||||
try {
|
||||
|
||||
ResponseEntity<Map> response = restTemplate.exchange(AUTH_TOKEN_KEY_URL, HttpMethod.GET, entity, Map.class);
|
||||
|
||||
String tokenKey = response.getBody().get("value").toString();
|
||||
|
||||
log.info("去认证服务器获取Token_Key:{}",tokenKey);
|
||||
|
||||
return tokenKey;
|
||||
|
||||
}catch (Exception e) {
|
||||
|
||||
log.error("远程调用认证服务器获取Token_Key失败:{}",e.getMessage());
|
||||
|
||||
throw new GateWayException(ResultCode.GET_TOKEN_KEY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法实现说明:生成公钥
|
||||
* @author:smlz
|
||||
* @param restTemplate:远程调用操作类
|
||||
* @return: PublicKey 公钥对象
|
||||
* @exception:
|
||||
* @date:2020/1/22 11:52
|
||||
*/
|
||||
public static PublicKey genPulicKey(RestTemplate restTemplate) throws GateWayException {
|
||||
|
||||
String tokenKey = getTokenKeyByRemoteCall(restTemplate);
|
||||
|
||||
try{
|
||||
|
||||
//把获取的公钥开头和结尾替换掉
|
||||
String dealTokenKey =tokenKey.replaceAll("\\-*BEGIN PUBLIC KEY\\-*", "").replaceAll("\\-*END PUBLIC KEY\\-*", "").trim();
|
||||
|
||||
java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
|
||||
|
||||
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(dealTokenKey));
|
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
|
||||
PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);
|
||||
|
||||
log.info("生成公钥:{}",publicKey);
|
||||
|
||||
return publicKey;
|
||||
|
||||
}catch (Exception e) {
|
||||
|
||||
log.info("生成公钥异常:{}",e.getMessage());
|
||||
|
||||
throw new GateWayException(ResultCode.GEN_PUBLIC_KEY_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static Claims validateJwtToken(String authHeader,PublicKey publicKey) {
|
||||
String token =null ;
|
||||
try{
|
||||
token = StringUtils.substringAfter(authHeader, AUTH_HEADER);
|
||||
|
||||
Jwt<JwsHeader, Claims> parseClaimsJwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
|
||||
|
||||
Claims claims = parseClaimsJwt.getBody();
|
||||
|
||||
//log.info("claims:{}",claims);
|
||||
|
||||
return claims;
|
||||
|
||||
}catch(Exception e){
|
||||
|
||||
log.error("校验token异常:{},异常信息:{}",token,e.getMessage());
|
||||
|
||||
throw new GateWayException(ResultCode.JWT_TOKEN_EXPIRE);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
tulingmall-gateway/src/main/resources/application.yml
Normal file
66
tulingmall-gateway/src/main/resources/application.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
server:
|
||||
port: 8888
|
||||
#spring:
|
||||
# application:
|
||||
# name: tulingmall-gateway
|
||||
# cloud:
|
||||
# gateway:
|
||||
# discovery:
|
||||
# locator:
|
||||
# lower-case-service-id: true
|
||||
# enabled: true
|
||||
# routes:
|
||||
# - id: tulingmall-authcenter
|
||||
# uri: lb://tulingmall-authcenter
|
||||
# predicates:
|
||||
# - Path=/oauth/**
|
||||
# - id: tulingmall-member
|
||||
# uri: lb://tulingmall-member
|
||||
# predicates:
|
||||
# - Path=/sso/**,/member/**
|
||||
# - id: tulingmall-order-curr
|
||||
# uri: lb://tulingmall-order-curr
|
||||
# predicates:
|
||||
# - Path=/order/**
|
||||
# - id: tulingmall-cart
|
||||
# uri: lb://tulingmall-cart
|
||||
# predicates:
|
||||
# - Path=/cart/**
|
||||
# - id: tulingmall-product
|
||||
# uri: lb://tulingmall-product
|
||||
# predicates:
|
||||
# - Path=/pms/**
|
||||
# - id: tulingmall-coupons
|
||||
# uri: lb://tulingmall-coupons
|
||||
# predicates:
|
||||
# - Path=/coupon/**
|
||||
# - id: tulingmall-portal
|
||||
# uri: lb://tulingmall-portal
|
||||
# predicates:
|
||||
# - Path=/home/**,/portal/**
|
||||
# nacos:
|
||||
# discovery:
|
||||
# server-addr: 192.168.65.206:8848,192.168.65.209:8848,192.168.65.210:8848
|
||||
#
|
||||
#management: #开启SpringBoot Admin的监控
|
||||
# endpoints:
|
||||
# web:
|
||||
# exposure:
|
||||
# include: '*'
|
||||
# endpoint:
|
||||
# health:
|
||||
# show-details: always
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: debug
|
||||
|
||||
#tuling:
|
||||
# gateway:
|
||||
# shouldSkipUrls:
|
||||
# - /oauth/**
|
||||
# - /sso/**
|
||||
# - /home/**
|
||||
# - /portal/commentlist/**
|
||||
# - /order/paySuccess/**
|
||||
# - /pms/**
|
||||
# - /static/qrcode/**
|
||||
53
tulingmall-gateway/src/main/resources/application.yml.bak
Normal file
53
tulingmall-gateway/src/main/resources/application.yml.bak
Normal file
@@ -0,0 +1,53 @@
|
||||
server:
|
||||
port: 8888
|
||||
spring:
|
||||
application:
|
||||
name: tulingmall-gateway
|
||||
cloud:
|
||||
gateway:
|
||||
discovery:
|
||||
locator:
|
||||
lower-case-service-id: true
|
||||
enabled: true
|
||||
routes:
|
||||
- id: tulingmall-authcenter
|
||||
uri: lb://tulingmall-authcenter
|
||||
predicates:
|
||||
- Path=/oauth/**
|
||||
- id: tulingmall-member
|
||||
uri: lb://tulingmall-member
|
||||
predicates:
|
||||
- Path=/sso/**,/member/**
|
||||
- id: tulingmall-order
|
||||
uri: lb://tulingmall-order
|
||||
predicates:
|
||||
- Path=/cart/**,/order/**
|
||||
- id: tulingmall-product
|
||||
uri: lb://tulingmall-product
|
||||
predicates:
|
||||
- Path=/pms/**
|
||||
- id: tulingmall-coupons
|
||||
uri: lb://tulingmall-coupons
|
||||
predicates:
|
||||
- Path=/coupon/**
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
|
||||
management: #开启SpringBoot Admin的监控
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: '*'
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
logging:
|
||||
level:
|
||||
org.springframework.cloud.gateway: debug
|
||||
|
||||
tuling:
|
||||
gateway:
|
||||
shouldSkipUrls:
|
||||
- /oauth/**
|
||||
- /sso/**
|
||||
19
tulingmall-gateway/src/main/resources/bootstrap.yml
Normal file
19
tulingmall-gateway/src/main/resources/bootstrap.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
spring:
|
||||
application:
|
||||
name: tulingmall-gateway
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: 127.0.0.1:8848 #配置中心的地址
|
||||
file-extension: yml #配置文件结尾的配置
|
||||
shared-configs[0]:
|
||||
data-id: tulingmall-nacos.yml
|
||||
group: DEFAULT_GROUP
|
||||
refresh: true
|
||||
shared-configs[1]:
|
||||
data-id: tulingmall-db-common.yml
|
||||
group: DEFAULT_GROUP
|
||||
refresh: true
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
14
tulingmall-gateway/src/main/resources/bootstrap.yml.bak
Normal file
14
tulingmall-gateway/src/main/resources/bootstrap.yml.bak
Normal file
@@ -0,0 +1,14 @@
|
||||
spring:
|
||||
application:
|
||||
name: tulingmall-gateway
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
server-addr: 192.168.159.8:8848 #配置中心的地址
|
||||
file-extension: yml #配置文件结尾的配置
|
||||
shared-dataids: tulingmall-common.yml #图灵商城公共配置
|
||||
discovery:
|
||||
server-addr: 192.168.159.8:8848
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
16
tulingmall-gateway/src/main/resources/rebel.xml
Normal file
16
tulingmall-gateway/src/main/resources/rebel.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
This is the JRebel configuration file. It maps the running application to your IDE workspace, enabling JRebel reloading for this project.
|
||||
Refer to https://manuals.jrebel.com/jrebel/standalone/config.html for more information.
|
||||
-->
|
||||
<application generated-by="intellij" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.zeroturnaround.com" xsi:schemaLocation="http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_3.xsd">
|
||||
|
||||
<id>tulingmall-gateway</id>
|
||||
|
||||
<classpath>
|
||||
<dir name="D:/GitSource/tmallV5/tulingmall-gateway/target/classes">
|
||||
</dir>
|
||||
</classpath>
|
||||
|
||||
</application>
|
||||
Reference in New Issue
Block a user