From cde9fa4bb6891091f7c044cfc711ce0a74907b2f Mon Sep 17 00:00:00 2001 From: wxz Date: Fri, 25 Jun 2021 13:31:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=B7=A8=E5=9F=9F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8Cdb:epmet=5Fadmin.cors=5Fconfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../epmet/dto/result/CorsConfigResultDTO.java | 20 +++ .../feign/EpmetAdminOpenFeignClient.java | 24 +++ .../EpmetAdminOpenFeignClientFallback.java | 18 +++ .../controller/CorsConfigController.java | 32 ++++ .../java/com/epmet/dao/CorsConfigDao.java | 33 +++++ .../com/epmet/entity/CorsConfigEntity.java | 51 +++++++ .../com/epmet/service/CorsConfigService.java | 14 ++ .../service/impl/CorsConfigServiceImpl.java | 36 +++++ .../main/resources/mapper/CorsConfigDao.xml | 19 +++ .../epmet/commons/tools/redis/RedisKeys.java | 10 ++ epmet-gateway/pom.xml | 5 + .../java/com/epmet/bean/CorsConfigCache.java | 21 +++ .../java/com/epmet/config/CorsConfig.java | 137 ++++++++++++++---- 13 files changed, 395 insertions(+), 25 deletions(-) create mode 100644 epmet-admin/epmet-admin-client/src/main/java/com/epmet/dto/result/CorsConfigResultDTO.java create mode 100644 epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/EpmetAdminOpenFeignClient.java create mode 100644 epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/fallback/EpmetAdminOpenFeignClientFallback.java create mode 100644 epmet-admin/epmet-admin-server/src/main/java/com/epmet/controller/CorsConfigController.java create mode 100644 epmet-admin/epmet-admin-server/src/main/java/com/epmet/dao/CorsConfigDao.java create mode 100644 epmet-admin/epmet-admin-server/src/main/java/com/epmet/entity/CorsConfigEntity.java create mode 100644 epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/CorsConfigService.java create mode 100644 epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/impl/CorsConfigServiceImpl.java create mode 100644 epmet-admin/epmet-admin-server/src/main/resources/mapper/CorsConfigDao.xml create mode 100644 epmet-gateway/src/main/java/com/epmet/bean/CorsConfigCache.java diff --git a/epmet-admin/epmet-admin-client/src/main/java/com/epmet/dto/result/CorsConfigResultDTO.java b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/dto/result/CorsConfigResultDTO.java new file mode 100644 index 0000000000..3faa333d74 --- /dev/null +++ b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/dto/result/CorsConfigResultDTO.java @@ -0,0 +1,20 @@ +package com.epmet.dto.result; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CorsConfigResultDTO { + /** + * 首部类型 + */ + private String headerType; + + /** + * 首部值 + */ + private String headerValue; +} diff --git a/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/EpmetAdminOpenFeignClient.java b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/EpmetAdminOpenFeignClient.java new file mode 100644 index 0000000000..e1b48a9186 --- /dev/null +++ b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/EpmetAdminOpenFeignClient.java @@ -0,0 +1,24 @@ +package com.epmet.feign; + +import com.epmet.commons.tools.constant.ServiceConstant; +import com.epmet.commons.tools.utils.Result; +import com.epmet.dto.result.CorsConfigResultDTO; +import com.epmet.feign.fallback.EpmetAdminOpenFeignClientFallback; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.List; + +@FeignClient(name = ServiceConstant.EPMET_ADMIN_SERVER, fallback = EpmetAdminOpenFeignClientFallback.class) +//@FeignClient(name = ServiceConstant.EPMET_ADMIN_SERVER, fallback = EpmetAdminOpenFeignClientFallback.class, url = "localhost:8082") +public interface EpmetAdminOpenFeignClient { + + /** + * @Description 查询跨域配置列表 + * @return + * @author wxz + * @date 2021.06.25 10:00 + */ + @PostMapping("/sys/cors-config/list") + Result> list(); +} diff --git a/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/fallback/EpmetAdminOpenFeignClientFallback.java b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/fallback/EpmetAdminOpenFeignClientFallback.java new file mode 100644 index 0000000000..72edce3446 --- /dev/null +++ b/epmet-admin/epmet-admin-client/src/main/java/com/epmet/feign/fallback/EpmetAdminOpenFeignClientFallback.java @@ -0,0 +1,18 @@ +package com.epmet.feign.fallback; + +import com.epmet.commons.tools.constant.ServiceConstant; +import com.epmet.commons.tools.utils.ModuleUtils; +import com.epmet.commons.tools.utils.Result; +import com.epmet.dto.result.CorsConfigResultDTO; +import com.epmet.feign.EpmetAdminOpenFeignClient; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class EpmetAdminOpenFeignClientFallback implements EpmetAdminOpenFeignClient { + @Override + public Result> list() { + return ModuleUtils.feignConError(ServiceConstant.EPMET_USER_SERVER, "list", null); + } +} diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/controller/CorsConfigController.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/controller/CorsConfigController.java new file mode 100644 index 0000000000..e2972f93d6 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/controller/CorsConfigController.java @@ -0,0 +1,32 @@ +package com.epmet.controller; + +import com.epmet.commons.tools.utils.Result; +import com.epmet.dto.result.CorsConfigResultDTO; +import com.epmet.service.CorsConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("cors-config") +public class CorsConfigController { + + @Autowired + private CorsConfigService corsConfigService; + + /** + * @Description 查询跨域配置列表 + * @return + * @author wxz + * @date 2021.06.25 10:00 + */ + @PostMapping("/list") + public Result> list() { + List list = corsConfigService.list(); + return new Result>().ok(list); + } + +} diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/dao/CorsConfigDao.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/dao/CorsConfigDao.java new file mode 100644 index 0000000000..0b6c499833 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/dao/CorsConfigDao.java @@ -0,0 +1,33 @@ +/** + * Copyright 2018 人人开源 https://www.renren.io + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.epmet.dao; + +import com.epmet.commons.mybatis.dao.BaseDao; +import com.epmet.entity.CorsConfigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 跨域配置表 + * + * @author generator generator@elink-cn.com + * @since v1.0.0 2021-06-25 + */ +@Mapper +public interface CorsConfigDao extends BaseDao { + +} \ No newline at end of file diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/entity/CorsConfigEntity.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/entity/CorsConfigEntity.java new file mode 100644 index 0000000000..75f7258e76 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/entity/CorsConfigEntity.java @@ -0,0 +1,51 @@ +/** + * Copyright 2018 人人开源 https://www.renren.io + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.epmet.entity; + +import com.baomidou.mybatisplus.annotation.TableName; + +import com.epmet.commons.mybatis.entity.BaseEpmetEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 跨域配置表 + * + * @author generator generator@elink-cn.com + * @since v1.0.0 2021-06-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("cors_config") +public class CorsConfigEntity extends BaseEpmetEntity { + + private static final long serialVersionUID = 1L; + + /** + * 首部类型 + */ + private String headerType; + + /** + * 首部值 + */ + private String headerValue; + +} diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/CorsConfigService.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/CorsConfigService.java new file mode 100644 index 0000000000..8bae0f9531 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/CorsConfigService.java @@ -0,0 +1,14 @@ +package com.epmet.service; + +import com.epmet.dto.result.CorsConfigResultDTO; + +import java.util.List; + +/** + * @Description 跨域配置service接口 + * @author wxz + * @date 2021-06-44 09:52:44 +*/ +public interface CorsConfigService { + List list(); +} diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/impl/CorsConfigServiceImpl.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/impl/CorsConfigServiceImpl.java new file mode 100644 index 0000000000..aed116d421 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/service/impl/CorsConfigServiceImpl.java @@ -0,0 +1,36 @@ +package com.epmet.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.epmet.commons.tools.utils.ConvertUtils; +import com.epmet.dao.CorsConfigDao; +import com.epmet.dto.result.CorsConfigResultDTO; +import com.epmet.entity.CorsConfigEntity; +import com.epmet.service.CorsConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Description 跨域配置 + * @author wxz + * @date 2021.06.25 09:53:57 +*/ +@Service +public class CorsConfigServiceImpl implements CorsConfigService { + + @Autowired + private CorsConfigDao corsConfigDao; + + /** + * @Description 查询配置列表 + * @return + * @author wxz + * @date 2021.06.25 09:58 + */ + @Override + public List list() { + LambdaQueryWrapper conditions = new LambdaQueryWrapper<>(); + return ConvertUtils.sourceToTarget(corsConfigDao.selectList(conditions), CorsConfigResultDTO.class); + } +} diff --git a/epmet-admin/epmet-admin-server/src/main/resources/mapper/CorsConfigDao.xml b/epmet-admin/epmet-admin-server/src/main/resources/mapper/CorsConfigDao.xml new file mode 100644 index 0000000000..ce43effd0a --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/resources/mapper/CorsConfigDao.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java index 0dfa22915c..68c417c580 100644 --- a/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java +++ b/epmet-commons/epmet-commons-tools/src/main/java/com/epmet/commons/tools/redis/RedisKeys.java @@ -459,4 +459,14 @@ public class RedisKeys { String currentMonth= DateUtils.getBeforeNMonth(NumConstant.ZERO); return "groupread:user:".concat(currentMonth).concat(":").concat(source).concat(":").concat(groupId).concat(":").concat(sourceId); } + + /** + * @Description 跨域配置key + * @return + * @author wxz + * @date 2021.06.25 10:31 + */ + public static String getCorsConfigKey() { + return rootPrefix.concat("sys:cors"); + } } diff --git a/epmet-gateway/pom.xml b/epmet-gateway/pom.xml index b89bc59c0e..6d62b56d53 100644 --- a/epmet-gateway/pom.xml +++ b/epmet-gateway/pom.xml @@ -81,6 +81,11 @@ epmet-commons-openapi 2.0.0 + + com.epmet + epmet-admin-client + 2.0.0 + diff --git a/epmet-gateway/src/main/java/com/epmet/bean/CorsConfigCache.java b/epmet-gateway/src/main/java/com/epmet/bean/CorsConfigCache.java new file mode 100644 index 0000000000..3a44510959 --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/bean/CorsConfigCache.java @@ -0,0 +1,21 @@ +package com.epmet.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Description 跨域配置缓存 + * @author wxz + * @date 2021.06.25 10:59:44 +*/ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CorsConfigCache { + + private List accessControlAllowOrigins; + +} diff --git a/epmet-gateway/src/main/java/com/epmet/config/CorsConfig.java b/epmet-gateway/src/main/java/com/epmet/config/CorsConfig.java index ffec185a98..0349eaac8d 100644 --- a/epmet-gateway/src/main/java/com/epmet/config/CorsConfig.java +++ b/epmet-gateway/src/main/java/com/epmet/config/CorsConfig.java @@ -1,15 +1,29 @@ /** * Copyright (c) 2018 人人开源 All rights reserved. - * + *

* https://www.renren.io - * + *

* 版权所有,侵权必究! */ package com.epmet.config; +import com.epmet.bean.CorsConfigCache; +import com.epmet.commons.tools.constant.ServiceConstant; +import com.epmet.commons.tools.exception.EpmetErrorCode; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.epmet.commons.tools.feign.ResultDataResolver; +import com.epmet.commons.tools.redis.RedisKeys; +import com.epmet.dto.result.CorsConfigResultDTO; +import com.epmet.feign.EpmetAdminOpenFeignClient; +import org.apache.commons.collections4.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -21,6 +35,11 @@ import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + /** * Cors跨域 * @@ -29,33 +48,101 @@ import reactor.core.publisher.Mono; */ @Configuration public class CorsConfig { - private static final String MAX_AGE = "18000L"; @Bean public WebFilter corsFilter() { - return (ServerWebExchange ctx, WebFilterChain chain) -> { - ServerHttpRequest request = ctx.getRequest(); - if (!CorsUtils.isCorsRequest(request)) { - return chain.filter(ctx); - } - HttpHeaders requestHeaders = request.getHeaders(); - ServerHttpResponse response = ctx.getResponse(); - HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); - HttpHeaders headers = response.getHeaders(); + return new EpmetWebFilter(); + } +} + +/** + * @Description 用于CORS等操作的判断 + * @return + * @author wxz + * @date 2021.06.25 10:42 + */ +class EpmetWebFilter implements WebFilter, ResultDataResolver { + + private static final String MAX_AGE = "18000L"; + + @Autowired + private EpmetAdminOpenFeignClient adminOpenFeignClient; + + @Resource + private RedisTemplate redisTemplate; + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public Mono filter(ServerWebExchange ctx, WebFilterChain chain) { + ServerHttpRequest request = ctx.getRequest(); + if (!CorsUtils.isCorsRequest(request)) { + return chain.filter(ctx); + } + HttpHeaders requestHeaders = request.getHeaders(); + ServerHttpResponse response = ctx.getResponse(); + HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); + HttpHeaders headers = response.getHeaders(); + if (isAllowed(requestHeaders.getOrigin())) { headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin()); - headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders()); - if (requestMethod != null) { - headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name()); + } + headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders()); + if (requestMethod != null) { + headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name()); + } + headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); + headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*"); + headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE); + if (request.getMethod() == HttpMethod.OPTIONS) { + response.setStatusCode(HttpStatus.OK); + return Mono.empty(); + } + return chain.filter(ctx); + } + + /** + * @Description 是否允许跨域 + * @return + * @author wxz + * @date 2021.06.25 10:43 + */ + private Boolean isAllowed(String origin) { + HashOperations> ope = redisTemplate.opsForHash(); + Map> configMap = ope.entries(RedisKeys.getCorsConfigKey()); + + if (configMap == null || configMap.size() == 0) { + List data = null; + try { + data = getResultDataOrThrowsException( + adminOpenFeignClient.list(), + ServiceConstant.EPMET_ADMIN_SERVER, + EpmetErrorCode.SERVER_ERROR.getCode(), + "调用Admin服务查询Cors配置失败"); + } catch (Exception e) { + logger.error("调用Admin服务查询Cors配置失败"); } - headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); - headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*"); - headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE); - if (request.getMethod() == HttpMethod.OPTIONS) { - response.setStatusCode(HttpStatus.OK); - return Mono.empty(); + + // 将origin集合 + List origins = data.stream() + .filter((c) -> HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN.equals(c.getHeaderType())) + .map((c) -> c.getHeaderValue()) + .collect(Collectors.toList()); + + try { + configMap.put(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origins); + ope.putAll(RedisKeys.getCorsConfigKey(), configMap); + } catch (Exception e) { + logger.error(String.format("gateway缓存跨域配置出错:%s", ExceptionUtils.getErrorStackTrace(e))); } - return chain.filter(ctx); - }; - } + } -} + List accessControlAllowOrigins = configMap.get(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN); + if (CollectionUtils.isEmpty(accessControlAllowOrigins)) { + return false; + } + if (accessControlAllowOrigins.contains("*")) { + return true; + } + return accessControlAllowOrigins.contains(origin); + } +} \ No newline at end of file