mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
5.0
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -27,6 +27,7 @@
|
||||
<!-- <module>sop-website</module>-->
|
||||
<module>sop-index</module>
|
||||
<module>sop-spring-boot-starter</module>
|
||||
<module>sop-registry</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@@ -119,15 +120,11 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.oschina.durcframework</groupId>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
<version>${fastmybatis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
|
@@ -54,6 +54,16 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 仅在开发中使用 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@@ -2,14 +2,17 @@ package com.gitee.sop.storyweb.open;
|
||||
|
||||
import com.gitee.sop.storyweb.open.req.StorySaveDTO;
|
||||
import com.gitee.sop.storyweb.open.resp.StoryResponse;
|
||||
import com.gitee.sop.support.annotation.Open;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface StoryService {
|
||||
|
||||
@Open("story.save")
|
||||
Integer save(StorySaveDTO storySaveDTO);
|
||||
|
||||
@Open("story.get")
|
||||
StoryResponse getById(Integer id);
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package com.gitee.sop.storyweb.open.impl;
|
||||
|
||||
import com.gitee.sop.springboot.starter.annotation.Open;
|
||||
import com.gitee.sop.support.annotation.Open;
|
||||
import com.gitee.sop.storyweb.open.StoryService;
|
||||
import com.gitee.sop.storyweb.open.req.StorySaveDTO;
|
||||
import com.gitee.sop.storyweb.open.resp.StoryResponse;
|
||||
@@ -17,14 +17,12 @@ import javax.validation.constraints.NotNull;
|
||||
@DubboService(validation = "true")
|
||||
public class StoreyServiceImpl implements StoryService {
|
||||
|
||||
@Open("story.save")
|
||||
@Override
|
||||
public Integer save(StorySaveDTO storySaveDTO) {
|
||||
System.out.println("save storySaveDTO:" + storySaveDTO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Open("story.get")
|
||||
@Override
|
||||
public StoryResponse getById(@NotNull(message = "id必填") Integer id) {
|
||||
StoryResponse storyResponse = new StoryResponse();
|
||||
|
@@ -1,9 +1,7 @@
|
||||
server.port=8082
|
||||
spring.application.name=story-service
|
||||
# nacos
|
||||
nacos.host=127.0.0.1:8848
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.application.qos-enable=false
|
||||
dubbo.registry.address=nacos://${nacos.host}
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package com.gitee.sop.index.api;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IsvService {
|
||||
void refresh(String appId);
|
||||
}
|
@@ -25,8 +25,6 @@ public class RegisterDTO implements Serializable {
|
||||
|
||||
private String paramTypeName;
|
||||
|
||||
private Integer isIgnoreValidate;
|
||||
|
||||
private Integer isPermission;
|
||||
|
||||
private Integer isNeedToken;
|
||||
|
@@ -44,6 +44,19 @@
|
||||
<version>0.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.gitee.durcframework</groupId>
|
||||
<artifactId>fastmybatis-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
@@ -65,6 +78,24 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided -->
|
||||
|
||||
<!-- 仅在开发中使用 -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 仅在开发中使用 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@@ -31,6 +31,8 @@ public class ApiInfoDTO implements Serializable {
|
||||
|
||||
private Integer isNeedToken;
|
||||
|
||||
private Integer status;
|
||||
|
||||
public String buildApiNameVersion() {
|
||||
return apiName + apiVersion;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.index.controller.param;
|
||||
package com.gitee.sop.index.common;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.index.common;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
public class ApiRequestContext {
|
||||
|
||||
private ApiRequest apiRequest;
|
||||
private Locale locale;
|
||||
private String ip;
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.index.controller.param;
|
||||
package com.gitee.sop.index.common;
|
||||
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
@@ -93,10 +93,9 @@ public class ApiResponse {
|
||||
}
|
||||
|
||||
|
||||
public static ApiResponse error(ErrorEnum errorEnum) {
|
||||
public static ApiResponse error(ApiException e) {
|
||||
ApiResponse apiResponse = new ApiResponse();
|
||||
ErrorMeta errorMeta = errorEnum.getErrorMeta();
|
||||
IError error = errorMeta.getError(Locale.SIMPLIFIED_CHINESE);
|
||||
IError error = e.getError();
|
||||
apiResponse.setCode(error.getCode());
|
||||
apiResponse.setMsg(error.getMsg());
|
||||
apiResponse.setSub_code(error.getSub_code());
|
||||
@@ -104,10 +103,10 @@ public class ApiResponse {
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
public static ApiResponse error(ErrorEnum errorEnum, String subMsg) {
|
||||
public static ApiResponse error(ErrorEnum errorEnum, Locale locale, String subMsg) {
|
||||
ApiResponse apiResponse = new ApiResponse();
|
||||
ErrorMeta errorMeta = errorEnum.getErrorMeta();
|
||||
IError error = errorMeta.getError(Locale.SIMPLIFIED_CHINESE);
|
||||
IError error = errorMeta.getError(locale);
|
||||
apiResponse.setCode(error.getCode());
|
||||
apiResponse.setMsg(error.getMsg());
|
||||
apiResponse.setSub_code(error.getSub_code());
|
||||
@@ -115,4 +114,15 @@ public class ApiResponse {
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
public static ApiResponse error(ErrorEnum errorEnum, Locale locale) {
|
||||
ApiResponse apiResponse = new ApiResponse();
|
||||
ErrorMeta errorMeta = errorEnum.getErrorMeta();
|
||||
IError error = errorMeta.getError(locale);
|
||||
apiResponse.setCode(error.getCode());
|
||||
apiResponse.setMsg(error.getMsg());
|
||||
apiResponse.setSub_code(error.getSub_code());
|
||||
apiResponse.setSub_msg(error.getSub_msg());
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.gitee.sop.index.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class RouteContext {
|
||||
|
||||
private ApiRequestContext apiRequestContext;
|
||||
private ApiInfoDTO apiInfo;
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.gitee.sop.index.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum StatusEnum {
|
||||
ENABLE(1),
|
||||
DISABLE(0);
|
||||
|
||||
private final int value;
|
||||
|
||||
}
|
@@ -1,16 +1,22 @@
|
||||
package com.gitee.sop.index.config;
|
||||
|
||||
import com.gitee.sop.index.message.ErrorFactory;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalApiCacheManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalSecretManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.LocalIsvManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisApiCacheManagerImpl;
|
||||
import com.gitee.sop.index.service.manager.impl.RedisIsvManagerImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class IndexConfig {
|
||||
|
||||
@Bean
|
||||
@@ -26,9 +32,20 @@ public class IndexConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.secret", havingValue = "local", matchIfMissing = true)
|
||||
public LocalSecretManagerImpl localSecretManager() {
|
||||
return new LocalSecretManagerImpl();
|
||||
@ConditionalOnProperty(value = "manager.isv", havingValue = "local", matchIfMissing = true)
|
||||
public LocalIsvManagerImpl localIsvManager() {
|
||||
return new LocalIsvManagerImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "manager.isv", havingValue = "redis")
|
||||
public RedisIsvManagerImpl redisIsvManager() {
|
||||
return new RedisIsvManagerImpl();
|
||||
}
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
ErrorFactory.initMessageSource(null);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package com.gitee.sop.index.controller;
|
||||
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.controller.param.ApiResponse;
|
||||
import com.gitee.sop.index.common.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ApiResponse;
|
||||
import com.gitee.sop.index.service.RouteService;
|
||||
import com.gitee.sop.index.util.RequestUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -10,6 +12,8 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 开放平台入口
|
||||
*
|
||||
@@ -23,7 +27,7 @@ public class IndexController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String home() {
|
||||
return "api";
|
||||
return "Open Platform";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,8 +36,14 @@ public class IndexController {
|
||||
* @return 返回响应内容
|
||||
*/
|
||||
@PostMapping("api")
|
||||
public ApiResponse index(@Validated @RequestBody ApiRequest request) {
|
||||
return routeService.route(request);
|
||||
public ApiResponse index(@RequestBody ApiRequest apiRequest, HttpServletRequest request) {
|
||||
String ip = RequestUtil.getIP(request);
|
||||
ApiRequestContext apiRequestContext = ApiRequestContext.builder()
|
||||
.apiRequest(apiRequest)
|
||||
.locale(request.getLocale())
|
||||
.ip(ip)
|
||||
.build();
|
||||
return routeService.route(apiRequestContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,80 @@
|
||||
package com.gitee.sop.index.dao.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
import com.gitee.fastmybatis.annotation.Pk;
|
||||
import com.gitee.fastmybatis.annotation.PkStrategy;
|
||||
import com.gitee.fastmybatis.annotation.Table;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 表名:api_info
|
||||
* 备注:接口信息表
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Table(name = "api_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
|
||||
@Data
|
||||
public class ApiInfo {
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
*/
|
||||
private String application;
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
private String apiName;
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* 接口class
|
||||
*/
|
||||
private String interfaceClassName;
|
||||
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
private String methodName;
|
||||
|
||||
/**
|
||||
* 参数名称
|
||||
*/
|
||||
private String paramName;
|
||||
|
||||
/**
|
||||
* 参数类型名称
|
||||
*/
|
||||
private String paramTypeName;
|
||||
|
||||
/**
|
||||
* 接口是否需要授权访问
|
||||
*/
|
||||
private Integer isPermission;
|
||||
|
||||
/**
|
||||
* 是否需要appAuthToken
|
||||
*/
|
||||
private Integer isNeedToken;
|
||||
|
||||
/**
|
||||
* 状态,1-启用,0-禁用
|
||||
*/
|
||||
private Integer apiStatus;
|
||||
|
||||
private LocalDateTime addTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.gitee.sop.index.dao.mapper;
|
||||
|
||||
|
||||
import com.gitee.fastmybatis.core.mapper.BaseMapper;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ApiInfoMapper extends BaseMapper<ApiInfo> {
|
||||
|
||||
default ApiInfo getByNameVersion(String apiName, String apiVersion) {
|
||||
return this.query()
|
||||
.eq(ApiInfo::getApiName, apiName)
|
||||
.eq(ApiInfo::getApiVersion, apiVersion)
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
package com.gitee.sop.index.dubbo;
|
||||
|
||||
import com.gitee.sop.index.api.ApiRegisterService;
|
||||
import com.gitee.sop.index.api.RegisterDTO;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Slf4j
|
||||
@DubboService
|
||||
public class ApiRegisterServiceImpl implements ApiRegisterService {
|
||||
|
||||
@Autowired
|
||||
private ApiCacheManager apiCache;
|
||||
|
||||
@Override
|
||||
public void register(RegisterDTO registerDTO) {
|
||||
log.info("收到接口注册, registerDTO={}", registerDTO);
|
||||
ApiInfoDTO apiInfoDTO = new ApiInfoDTO();
|
||||
BeanUtils.copyProperties(registerDTO, apiInfoDTO);
|
||||
apiCache.save(apiInfoDTO);
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
package com.gitee.sop.index.exception;
|
||||
|
||||
|
||||
import com.gitee.sop.index.message.ErrorFactory;
|
||||
import com.gitee.sop.index.message.ErrorMeta;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.message.IError;
|
||||
|
||||
import java.util.Locale;
|
||||
@@ -13,33 +12,36 @@ import java.util.Locale;
|
||||
public class ApiException extends RuntimeException {
|
||||
private static final long serialVersionUID = 8278005515613227643L;
|
||||
|
||||
private transient IError error;
|
||||
private final transient Locale locale;
|
||||
|
||||
private transient ErrorMeta errorMeta;
|
||||
private transient Object[] params;
|
||||
private final transient ErrorEnum errorEnum;
|
||||
private final transient Object[] params;
|
||||
|
||||
public ApiException(ErrorMeta errorMeta, Object... params) {
|
||||
this.errorMeta = errorMeta;
|
||||
public ApiException(ErrorEnum errorEnum, Locale locale, Object... params) {
|
||||
this.errorEnum = errorEnum;
|
||||
this.params = params;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public ApiException(Throwable cause, ErrorMeta errorMeta, Object... params) {
|
||||
public ApiException(Throwable cause, ErrorEnum errorEnum, Locale locale, Object... params) {
|
||||
super(cause);
|
||||
this.errorMeta = errorMeta;
|
||||
this.errorEnum = errorEnum;
|
||||
this.params = params;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public IError getError(Locale locale) {
|
||||
if (error == null) {
|
||||
error = ErrorFactory.getError(this.errorMeta, locale, params);
|
||||
}
|
||||
return error;
|
||||
public IError getError() {
|
||||
return errorEnum.getErrorMeta().getError(locale, params);
|
||||
}
|
||||
|
||||
public ErrorEnum getErrorEnum() {
|
||||
return errorEnum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = super.getMessage();
|
||||
return message == null ? errorMeta.toString() : message;
|
||||
return message == null ? errorEnum.getErrorMeta().toString() : message;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,77 @@
|
||||
package com.gitee.sop.index.exception;
|
||||
|
||||
import com.gitee.sop.index.common.ApiResponse;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.ValidationException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class ControllerGlobalExceptionHandler {
|
||||
|
||||
// 未知异常
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public ApiResponse globalExceptionHandler(Exception e, HttpServletRequest request) {
|
||||
log.error("系统出错", e);
|
||||
return ApiResponse.error(ErrorEnum.ISP_UNKNOWN_ERROR, request.getLocale());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理jsr303的字段校验异常,也可以自定义注解校验
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ApiResponse handleValidationExceptions(
|
||||
MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
Map<String, String> errors = new HashMap<>();
|
||||
ex.getBindingResult().getAllErrors().forEach((error) -> {
|
||||
String fieldName = ((FieldError) error).getField();
|
||||
String errorMessage = error.getDefaultMessage();
|
||||
errors.put(fieldName, errorMessage);
|
||||
});
|
||||
|
||||
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, request.getLocale(), String.join(",", errors.values()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证异常
|
||||
*
|
||||
* @param e e
|
||||
*/
|
||||
@ExceptionHandler(ValidationException.class)
|
||||
public ApiResponse validationException(ValidationException e, HttpServletRequest request) {
|
||||
List<String> msgList = new ArrayList<>();
|
||||
for (ConstraintViolation<?> constraintViolation : ((ConstraintViolationException) e).getConstraintViolations()) {
|
||||
msgList.add(constraintViolation.getMessage());
|
||||
}
|
||||
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, request.getLocale(), String.join(",", msgList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 参数绑定异常,GET请求参数校验
|
||||
*
|
||||
* @param e e
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public ApiResponse bandException(BindException e, HttpServletRequest request) {
|
||||
List<FieldError> fieldErrors = e.getFieldErrors();
|
||||
List<String> msgList = new ArrayList<>();
|
||||
for (FieldError fieldError : fieldErrors) {
|
||||
msgList.add(fieldError.getDefaultMessage());
|
||||
}
|
||||
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, request.getLocale(), String.join(",", msgList));
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.index.exception;
|
||||
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ApiResponse;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface ExceptionExecutor {
|
||||
|
||||
ApiResponse executeException(ApiRequestContext apiRequestContext, Exception e);
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.gitee.sop.index.exception;
|
||||
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Getter
|
||||
public class SignException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 4049365045207852768L;
|
||||
|
||||
public SignException(ErrorEnum errorEnum) {
|
||||
this.errorEnum = errorEnum;
|
||||
}
|
||||
|
||||
public SignException(ErrorEnum errorEnum, Throwable cause) {
|
||||
super(cause);
|
||||
this.errorEnum = errorEnum;
|
||||
}
|
||||
|
||||
private final ErrorEnum errorEnum;
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.gitee.sop.index.exception.impl;
|
||||
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ApiResponse;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.exception.ExceptionExecutor;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import org.apache.dubbo.rpc.service.GenericException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class ExceptionExecutorImpl implements ExceptionExecutor {
|
||||
|
||||
private static final String CONSTRAINT_VIOLATION_EXCEPTION = "ConstraintViolationException";
|
||||
|
||||
@Override
|
||||
public ApiResponse executeException(ApiRequestContext apiRequestContext, Exception e) {
|
||||
if (e instanceof GenericException) {
|
||||
GenericException genericException = (GenericException) e;
|
||||
String exceptionClass = genericException.getExceptionClass();
|
||||
if (exceptionClass.contains(CONSTRAINT_VIOLATION_EXCEPTION)) {
|
||||
String exceptionMessage = genericException.getExceptionMessage();
|
||||
// 参数校验:Failed to validate service: com.gitee.sop.storyweb.open.StoryService, method: save, cause: [ConstraintViolationImpl{interpolatedMessage='故事名称必填', propertyPath=storyName, rootBeanClass=class com.gitee.sop.storyweb.open.req.StorySaveDTO, messageTemplate='故事名称必填'}]
|
||||
Set<String> msgs = findErrorMsg(exceptionMessage);
|
||||
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, apiRequestContext.getLocale(), String.join(",", msgs));
|
||||
}
|
||||
} else if (e instanceof ApiException) {
|
||||
return ApiResponse.error(((ApiException) e));
|
||||
}
|
||||
return ApiResponse.error(ErrorEnum.ISP_SERVICE_UNKNOWN_ERROR, apiRequestContext.getLocale());
|
||||
}
|
||||
|
||||
private static Set<String> findErrorMsg(String text) {
|
||||
Pattern pattern = Pattern.compile("'([^']*)'");
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
Set<String> msgList = new LinkedHashSet<>();
|
||||
while (matcher.find()) {
|
||||
msgList.add(matcher.group(1));
|
||||
}
|
||||
return msgList;
|
||||
}
|
||||
}
|
@@ -109,7 +109,7 @@ public enum ErrorEnum {
|
||||
ISV_IP_FORBIDDEN(Codes.CODE_ISV_PERM, "isv.ip-forbidden"),
|
||||
|
||||
;
|
||||
private ErrorMeta errorMeta;
|
||||
private final ErrorMeta errorMeta;
|
||||
|
||||
ErrorEnum(String code, String subCode) {
|
||||
this.errorMeta = new ErrorMeta("open.error_", code, subCode);
|
||||
|
@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -48,7 +49,7 @@ public class ErrorFactory {
|
||||
HashSet<String> baseNamesSet = new HashSet<>();
|
||||
baseNamesSet.add(I18N_OPEN_ERROR);
|
||||
|
||||
if (!isvModules.isEmpty()) {
|
||||
if (!CollectionUtils.isEmpty(isvModules)) {
|
||||
baseNamesSet.addAll(isvModules);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.gitee.sop.index.message;
|
||||
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Locale;
|
||||
@@ -23,25 +22,10 @@ public class ErrorMeta {
|
||||
this.subCode = subCode;
|
||||
}
|
||||
|
||||
public IError getError(Locale locale) {
|
||||
return ErrorFactory.getError(this, locale);
|
||||
public IError getError(Locale locale, Object... params) {
|
||||
return ErrorFactory.getError(this, locale, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回网关exception
|
||||
*
|
||||
* @param params 参数
|
||||
* @return 返回exception
|
||||
*/
|
||||
public ApiException getException(Object... params) {
|
||||
if (params != null && params.length == 1) {
|
||||
Object param = params[0];
|
||||
if (param instanceof Throwable) {
|
||||
return new ApiException((Throwable) param, this);
|
||||
}
|
||||
}
|
||||
return new ApiException(this, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@@ -1,23 +1,33 @@
|
||||
package com.gitee.sop.index.service;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class ApiService {
|
||||
|
||||
@Autowired
|
||||
@Resource
|
||||
private ApiCacheManager apiCacheManager;
|
||||
|
||||
@Resource
|
||||
private ApiInfoMapper apiInfoMapper;
|
||||
|
||||
public ApiInfoDTO getApi(String apiName, String apiVersion) {
|
||||
return apiCacheManager.getOrElse(apiName, apiVersion, () -> {
|
||||
ApiInfoDTO record = new ApiInfoDTO();
|
||||
|
||||
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiName, apiVersion);
|
||||
if (apiInfo != null) {
|
||||
BeanUtils.copyProperties(apiInfo, record);
|
||||
}
|
||||
return record;
|
||||
});
|
||||
}
|
||||
|
@@ -15,11 +15,15 @@ import org.springframework.stereotype.Service;
|
||||
*/
|
||||
@Service
|
||||
public class GenericServiceInvoker implements InitializingBean {
|
||||
private static final String TRUE = "true";
|
||||
private ApplicationConfig applicationConfig;
|
||||
|
||||
@Value("${nacos.host:localhost:8848}")
|
||||
private String nacosHost;
|
||||
|
||||
@Value("${register.address:${register.type}://${register.host}")
|
||||
private String registerAddress;
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String appName;
|
||||
|
||||
@@ -36,13 +40,13 @@ public class GenericServiceInvoker implements InitializingBean {
|
||||
|
||||
private RegistryConfig buildRegistryConfig(String nacosHost) {
|
||||
RegistryConfig config = new RegistryConfig();
|
||||
config.setAddress("nacos://" + nacosHost);
|
||||
config.setAddress(registerAddress);
|
||||
return config;
|
||||
}
|
||||
|
||||
public Object invoke(String interfaceName, String method, String[] parameterTypes, Object[] params) {
|
||||
ReferenceConfig<GenericService> reference = new ReferenceConfig<>();
|
||||
reference.setGeneric("true");
|
||||
reference.setGeneric(TRUE);
|
||||
reference.setApplication(applicationConfig);
|
||||
reference.setInterface(interfaceName);
|
||||
reference.setTimeout(timeout);
|
||||
|
@@ -3,22 +3,19 @@ package com.gitee.sop.index.service;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.controller.param.ApiResponse;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.common.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ApiResponse;
|
||||
import com.gitee.sop.index.exception.ExceptionExecutor;
|
||||
import com.gitee.sop.index.service.validate.Validator;
|
||||
import com.gitee.sop.index.util.ClassUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.rpc.service.GenericException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 接口路由
|
||||
@@ -29,94 +26,66 @@ import java.util.regex.Pattern;
|
||||
@Slf4j
|
||||
public class RouteService {
|
||||
|
||||
private static final String CONSTRAINT_VIOLATION_EXCEPTION = "ConstraintViolationException";
|
||||
|
||||
@Autowired
|
||||
@Resource
|
||||
private Validator validator;
|
||||
|
||||
@Autowired
|
||||
@Resource
|
||||
private GenericServiceInvoker genericServiceInvoker;
|
||||
|
||||
@Autowired
|
||||
private ApiService apiService;
|
||||
@Resource
|
||||
private ExceptionExecutor exceptionExecutor;
|
||||
|
||||
public ApiResponse route(ApiRequest apiRequest) {
|
||||
// 校验
|
||||
validator.validate(apiRequest);
|
||||
return doRoute(apiRequest);
|
||||
public ApiResponse route(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
log.info("收到客户端请求, ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest);
|
||||
try {
|
||||
// 接口校验
|
||||
ApiInfoDTO apiInfoDTO = validator.validate(apiRequestContext);
|
||||
return doRoute(apiRequestContext, apiInfoDTO);
|
||||
} catch (Exception e) {
|
||||
log.error("接口请求报错, , ip={}, apiRequest={}", apiRequestContext.getIp(), apiRequest, e);
|
||||
return exceptionExecutor.executeException(apiRequestContext, e);
|
||||
}
|
||||
}
|
||||
|
||||
private ApiResponse doRoute(ApiRequest apiRequest) {
|
||||
ApiInfoDTO apiInfo = apiService.getApi(apiRequest.getMethod(), apiRequest.getVersion());
|
||||
Object result = null;
|
||||
Exception error = null;
|
||||
|
||||
beforeRoute(apiRequest, apiInfo);
|
||||
try {
|
||||
result = genericServiceInvoker.invoke(
|
||||
apiInfo.getInterfaceClassName(),
|
||||
apiInfo.getMethodName(),
|
||||
new String[]{apiInfo.getParamTypeName()},
|
||||
new Object[]{buildInvokeParam(apiRequest, apiInfo)}
|
||||
);
|
||||
} catch (Exception e) {
|
||||
error = e;
|
||||
log.error("请求服务出错, apiRequest={}, apiInfo={}", apiInfo, apiInfo, e);
|
||||
return buildApiResponse(e);
|
||||
} finally {
|
||||
afterRoute(result, apiRequest, error);
|
||||
}
|
||||
private ApiResponse doRoute(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfo) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
Object result = genericServiceInvoker.invoke(
|
||||
apiInfo.getInterfaceClassName(),
|
||||
apiInfo.getMethodName(),
|
||||
buildParamType(apiInfo),
|
||||
buildInvokeParam(apiRequest, apiInfo)
|
||||
);
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
private ApiResponse buildApiResponse(Exception e) {
|
||||
if (e instanceof GenericException) {
|
||||
GenericException genericException = (GenericException) e;
|
||||
String exceptionClass = genericException.getExceptionClass();
|
||||
if (exceptionClass.contains(CONSTRAINT_VIOLATION_EXCEPTION)) {
|
||||
String exceptionMessage = genericException.getExceptionMessage();
|
||||
// 参数校验:Failed to validate service: com.gitee.sop.storyweb.open.StoryService, method: save, cause: [ConstraintViolationImpl{interpolatedMessage='故事名称必填', propertyPath=storyName, rootBeanClass=class com.gitee.sop.storyweb.open.req.StorySaveDTO, messageTemplate='故事名称必填'}]
|
||||
Set<String> msgs = findErrorMsg(exceptionMessage);
|
||||
return ApiResponse.error(ErrorEnum.ISV_ERROR_PARAMETER, String.join(",", msgs));
|
||||
}
|
||||
private String[] buildParamType(ApiInfoDTO apiInfo) {
|
||||
String paramTypeName = apiInfo.getParamTypeName();
|
||||
if (StringUtils.hasText(paramTypeName)) {
|
||||
return new String[]{apiInfo.getParamTypeName()};
|
||||
} else {
|
||||
return new String[0];
|
||||
}
|
||||
return ApiResponse.error(ErrorEnum.ISP_SERVICE_UNKNOWN_ERROR);
|
||||
}
|
||||
|
||||
private Set<String> findErrorMsg(String text) {
|
||||
Pattern pattern = Pattern.compile("'([^']*)'");
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
Set<String> msgList = new LinkedHashSet<>();
|
||||
while (matcher.find()) {
|
||||
msgList.add(matcher.group(1));
|
||||
}
|
||||
return msgList;
|
||||
}
|
||||
|
||||
private Object buildInvokeParam(ApiRequest apiRequest, ApiInfoDTO apiInfo) {
|
||||
private Object[] buildInvokeParam(ApiRequest apiRequest, ApiInfoDTO apiInfo) {
|
||||
if (ObjectUtils.isEmpty(apiInfo.getParamTypeName())) {
|
||||
return null;
|
||||
return new Object[0];
|
||||
}
|
||||
String bizContent = apiRequest.getBiz_content();
|
||||
JSONObject jsonObject = JSON.parseObject(bizContent);
|
||||
if (ClassUtil.isPrimitive(apiInfo.getParamTypeName())) {
|
||||
try {
|
||||
return jsonObject.getObject(apiInfo.getParamName(), ClassUtils.forName(apiInfo.getParamTypeName()));
|
||||
Object value = jsonObject.getObject(apiInfo.getParamName(), ClassUtils.forName(apiInfo.getParamTypeName()));
|
||||
return new Object[]{value};
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("找不到参数class, paramTypeName={}, apiRequest={}, apiInfo={}",
|
||||
apiInfo.getParamTypeName(), apiRequest, apiInfo, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
return jsonObject;
|
||||
return new Object[]{jsonObject};
|
||||
}
|
||||
}
|
||||
|
||||
private void beforeRoute(ApiRequest apiRequest, ApiInfoDTO apiInfo) {
|
||||
log.info("收到接口请求, apiRequest={}", apiRequest);
|
||||
}
|
||||
|
||||
private void afterRoute(Object result, ApiRequest apiRequest, Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
package com.gitee.sop.index.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class ValidateService {
|
||||
|
||||
public void validate(ApiRequest apiRequest) {
|
||||
String jsonString = JSON.toJSONString(apiRequest);
|
||||
JSONObject jsonObject = JSON.parseObject(jsonString);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.gitee.sop.index.service.dubbo;
|
||||
|
||||
import com.gitee.sop.index.api.ApiRegisterService;
|
||||
import com.gitee.sop.index.api.RegisterDTO;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.common.StatusEnum;
|
||||
import com.gitee.sop.index.dao.entity.ApiInfo;
|
||||
import com.gitee.sop.index.dao.mapper.ApiInfoMapper;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Slf4j
|
||||
@DubboService
|
||||
public class ApiRegisterServiceImpl implements ApiRegisterService {
|
||||
|
||||
@Resource
|
||||
private ApiCacheManager apiCacheManager;
|
||||
|
||||
@Resource
|
||||
private ApiInfoMapper apiInfoMapper;
|
||||
|
||||
@Override
|
||||
public void register(RegisterDTO registerDTO) {
|
||||
log.info("收到接口注册, registerDTO={}", registerDTO);
|
||||
ApiInfoDTO apiInfoDTO = new ApiInfoDTO();
|
||||
BeanUtils.copyProperties(registerDTO, apiInfoDTO);
|
||||
|
||||
ApiInfo apiInfo = apiInfoMapper.getByNameVersion(apiInfoDTO.getApiName(), apiInfoDTO.getApiVersion());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (apiInfo == null) {
|
||||
apiInfo = new ApiInfo();
|
||||
apiInfo.setApiStatus(StatusEnum.ENABLE.getValue());
|
||||
apiInfo.setAddTime(now);
|
||||
}
|
||||
apiInfo.setUpdateTime(now);
|
||||
BeanUtils.copyProperties(apiInfoDTO, apiInfo);
|
||||
// 保存到数据库
|
||||
apiInfoMapper.saveOrUpdate(apiInfo);
|
||||
// 保存到缓存
|
||||
apiCacheManager.save(apiInfoDTO);
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.gitee.sop.index.service.dubbo;
|
||||
|
||||
import com.gitee.sop.index.api.IsvService;
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@DubboService
|
||||
@Slf4j
|
||||
public class IsvServiceImpl implements IsvService {
|
||||
|
||||
private IsvManager isvManager;
|
||||
|
||||
@Override
|
||||
public void refresh(String appId) {
|
||||
log.info("refresh Isv, appId={}", appId);
|
||||
// 刷新isv信息
|
||||
isvManager.reload(appId);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.index.service.manager;
|
||||
|
||||
/**
|
||||
* IP黑名单管理
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IpBlacklistManager {
|
||||
|
||||
boolean contains(String ip);
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.index.service.manager;
|
||||
|
||||
/**
|
||||
* isv接口授权管理
|
||||
*
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IsvApiPermissionManager {
|
||||
|
||||
boolean hasPermission(String appId, String apiNameVersion);
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.gitee.sop.index.service.manager;
|
||||
|
||||
import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public interface IsvManager {
|
||||
|
||||
IsvDTO getIsv(String appId);
|
||||
|
||||
void reload(String appId);
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.gitee.sop.index.service.manager.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class IsvDTO {
|
||||
|
||||
private String appId;
|
||||
|
||||
private String publicKey;
|
||||
|
||||
private String privateKeyPlatform;
|
||||
|
||||
private Integer status;
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.IpBlacklistManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class IpBlacklistManagerImpl implements IpBlacklistManager {
|
||||
@Override
|
||||
public boolean contains(String ip) {
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.IsvApiPermissionManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Service
|
||||
public class IsvApiPermissionManagerImpl implements IsvApiPermissionManager {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String appId, String apiNameVersion) {
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class LocalIsvManagerImpl implements IsvManager {
|
||||
|
||||
private static final Map<String, IsvDTO> ISV_MAP = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public IsvDTO getIsv(String appId) {
|
||||
return ISV_MAP.get(appId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(String appId) {
|
||||
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.gitee.sop.index.service.manager.impl;
|
||||
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class RedisIsvManagerImpl implements IsvManager {
|
||||
|
||||
@Override
|
||||
public IsvDTO getIsv(String appId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(String appId) {
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
@@ -14,19 +16,20 @@ public abstract class AbstractSigner implements Signer {
|
||||
/**
|
||||
* 构建服务端签名串
|
||||
*
|
||||
* @param params 接口参数
|
||||
* @param apiRequestContext 接口参数
|
||||
* @param secret 秘钥
|
||||
* @return 返回服务端签名串
|
||||
*/
|
||||
protected abstract String buildServerSign(ApiRequest params, String secret);
|
||||
protected abstract String buildServerSign(ApiRequestContext apiRequestContext, String secret);
|
||||
|
||||
@Override
|
||||
public boolean checkSign(ApiRequest apiRequest, String secret) {
|
||||
public boolean checkSign(ApiRequestContext apiRequestContext, String publicKey) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String clientSign = apiRequest.getSign();
|
||||
if (StringUtils.isBlank(clientSign)) {
|
||||
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException();
|
||||
throw new ApiException(ErrorEnum.ISV_MISSING_SIGNATURE, apiRequestContext.getLocale());
|
||||
}
|
||||
String serverSign = buildServerSign(apiRequest, secret);
|
||||
String serverSign = buildServerSign(apiRequestContext, publicKey);
|
||||
return clientSign.equals(serverSign);
|
||||
}
|
||||
|
||||
|
@@ -1,18 +1,25 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.common.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ParamNames;
|
||||
import com.gitee.sop.index.config.ApiConfig;
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.service.manager.ApiCacheManager;
|
||||
import com.gitee.sop.index.service.manager.SecretManager;
|
||||
import com.gitee.sop.index.service.ApiService;
|
||||
import com.gitee.sop.index.service.manager.IpBlacklistManager;
|
||||
import com.gitee.sop.index.service.manager.IsvApiPermissionManager;
|
||||
import com.gitee.sop.index.service.manager.IsvManager;
|
||||
import com.gitee.sop.index.service.manager.dto.IsvDTO;
|
||||
import com.gitee.sop.index.service.validate.alipay.AlipaySigner;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@@ -21,6 +28,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 负责校验,校验工作都在这里
|
||||
@@ -28,7 +36,7 @@ import java.util.List;
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ApiValidator implements Validator {
|
||||
|
||||
@@ -37,18 +45,6 @@ public class ApiValidator implements Validator {
|
||||
|
||||
private static List<String> FORMAT_LIST = Arrays.asList("json", "xml");
|
||||
|
||||
// @Autowired
|
||||
// private IsvManager isvManager;
|
||||
//
|
||||
// @Autowired
|
||||
// private IsvRoutePermissionManager isvRoutePermissionManager;
|
||||
//
|
||||
// @Autowired
|
||||
// private IPBlacklistManager ipBlacklistManager;
|
||||
//
|
||||
// @Autowired
|
||||
// private RouteConfigManager routeConfigManager;
|
||||
|
||||
private final Signer signer = new AlipaySigner();
|
||||
|
||||
/**
|
||||
@@ -57,137 +53,114 @@ public class ApiValidator implements Validator {
|
||||
@Value("${upload.max-file-size:${spring.servlet.multipart.max-file-size:10MB}}")
|
||||
private String maxFileSize;
|
||||
|
||||
@Autowired
|
||||
private ApiConfig apiConfig;
|
||||
private final ApiConfig apiConfig;
|
||||
|
||||
@Autowired
|
||||
private ApiCacheManager apiCacheManager;
|
||||
private ApiService apiService;
|
||||
|
||||
@Autowired
|
||||
private SecretManager secretManager;
|
||||
private IpBlacklistManager ipBlacklistManager;
|
||||
|
||||
@Autowired
|
||||
private IsvApiPermissionManager isvApiPermissionManager;
|
||||
|
||||
@Autowired
|
||||
private IsvManager isvManager;
|
||||
|
||||
|
||||
@Override
|
||||
public void validate(ApiRequest param) {
|
||||
// checkIP(param);
|
||||
// TargetRoute targetRoute = checkEnable(param);
|
||||
// initFields(targetRoute, param);
|
||||
// ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
// checkAppKey(param);
|
||||
// if (apiConfig.isIgnoreValidate()
|
||||
// || BooleanUtils.toBoolean(targetRoute.getRouteDefinition().getIgnoreValidate())) {
|
||||
// if (log.isDebugEnabled()) {
|
||||
// log.debug("忽略签名校验, name:{}, version:{}", param.fetchName(), param.fetchVersion());
|
||||
// }
|
||||
// } else {
|
||||
// checkSign(param);
|
||||
// }
|
||||
checkSign(param);
|
||||
checkTimeout(param);
|
||||
checkFormat(param);
|
||||
// checkUploadFile(param);
|
||||
// checkPermission(param);
|
||||
// checkToken(param);
|
||||
public ApiInfoDTO validate(ApiRequestContext apiRequestContext) {
|
||||
// 校验字段完整性
|
||||
checkField(apiRequestContext);
|
||||
// 检查isv
|
||||
IsvDTO isvDTO = checkIsv(apiRequestContext);
|
||||
// 校验签名
|
||||
checkSign(apiRequestContext, isvDTO);
|
||||
// 检查是否超时
|
||||
checkTimeout(apiRequestContext);
|
||||
// 检查格式化
|
||||
checkFormat(apiRequestContext);
|
||||
// IP能否访问
|
||||
checkIP(apiRequestContext);
|
||||
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
ApiInfoDTO apiInfo = apiService.getApi(apiRequest.getMethod(), apiRequest.getVersion());
|
||||
// 检查接口信息
|
||||
checkApiInfo(apiRequestContext, apiInfo);
|
||||
|
||||
// 检查上传文件
|
||||
checkUploadFile(apiRequestContext);
|
||||
// 检查token
|
||||
checkToken(apiRequestContext);
|
||||
return apiInfo;
|
||||
}
|
||||
|
||||
public void checkApiInfo(ApiRequestContext apiRequestContext, ApiInfoDTO apiInfoDTO) {
|
||||
// 检查路由是否存在
|
||||
if (apiInfoDTO == null) {
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_METHOD, apiRequestContext.getLocale());
|
||||
}
|
||||
// 检查路由是否启用
|
||||
if (!BooleanUtils.toBoolean(apiInfoDTO.getStatus())) {
|
||||
throw new ApiException(ErrorEnum.ISP_API_DISABLED, apiRequestContext.getLocale());
|
||||
}
|
||||
// 校验是否需要授权访问
|
||||
boolean needCheckPermission = BooleanUtils.toBoolean(apiInfoDTO.getIsPermission());
|
||||
if (needCheckPermission) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String appKey = apiRequest.getApp_id();
|
||||
String nameVersion = apiRequest.takeNameVersion();
|
||||
boolean hasPermission = isvApiPermissionManager.hasPermission(appKey, nameVersion);
|
||||
if (!hasPermission) {
|
||||
throw new ApiException(ErrorEnum.ISV_ROUTE_NO_PERMISSIONS, apiRequestContext.getLocale());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkField(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
Locale locale = apiRequestContext.getLocale();
|
||||
if (ObjectUtils.isEmpty(apiRequest.getApp_id())) {
|
||||
throw new ApiException(ErrorEnum.ISV_MISSING_APP_ID, locale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在IP黑名单中
|
||||
*
|
||||
* @param param 接口参数
|
||||
* @param apiRequestContext 接口参数
|
||||
*/
|
||||
// protected void checkIP(ApiRequest param) {
|
||||
// String ip = param.fetchIp();
|
||||
// if (ipBlacklistManager.contains(ip)) {
|
||||
// throw ErrorEnum.ISV_IP_FORBIDDEN.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
protected void checkIP(ApiRequestContext apiRequestContext) {
|
||||
String ip = apiRequestContext.getIp();
|
||||
if (ipBlacklistManager.contains(ip)) {
|
||||
throw new ApiException(ErrorEnum.ISV_IP_FORBIDDEN, apiRequestContext.getLocale());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测能否访问
|
||||
*
|
||||
* @param param 接口参数
|
||||
*/
|
||||
// protected TargetRoute checkEnable(ApiRequest param) {
|
||||
// String name = param.getMethod();
|
||||
// if (name == null) {
|
||||
// throw ErrorEnum.ISV_MISSING_METHOD.getErrorMeta().getException();
|
||||
// }
|
||||
// String version = param.fetchVersion();
|
||||
// if (version == null) {
|
||||
// throw ErrorEnum.ISV_MISSING_VERSION.getErrorMeta().getException();
|
||||
// }
|
||||
// String routeId = param.takeApiName();
|
||||
// // 检查路由是否存在
|
||||
// TargetRoute targetRoute = RouteRepositoryContext.getTargetRoute(routeId);
|
||||
// if (targetRoute == null) {
|
||||
// throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
|
||||
// }
|
||||
// // 检查路由是否启用
|
||||
// RouteConfig routeConfig = routeConfigManager.get(routeId);
|
||||
// if (!routeConfig.enable()) {
|
||||
// throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException();
|
||||
// }
|
||||
// return targetRoute;
|
||||
// }
|
||||
|
||||
// private void initFields(TargetRoute targetRoute, ApiRequest apiParam) {
|
||||
// apiParam.setServiceId(targetRoute.getServiceDefinition().getServiceId());
|
||||
// boolean mergeResult;
|
||||
// Boolean defaultSetting = ApiContext.getApiConfig().getMergeResult();
|
||||
// if (defaultSetting != null) {
|
||||
// mergeResult = defaultSetting;
|
||||
// } else {
|
||||
// RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
|
||||
// mergeResult = routeDefinition == null || BooleanUtils.toBoolean(routeDefinition.getMergeResult());
|
||||
// }
|
||||
// apiParam.setMergeResult(mergeResult);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 校验上传文件内容
|
||||
*
|
||||
* @param param
|
||||
* @param apiRequestContext apiRequestContext
|
||||
*/
|
||||
// protected void checkUploadFile(ApiRequest param) {
|
||||
// UploadContext uploadContext = param.fetchUploadContext();
|
||||
// if (uploadContext != null) {
|
||||
// try {
|
||||
// List<MultipartFile> files = uploadContext.getAllFile();
|
||||
// for (MultipartFile file : files) {
|
||||
// checkSingleFileSize(file);
|
||||
// checkFileMd5(param, file);
|
||||
// }
|
||||
// } catch (IOException e) {
|
||||
// log.error("验证上传文件MD5错误", e);
|
||||
// throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void checkFileMd5(ApiRequest param, MultipartFile file) throws IOException {
|
||||
// // 客户端传来的文件md5
|
||||
// String clientMd5 = param.getString(file.getName());
|
||||
// if (clientMd5 != null) {
|
||||
// String fileMd5 = DigestUtils.md5Hex(file.getBytes());
|
||||
// if (!clientMd5.equals(fileMd5)) {
|
||||
// throw ErrorEnum.ISV_UPLOAD_FAIL.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
protected void checkUploadFile(ApiRequestContext apiRequestContext) {
|
||||
// todo:校验上传文件内容
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单个文件大小
|
||||
*
|
||||
* @param file 文件
|
||||
*/
|
||||
private void checkSingleFileSize(MultipartFile file) {
|
||||
private void checkSingleFileSize(ApiRequestContext apiRequestContext, MultipartFile file) {
|
||||
long fileSize = file.getSize();
|
||||
if (fileSize > DataSize.parse(maxFileSize).toBytes()) {
|
||||
throw ErrorEnum.ISV_INVALID_FILE_SIZE.getErrorMeta().getException(file.getName(), maxFileSize);
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_FILE_SIZE, apiRequestContext.getLocale(), fileSize, maxFileSize);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkTimeout(ApiRequest param) {
|
||||
protected void checkTimeout(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
int timeoutSeconds = apiConfig.getTimeoutSeconds();
|
||||
// 如果设置为0,表示不校验
|
||||
if (timeoutSeconds == 0) {
|
||||
@@ -196,103 +169,71 @@ public class ApiValidator implements Validator {
|
||||
if (timeoutSeconds < 0) {
|
||||
throw new IllegalArgumentException("服务端timeoutSeconds设置错误");
|
||||
}
|
||||
String requestTime = param.getTimestamp();
|
||||
String requestTime = apiRequest.getTimestamp();
|
||||
try {
|
||||
Date requestDate = new SimpleDateFormat(ParamNames.TIMESTAMP_PATTERN).parse(requestTime);
|
||||
long requestMilliseconds = requestDate.getTime();
|
||||
if (System.currentTimeMillis() - requestMilliseconds > timeoutSeconds * MILLISECOND_OF_ONE_SECOND) {
|
||||
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException();
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_TIMESTAMP, apiRequestContext.getLocale());
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
throw ErrorEnum.ISV_INVALID_TIMESTAMP.getErrorMeta().getException(param.takeNameVersion());
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_TIMESTAMP, apiRequestContext.getLocale(), apiRequest.takeNameVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// protected void checkAppKey(ApiRequest param) {
|
||||
// if (StringUtils.isEmpty(param.fetchAppKey())) {
|
||||
// throw ErrorEnum.ISV_MISSING_APP_ID.getErrorMeta().getException();
|
||||
// }
|
||||
// Isv isv = isvManager.getIsv(param.fetchAppKey());
|
||||
// // 没有用户
|
||||
// if (isv == null) {
|
||||
// throw ErrorEnum.ISV_INVALID_APP_ID.getErrorMeta().getException();
|
||||
// }
|
||||
// // 禁止访问
|
||||
// if (isv.getStatus() == null || isv.getStatus() == STATUS_FORBIDDEN) {
|
||||
// throw ErrorEnum.ISV_ACCESS_FORBIDDEN.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
protected IsvDTO checkIsv(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
IsvDTO isv = isvManager.getIsv(apiRequest.getApp_id());
|
||||
// 没有用户
|
||||
if (isv == null) {
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_APP_ID, apiRequestContext.getLocale());
|
||||
}
|
||||
// 禁止访问
|
||||
if (isv.getStatus() == null || isv.getStatus() == STATUS_FORBIDDEN) {
|
||||
throw new ApiException(ErrorEnum.ISV_ACCESS_FORBIDDEN, apiRequestContext.getLocale());
|
||||
}
|
||||
return isv;
|
||||
}
|
||||
|
||||
protected void checkSign(ApiRequest param) {
|
||||
String clientSign = param.getSign();
|
||||
try {
|
||||
if (StringUtils.isEmpty(clientSign)) {
|
||||
throw ErrorEnum.ISV_MISSING_SIGNATURE.getErrorMeta().getException(param.takeNameVersion(), ParamNames.SIGN_NAME);
|
||||
}
|
||||
// ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
// // 根据appId获取秘钥
|
||||
// Isv isvInfo = isvManager.getIsv(param.fetchAppKey());
|
||||
// String secret = isvInfo.getSecretInfo();
|
||||
String secret = secretManager.getIsvPublicKey(param.getApp_id());
|
||||
// if (StringUtils.isEmpty(secret)) {
|
||||
// throw ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG.getErrorMeta().getException();
|
||||
// }
|
||||
// 错误的sign
|
||||
if (!signer.checkSign(param, secret)) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(param.takeNameVersion());
|
||||
}
|
||||
} finally {
|
||||
// 校验过程中会移除sign,这里需要重新设置进去
|
||||
param.setSign(clientSign);
|
||||
protected void checkSign(ApiRequestContext apiRequestContext, IsvDTO isv) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String clientSign = apiRequest.getSign();
|
||||
if (ObjectUtils.isEmpty(clientSign)) {
|
||||
throw new ApiException(ErrorEnum.ISV_MISSING_SIGNATURE, apiRequestContext.getLocale(),
|
||||
apiRequest.takeNameVersion(), ParamNames.SIGN_NAME);
|
||||
}
|
||||
// ISV上传的公钥
|
||||
String publicKey = isv.getPublicKey();
|
||||
if (ObjectUtils.isEmpty(publicKey)) {
|
||||
throw new ApiException(ErrorEnum.ISV_MISSING_SIGNATURE_CONFIG, apiRequestContext.getLocale(),
|
||||
apiRequest.takeNameVersion());
|
||||
}
|
||||
// 错误的sign
|
||||
if (!signer.checkSign(apiRequestContext, publicKey)) {
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_SIGNATURE, apiRequestContext.getLocale(),
|
||||
apiRequest.takeNameVersion());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void checkFormat(ApiRequest param) {
|
||||
String format = param.getFormat();
|
||||
protected void checkFormat(ApiRequestContext apiRequestContext) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
String format = apiRequest.getFormat();
|
||||
boolean contains = FORMAT_LIST.contains(format.toLowerCase());
|
||||
|
||||
if (!contains) {
|
||||
throw ErrorEnum.ISV_INVALID_FORMAT.getErrorMeta().getException(param.takeNameVersion(), format);
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_FORMAT, apiRequestContext.getLocale(),
|
||||
apiRequest.takeNameVersion(), format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验访问权限
|
||||
*
|
||||
* @param apiParam 参数
|
||||
*/
|
||||
// protected void checkPermission(ApiRequest apiParam) {
|
||||
// String routeId = apiParam.takeApiName();
|
||||
// TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId);
|
||||
// RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
|
||||
// boolean needCheckPermission = BooleanUtils.toBoolean(routeDefinition.getPermission());
|
||||
// if (needCheckPermission) {
|
||||
// String appKey = apiParam.fetchAppKey();
|
||||
// boolean hasPermission = isvRoutePermissionManager.hasPermission(appKey, routeId);
|
||||
// if (!hasPermission) {
|
||||
// throw ErrorEnum.ISV_ROUTE_NO_PERMISSIONS.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 校验token
|
||||
*
|
||||
* @param apiParam 参数
|
||||
* @param apiRequestContext 参数
|
||||
*/
|
||||
// protected void checkToken(ApiRequest apiParam) {
|
||||
// String routeId = apiParam.takeApiName();
|
||||
// TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId);
|
||||
// RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
|
||||
// boolean needToken = BooleanUtils.toBoolean(routeDefinition.getNeedToken());
|
||||
// if (needToken) {
|
||||
// TokenValidator tokenValidator = ApiConfig.getInstance().getTokenValidator();
|
||||
// boolean rightToken = tokenValidator.validateToken(apiParam);
|
||||
// if (!rightToken) {
|
||||
// throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
protected void checkToken(ApiRequestContext apiRequestContext) {
|
||||
// todo:校验token
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,20 +1,21 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
|
||||
/**
|
||||
* 负责签名校验
|
||||
* @author tanghc
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface Signer {
|
||||
|
||||
/**
|
||||
* 签名校验
|
||||
* @param apiRequest 参数
|
||||
* @param secret 秘钥
|
||||
*
|
||||
* @param apiRequestContext 参数
|
||||
* @param publicKey 公钥
|
||||
* @return true签名正确
|
||||
*/
|
||||
boolean checkSign(ApiRequest apiRequest, String secret);
|
||||
boolean checkSign(ApiRequestContext apiRequestContext, String publicKey);
|
||||
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TokenValidator {
|
||||
boolean validateToken(ApiRequest apiRequest);
|
||||
}
|
@@ -1,18 +1,20 @@
|
||||
package com.gitee.sop.index.service.validate;
|
||||
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiInfoDTO;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
|
||||
/**
|
||||
* 校验接口
|
||||
*
|
||||
* @author tanghc
|
||||
*
|
||||
*/
|
||||
public interface Validator {
|
||||
/**
|
||||
* 接口验证
|
||||
* @param apiRequest 接口参数
|
||||
*
|
||||
* @param apiRequestContext 请求内容
|
||||
* @return 校验通过返回路由信息
|
||||
*/
|
||||
void validate(ApiRequest apiRequest);
|
||||
ApiInfoDTO validate(ApiRequestContext apiRequestContext);
|
||||
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@
|
||||
package com.gitee.sop.index.service.validate.alipay;
|
||||
|
||||
|
||||
import com.gitee.sop.index.common.ParamNames;
|
||||
import com.gitee.sop.index.exception.SignException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.service.validate.SignConfig;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -71,17 +73,14 @@ public class AlipaySignature {
|
||||
* @return
|
||||
*/
|
||||
public static String rsaSign(String content, String publicKey, String charset,
|
||||
String signType) {
|
||||
String signType) throws SignException {
|
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {
|
||||
|
||||
return rsaSign(content, publicKey, charset);
|
||||
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) {
|
||||
|
||||
return rsa256Sign(content, publicKey, charset);
|
||||
} else {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException();
|
||||
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE_TYPE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,7 +94,7 @@ public class AlipaySignature {
|
||||
* @return
|
||||
*/
|
||||
public static String rsa256Sign(String content, String privateKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
|
||||
try {
|
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
|
||||
@@ -116,8 +115,7 @@ public class AlipaySignature {
|
||||
|
||||
return new String(Base64.encodeBase64(signed));
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -131,7 +129,7 @@ public class AlipaySignature {
|
||||
* @return
|
||||
*/
|
||||
public static String rsaSign(String content, String publicKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
try {
|
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
|
||||
new ByteArrayInputStream(publicKey.getBytes()));
|
||||
@@ -151,16 +149,14 @@ public class AlipaySignature {
|
||||
|
||||
return new String(Base64.encodeBase64(signed));
|
||||
} catch (InvalidKeySpecException ie) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException(ie);
|
||||
// throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE_TYPE, ie);
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String rsaSign(Map<String, Object> params, String publicKey,
|
||||
String charset, String signType) {
|
||||
String charset, String signType) throws SignException {
|
||||
String signContent = getSignContent(params);
|
||||
|
||||
return rsaSign(signContent, publicKey, charset, signType);
|
||||
@@ -224,15 +220,14 @@ public class AlipaySignature {
|
||||
}
|
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
|
||||
String charset) {
|
||||
String sign = params.get("sign");
|
||||
String charset) throws SignException {
|
||||
String sign = params.get(ParamNames.SIGN_NAME);
|
||||
String content = getSignCheckContentV1(params);
|
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset);
|
||||
}
|
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
|
||||
String charset, String signType) {
|
||||
String charset, String signType) throws SignException {
|
||||
String sign = params.get("sign");
|
||||
String content = getSignCheckContentV1(params);
|
||||
|
||||
@@ -240,15 +235,15 @@ public class AlipaySignature {
|
||||
}
|
||||
|
||||
public static boolean rsaCheckV2(Map<String, String> params, String publicKey,
|
||||
String charset) {
|
||||
String sign = params.get("sign");
|
||||
String charset) throws SignException {
|
||||
String sign = params.get(ParamNames.SIGN_NAME);
|
||||
String content = getSignCheckContentV2(params);
|
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset);
|
||||
}
|
||||
|
||||
public static boolean rsaCheckV2(Map<String, ?> params, String publicKey,
|
||||
String charset, String signType) {
|
||||
String charset, String signType) throws SignException {
|
||||
String sign = String.valueOf(params.get("sign"));
|
||||
String content = getSignCheckContentV2(params);
|
||||
|
||||
@@ -256,7 +251,7 @@ public class AlipaySignature {
|
||||
}
|
||||
|
||||
public static boolean rsaCheck(String content, String sign, String publicKey, String charset,
|
||||
String signType) {
|
||||
String signType) throws SignException {
|
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) {
|
||||
|
||||
@@ -267,14 +262,13 @@ public class AlipaySignature {
|
||||
return rsa256CheckContent(content, sign, publicKey, charset);
|
||||
|
||||
} else {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE_TYPE.getErrorMeta().getException();
|
||||
// throw new AlipayApiException("Sign Type is Not Support : signType=" + signType);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE_TYPE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean rsa256CheckContent(String content, String sign, String publicKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
try {
|
||||
PublicKey pubKey = getPublicKeyFromX509("RSA",
|
||||
new ByteArrayInputStream(publicKey.getBytes()));
|
||||
@@ -292,14 +286,12 @@ public class AlipaySignature {
|
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes()));
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException(
|
||||
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean rsaCheckContent(String content, String sign, String publicKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
try {
|
||||
PublicKey pubKey = getPublicKeyFromX509("RSA",
|
||||
new ByteArrayInputStream(publicKey.getBytes()));
|
||||
@@ -317,9 +309,7 @@ public class AlipaySignature {
|
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes()));
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException(
|
||||
// "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,13 +349,12 @@ public class AlipaySignature {
|
||||
*/
|
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey,
|
||||
String cusPrivateKey, boolean isCheckSign,
|
||||
boolean isDecrypt) {
|
||||
String charset = params.get("charset");
|
||||
String bizContent = params.get("biz_content");
|
||||
boolean isDecrypt) throws SignException {
|
||||
String charset = params.get(ParamNames.CHARSET_NAME);
|
||||
String bizContent = params.get(ParamNames.BIZ_CONTENT_NAME);
|
||||
if (isCheckSign) {
|
||||
if (!rsaCheckV2(params, alipayPublicKey, charset)) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
|
||||
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,13 +387,12 @@ public class AlipaySignature {
|
||||
*/
|
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey,
|
||||
String cusPrivateKey, boolean isCheckSign,
|
||||
boolean isDecrypt, String signType) {
|
||||
boolean isDecrypt, String signType) throws SignException {
|
||||
String charset = params.get("charset");
|
||||
String bizContent = params.get("biz_content");
|
||||
if (isCheckSign) {
|
||||
if (!rsaCheckV2(params, alipayPublicKey, charset, signType)) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException();
|
||||
// throw new AlipayApiException("rsaCheck failure:rsaParams=" + params);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,7 +426,7 @@ public class AlipaySignature {
|
||||
*/
|
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey,
|
||||
String cusPrivateKey, String charset, boolean isEncrypt,
|
||||
boolean isSign) {
|
||||
boolean isSign) throws SignException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (StringUtils.isEmpty(charset)) {
|
||||
charset = AlipayConstants.CHARSET_GBK;
|
||||
@@ -491,7 +479,7 @@ public class AlipaySignature {
|
||||
*/
|
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey,
|
||||
String cusPrivateKey, String charset, boolean isEncrypt,
|
||||
boolean isSign, String signType) {
|
||||
boolean isSign, String signType) throws SignException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (StringUtils.isEmpty(charset)) {
|
||||
charset = AlipayConstants.CHARSET_GBK;
|
||||
@@ -534,7 +522,7 @@ public class AlipaySignature {
|
||||
* @return 密文内容
|
||||
*/
|
||||
public static String rsaEncrypt(String content, String publicKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
try {
|
||||
PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA,
|
||||
new ByteArrayInputStream(publicKey.getBytes()));
|
||||
@@ -564,9 +552,7 @@ public class AlipaySignature {
|
||||
return StringUtils.isEmpty(charset) ? new String(encryptedData)
|
||||
: new String(encryptedData, charset);
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset,
|
||||
// e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +565,7 @@ public class AlipaySignature {
|
||||
* @return 明文内容
|
||||
*/
|
||||
public static String rsaDecrypt(String content, String publicKey,
|
||||
String charset) {
|
||||
String charset) throws SignException {
|
||||
try {
|
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA,
|
||||
new ByteArrayInputStream(publicKey.getBytes()));
|
||||
@@ -610,8 +596,7 @@ public class AlipaySignature {
|
||||
return StringUtils.isEmpty(charset) ? new String(decryptedData)
|
||||
: new String(decryptedData, charset);
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_SIGNATURE.getErrorMeta().getException(e);
|
||||
// throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e);
|
||||
throw new SignException(ErrorEnum.ISV_INVALID_SIGNATURE, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,13 @@
|
||||
package com.gitee.sop.index.service.validate.alipay;
|
||||
|
||||
|
||||
import com.gitee.sop.index.common.ApiRequest;
|
||||
import com.gitee.sop.index.common.ApiRequestContext;
|
||||
import com.gitee.sop.index.common.ParamNames;
|
||||
import com.gitee.sop.index.controller.param.ApiRequest;
|
||||
import com.gitee.sop.index.exception.ApiException;
|
||||
import com.gitee.sop.index.exception.SignException;
|
||||
import com.gitee.sop.index.message.ErrorEnum;
|
||||
import com.gitee.sop.index.message.ErrorMeta;
|
||||
import com.gitee.sop.index.service.validate.Signer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -20,23 +24,29 @@ import java.util.Map;
|
||||
public class AlipaySigner implements Signer {
|
||||
|
||||
@Override
|
||||
public boolean checkSign(ApiRequest apiRequest, String secret) {
|
||||
public boolean checkSign(ApiRequestContext apiRequestContext, String publicKey) {
|
||||
ApiRequest apiRequest = apiRequestContext.getApiRequest();
|
||||
// 服务端存的是公钥
|
||||
String publicKey = secret;
|
||||
String charset = apiRequest.getCharset();
|
||||
String signType = apiRequest.getSign_type();
|
||||
if (signType == null) {
|
||||
throw ErrorEnum.ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE.getErrorMeta().getException();
|
||||
throw new ApiException(ErrorEnum.ISV_DECRYPTION_ERROR_MISSING_ENCRYPT_TYPE, apiRequestContext.getLocale());
|
||||
}
|
||||
if (charset == null) {
|
||||
throw ErrorEnum.ISV_INVALID_CHARSET.getErrorMeta().getException();
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_CHARSET, apiRequestContext.getLocale());
|
||||
}
|
||||
Map<String, String> params = buildParams(apiRequest);
|
||||
try {
|
||||
return AlipaySignature.rsaCheckV2(params, publicKey, charset, signType);
|
||||
} catch (SignException e) {
|
||||
ErrorEnum errorEnum = e.getErrorEnum();
|
||||
ErrorMeta errorMeta = errorEnum.getErrorMeta();
|
||||
log.error("验签错误, code={}, subCode={}, apiRequest={}",
|
||||
errorMeta.getCode(), errorMeta.getSubCode(), apiRequest, e);
|
||||
throw new ApiException(errorEnum, apiRequestContext.getLocale());
|
||||
} catch (Exception e) {
|
||||
log.error("RSA2解密异常, params={}", params, e);
|
||||
return false;
|
||||
log.error("验签未知错误, apiRequest={}", apiRequest, e);
|
||||
throw new ApiException(ErrorEnum.ISV_INVALID_SIGNATURE, apiRequestContext.getLocale());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package com.gitee.sop.index.util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public class RequestUtil {
|
||||
private static final String IP_UNKNOWN = "unknown";
|
||||
private static final String IP_LOCAL = "127.0.0.1";
|
||||
private static final int IP_LEN = 15;
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @param request request
|
||||
* @return 返回ip
|
||||
*/
|
||||
public static String getIP(HttpServletRequest request) {
|
||||
String ipAddress = request.getHeader("x-forwarded-for");
|
||||
if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getRemoteAddr();
|
||||
if (IP_LOCAL.equals(ipAddress)) {
|
||||
// 根据网卡取本机配置的IP
|
||||
try {
|
||||
InetAddress inet = InetAddress.getLocalHost();
|
||||
ipAddress = inet.getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||
if (ipAddress != null && ipAddress.length() > IP_LEN) {
|
||||
if (ipAddress.indexOf(",") > 0) {
|
||||
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
|
||||
}
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
nacos.host=127.0.0.1:8848
|
||||
dubbo.registry.address=zookeeper://localhost:2181
|
||||
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
dubbo.registry.address=nacos://${nacos.host}
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:test
|
||||
spring.sql.init.schema-locations=classpath:schema.sql
|
||||
spring.datasource.username=${mysql.username}
|
||||
spring.datasource.password=${mysql.password}
|
||||
|
@@ -1,3 +1,28 @@
|
||||
spring.profiles.active=dev
|
||||
spring.application.name=sop-index
|
||||
server.port=8081
|
||||
|
||||
####### dubbo config #######
|
||||
dubbo.protocol.name=dubbo
|
||||
dubbo.protocol.port=-1
|
||||
# ### register config see:https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/overview/
|
||||
# ------
|
||||
# nacos://localhost:8848 Cluster config:nacos://localhost:8848?backup=localshot:8846,localshot:8847
|
||||
# zookeeper://localhost:2181 Cluster config:zookeeper://10.20.153.10:2181?backup=10.20.153.11:2181,10.20.153.12:2181
|
||||
# redis://localhost:6379 Cluster config:redis://10.20.153.10:6379?backup=10.20.153.11:6379,10.20.153.12:6379
|
||||
# ------
|
||||
dubbo.registry.address=nacos://localhost:8848
|
||||
####### dubbo config end #######
|
||||
|
||||
|
||||
####### mysql config #######
|
||||
mysql.host=127.0.0.1:3306
|
||||
mysql.username=root
|
||||
mysql.password=root
|
||||
mysql.db=sop
|
||||
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
|
||||
spring.datasource.username=${mysql.username}
|
||||
spring.datasource.password=${mysql.password}
|
||||
####### mysql config end #######
|
||||
|
16
sop-index/sop-index-service/src/main/resources/schema.sql
Normal file
16
sop-index/sop-index-service/src/main/resources/schema.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE `api_info`
|
||||
(
|
||||
`id` INTEGER PRIMARY KEY AUTO_INCREMENT,
|
||||
`application` varchar(64) NOT NULL DEFAULT '' COMMENT '应用名称',
|
||||
`api_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名称',
|
||||
`api_version` varchar(16) NOT NULL DEFAULT '1.0' COMMENT '版本号',
|
||||
`interface_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口class',
|
||||
`method_name` varchar(128) NOT NULL DEFAULT '' COMMENT '方法名称',
|
||||
`param_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数名称',
|
||||
`param_type_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数类型名称',
|
||||
is_permission tinyint(4) NOT NULL DEFAULT '0' COMMENT '接口是否需要授权访问',
|
||||
is_need_Token tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否需要appAuthToken',
|
||||
api_status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1-启用,0-禁用',
|
||||
`add_time` datetime NOT NULL DEFAULT null,
|
||||
`update_time` datetime NOT NULL DEFAULT null
|
||||
);
|
52
sop-registry/pom.xml
Normal file
52
sop-registry/pom.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-parent</artifactId>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>sop-registry</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<type>pom</type>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>log4j</artifactId>
|
||||
<groupId>log4j</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
4
sop-registry/readme.md
Normal file
4
sop-registry/readme.md
Normal file
@@ -0,0 +1,4 @@
|
||||
内置简单的注册中心,基于zookeeper,用来开发演示使用.
|
||||
|
||||
生产环境不可使用!
|
||||
生产环境不可使用!
|
@@ -0,0 +1,309 @@
|
||||
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.gitee.sop.registry;
|
||||
|
||||
import org.apache.zookeeper.server.ServerConfig;
|
||||
import org.apache.zookeeper.server.ZooKeeperServerMain;
|
||||
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* from: https://github.com/spring-projects/spring-xd/blob/v1.3.1.RELEASE/spring-xd-dirt/src/main/java/org/springframework/xd/dirt/zookeeper/ZooKeeperUtils.java
|
||||
* <p>
|
||||
* Helper class to start an embedded instance of standalone (non clustered) ZooKeeper.
|
||||
* <p>
|
||||
* NOTE: at least an external standalone server (if not an ensemble) are recommended, even for
|
||||
* {@link org.springframework.xd.dirt.server.singlenode.SingleNodeApplication}
|
||||
*
|
||||
* @author Patrick Peralta
|
||||
* @author Mark Fisher
|
||||
* @author David Turanski
|
||||
*/
|
||||
public class EmbeddedZooKeeper implements SmartLifecycle {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
/**
|
||||
* Logger.
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(EmbeddedZooKeeper.class);
|
||||
|
||||
/**
|
||||
* ZooKeeper client port. This will be determined dynamically upon startup.
|
||||
*/
|
||||
private final int clientPort;
|
||||
|
||||
/**
|
||||
* Whether to auto-start. Default is true.
|
||||
*/
|
||||
private boolean autoStartup = true;
|
||||
|
||||
/**
|
||||
* Lifecycle phase. Default is 0.
|
||||
*/
|
||||
private int phase = 0;
|
||||
|
||||
/**
|
||||
* Thread for running the ZooKeeper server.
|
||||
*/
|
||||
private volatile Thread zkServerThread;
|
||||
|
||||
/**
|
||||
* ZooKeeper server.
|
||||
*/
|
||||
private volatile ZooKeeperServerMain zkServer;
|
||||
|
||||
/**
|
||||
* {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread.
|
||||
*/
|
||||
private ErrorHandler errorHandler;
|
||||
|
||||
private boolean daemon = true;
|
||||
|
||||
/**
|
||||
* Construct an EmbeddedZooKeeper with a random port.
|
||||
*/
|
||||
public EmbeddedZooKeeper() {
|
||||
clientPort = findRandomPort(30000, 65535);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an EmbeddedZooKeeper with the provided port.
|
||||
*
|
||||
* @param clientPort port for ZooKeeper server to bind to
|
||||
*/
|
||||
public EmbeddedZooKeeper(int clientPort, boolean daemon) {
|
||||
this.clientPort = clientPort;
|
||||
this.daemon = daemon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port that clients should use to connect to this embedded server.
|
||||
*
|
||||
* @return dynamically determined client port
|
||||
*/
|
||||
public int getClientPort() {
|
||||
return this.clientPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to start automatically. Default is true.
|
||||
*
|
||||
* @param autoStartup whether to start automatically
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return this.autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the lifecycle phase for the embedded server.
|
||||
*
|
||||
* @param phase the lifecycle phase
|
||||
*/
|
||||
public void setPhase(int phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return (zkServerThread != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ZooKeeper server in a background thread.
|
||||
* <p>
|
||||
* Register an error handler via {@link #setErrorHandler} in order to handle
|
||||
* any exceptions thrown during startup or execution.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void start() {
|
||||
if (zkServerThread == null) {
|
||||
zkServerThread = new Thread(new ServerRunnable(), "ZooKeeper Server Starter");
|
||||
zkServerThread.setDaemon(daemon);
|
||||
zkServerThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the ZooKeeper server.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void stop() {
|
||||
if (zkServerThread != null) {
|
||||
// The shutdown method is protected...thus this hack to invoke it.
|
||||
// This will log an exception on shutdown; see
|
||||
// https://issues.apache.org/jira/browse/ZOOKEEPER-1873 for details.
|
||||
try {
|
||||
Method shutdown = ZooKeeperServerMain.class.getDeclaredMethod("shutdown");
|
||||
shutdown.setAccessible(true);
|
||||
shutdown.invoke(zkServer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// It is expected that the thread will exit after
|
||||
// the server is shutdown; this will block until
|
||||
// the shutdown is complete.
|
||||
try {
|
||||
zkServerThread.join(5000);
|
||||
zkServerThread = null;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.warn("Interrupted while waiting for embedded ZooKeeper to exit");
|
||||
// abandoning zk thread
|
||||
zkServerThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server if running and invoke the callback when complete.
|
||||
*/
|
||||
@Override
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an {@link ErrorHandler} to be invoked if an Exception is thrown from the ZooKeeper server thread. If none
|
||||
* is provided, only error-level logging will occur.
|
||||
*
|
||||
* @param errorHandler the {@link ErrorHandler} to be invoked
|
||||
*/
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable implementation that starts the ZooKeeper server.
|
||||
*/
|
||||
private class ServerRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
File file = new File(System.getProperty("java.io.tmpdir")
|
||||
+ File.separator + UUID.randomUUID());
|
||||
file.deleteOnExit();
|
||||
properties.setProperty("dataDir", file.getAbsolutePath());
|
||||
properties.setProperty("clientPort", String.valueOf(clientPort));
|
||||
|
||||
QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig();
|
||||
quorumPeerConfig.parseProperties(properties);
|
||||
|
||||
zkServer = new ZooKeeperServerMain();
|
||||
ServerConfig configuration = new ServerConfig();
|
||||
configuration.readFrom(quorumPeerConfig);
|
||||
|
||||
System.setProperty("zookeeper.admin.enableServer", "false");
|
||||
|
||||
zkServer.runFromConfig(configuration);
|
||||
} catch (Exception e) {
|
||||
if (errorHandler != null) {
|
||||
errorHandler.handleError(e);
|
||||
} else {
|
||||
logger.error("Exception running embedded ZooKeeper", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for SocketUtils.findRandomPort() deprecation.
|
||||
*
|
||||
* @param min min port
|
||||
* @param max max port
|
||||
* @return a random generated available port
|
||||
*/
|
||||
private static int findRandomPort(int min, int max) {
|
||||
if (min < 1024) {
|
||||
throw new IllegalArgumentException("Max port shouldn't be less than 1024.");
|
||||
}
|
||||
|
||||
if (max > 65535) {
|
||||
throw new IllegalArgumentException("Max port shouldn't be greater than 65535.");
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
throw new IllegalArgumentException("Min port shouldn't be greater than max port.");
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
int counter = 0;
|
||||
|
||||
// Workaround for legacy JDK doesn't support Random.nextInt(min, max).
|
||||
List<Integer> randomInts = RANDOM.ints(min, max + 1)
|
||||
.limit(max - min)
|
||||
.mapToObj(Integer::valueOf)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
do {
|
||||
if (counter > max - min) {
|
||||
throw new IllegalStateException("Unable to find a port between " + min + "-" + max);
|
||||
}
|
||||
|
||||
port = randomInts.get(counter);
|
||||
counter++;
|
||||
} while (isPortInUse(port));
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
private static boolean isPortInUse(int port) {
|
||||
try (ServerSocket ignored = new ServerSocket(port)) {
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
// continue
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.registry;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class RegistryMain {
|
||||
public static void main(String[] args) {
|
||||
int zkPort = 2181;
|
||||
System.out.println("启动内置zookeeper注册中心(仅在开发环境下使用),port=" + zkPort);
|
||||
new EmbeddedZooKeeper(zkPort, false).start();
|
||||
}
|
||||
}
|
@@ -21,12 +21,6 @@
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
|
@@ -1,16 +0,0 @@
|
||||
package com.gitee.sop.springboot.starter.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface OpenParam {
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
package com.gitee.sop.springboot.starter.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class IntId {
|
||||
@NotNull(message = "id不能为空")
|
||||
private Integer id;
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
package com.gitee.sop.springboot.starter.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
@Data
|
||||
public class LongId {
|
||||
|
||||
private Long id;
|
||||
|
||||
}
|
@@ -1,7 +1,8 @@
|
||||
package com.gitee.sop.springboot.starter.annotation;
|
||||
package com.gitee.sop.support.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -9,6 +10,7 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Inherited
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@@ -24,16 +26,6 @@ public @interface Open {
|
||||
*/
|
||||
String version() default "1.0";
|
||||
|
||||
/**
|
||||
* 忽略验证,业务参数除外
|
||||
*/
|
||||
boolean ignoreValidate() default false;
|
||||
|
||||
/**
|
||||
* 告诉网关是否对结果进行合并,默认合并。设置为false,客户端将直接收到微服务端的结果。
|
||||
*/
|
||||
boolean mergeResult() default true;
|
||||
|
||||
/**
|
||||
* 指定接口是否需要授权才能访问,可在admin中进行修改
|
||||
*/
|
@@ -1,9 +1,8 @@
|
||||
package com.gitee.sop.springboot.starter.config;
|
||||
package com.gitee.sop.support.config;
|
||||
|
||||
import com.gitee.sop.index.api.ApiRegisterService;
|
||||
import com.gitee.sop.index.api.RegisterDTO;
|
||||
import com.gitee.sop.springboot.starter.annotation.Open;
|
||||
import com.gitee.sop.springboot.starter.annotation.OpenParam;
|
||||
import com.gitee.sop.support.annotation.Open;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
@@ -20,7 +19,6 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,13 +43,23 @@ public class SopAutoConfiguration implements InitializingBean {
|
||||
Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(DubboService.class);
|
||||
for (Object serviceObj : beanMap.values()) {
|
||||
Class<?> objClass = serviceObj.getClass();
|
||||
ReflectionUtils.doWithMethods(objClass, method -> {
|
||||
regApi(appName, objClass, method);
|
||||
Class<?> interfaceClass = findInterfaceClass(serviceObj);
|
||||
ReflectionUtils.doWithMethods(interfaceClass, method -> {
|
||||
regApi(appName, interfaceClass, method);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void regApi(String appName, Class<?> clazz, Method method) {
|
||||
private Class<?> findInterfaceClass(Object serviceObj) {
|
||||
Class<?> objClass = serviceObj.getClass();
|
||||
Class<?>[] interfaces = objClass.getInterfaces();
|
||||
if (interfaces.length == 0) {
|
||||
throw new RuntimeException("Dubbo接口必须要实现接口, class=" + objClass.getName());
|
||||
}
|
||||
return interfaces[0];
|
||||
}
|
||||
|
||||
private void regApi(String appName, Class<?> interfaceClass, Method method) {
|
||||
Open open = AnnotationUtils.getAnnotation(method, Open.class);
|
||||
if (open == null) {
|
||||
return;
|
||||
@@ -64,46 +72,25 @@ public class SopAutoConfiguration implements InitializingBean {
|
||||
registerDTO.setApplication(appName);
|
||||
registerDTO.setApiName(open.value());
|
||||
registerDTO.setApiVersion(open.version());
|
||||
Class<?>[] interfaces = clazz.getInterfaces();
|
||||
if (interfaces.length == 0) {
|
||||
throw new RuntimeException("Dubbo接口必须要实现接口, class=" + clazz.getName());
|
||||
}
|
||||
Optional<Class<?>> interfaceClassOpt = filterInterfaceClass(method, interfaces);
|
||||
if (!interfaceClassOpt.isPresent()) {
|
||||
throw new RuntimeException("找不到dubbo接口, clazz=" + clazz.getName());
|
||||
}
|
||||
registerDTO.setInterfaceClassName(interfaceClassOpt.get().getName());
|
||||
registerDTO.setInterfaceClassName(interfaceClass.getName());
|
||||
registerDTO.setMethodName(method.getName());
|
||||
registerDTO.setParamName(paramName);
|
||||
registerDTO.setParamTypeName(paramTypeName);
|
||||
registerDTO.setIsIgnoreValidate(parseBoolean(open.ignoreValidate()));
|
||||
registerDTO.setIsPermission(parseBoolean(open.permission()));
|
||||
registerDTO.setIsNeedToken(parseBoolean(open.needToken()));
|
||||
log.info("注册开放接口, apiInfo={}", registerDTO);
|
||||
apiRegisterService.register(registerDTO);
|
||||
try {
|
||||
apiRegisterService.register(registerDTO);
|
||||
} catch (Exception e) {
|
||||
log.error("接口注册失败, registerDTO={}, method={}", registerDTO, method, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Parameter> filterParameter(Parameter[] parameters) {
|
||||
if (ObjectUtils.isEmpty(parameters)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Optional<Parameter> first = Stream.of(parameters)
|
||||
.filter(parameter -> AnnotationUtils.getAnnotation(parameter, OpenParam.class) != null)
|
||||
.findFirst();
|
||||
return first.isPresent() ? first : Optional.of(parameters[0]);
|
||||
}
|
||||
|
||||
private Optional<Class<?>> filterInterfaceClass(Method method, Class<?>[] interfaces) {
|
||||
return Stream.of(interfaces)
|
||||
.filter(inter -> {
|
||||
for (Method interMethod : inter.getMethods()) {
|
||||
if (interMethod.getName().equals(method.getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.findFirst();
|
||||
return Optional.of(parameters[0]);
|
||||
}
|
||||
|
||||
private int parseBoolean(boolean b) {
|
@@ -0,0 +1,10 @@
|
||||
package com.gitee.sop.support.context;
|
||||
|
||||
/**
|
||||
* @author 六如
|
||||
*/
|
||||
public class OpenContext {
|
||||
|
||||
|
||||
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
package com.gitee.sop.springboot.starter.request;
|
||||
package com.gitee.sop.support.request;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import lombok.Data;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
||||
@@ -90,13 +89,4 @@ public class OpenRequest<T> implements Serializable {
|
||||
*/
|
||||
private String biz_content;
|
||||
|
||||
/**
|
||||
* 将biz_content转成对象
|
||||
*
|
||||
* @return 返回对象
|
||||
*/
|
||||
public T toJavaObject() {
|
||||
return JSON.parseObject(biz_content, clazz);
|
||||
}
|
||||
|
||||
}
|
@@ -1,3 +1,3 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.gitee.sop.springboot.starter.config.SopAutoConfiguration
|
||||
com.gitee.sop.support.config.SopAutoConfiguration
|
||||
|
@@ -1 +1 @@
|
||||
com.gitee.sop.springboot.starter.config.SopAutoConfiguration
|
||||
com.gitee.sop.support.config.SopAutoConfiguration
|
||||
|
29
sop.sql
29
sop.sql
@@ -5,21 +5,20 @@ USE `sop`;
|
||||
-- 5.0
|
||||
|
||||
CREATE TABLE `api_info` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`application` varchar(64) NOT NULL DEFAULT '' COMMENT '应用名称',
|
||||
`api_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名称',
|
||||
`api_version` varchar(16) NOT NULL DEFAULT '1.0' COMMENT '版本号',
|
||||
`interface_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口class',
|
||||
`method_name` varchar(128) NOT NULL DEFAULT '' COMMENT '方法名称',
|
||||
`param_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数class',
|
||||
is_ignore_validate tinyint(4) NOT NULL DEFAULT '0' COMMENT '忽略验证',
|
||||
is_permission tinyint(4) NOT NULL DEFAULT '0' COMMENT '接口是否需要授权访问',
|
||||
is_need_Token tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否需要appAuthToken',
|
||||
status tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1-启用,0-禁用',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_apiname_version` (`api_name`, api_version) USING BTREE
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`application` varchar(64) NOT NULL DEFAULT '' COMMENT '应用名称',
|
||||
`api_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口名称',
|
||||
`api_version` varchar(16) NOT NULL DEFAULT '1.0' COMMENT '版本号',
|
||||
`interface_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '接口class',
|
||||
`method_name` varchar(128) NOT NULL DEFAULT '' COMMENT '方法名称',
|
||||
`param_class_name` varchar(128) NOT NULL DEFAULT '' COMMENT '参数class',
|
||||
`is_permission` tinyint(4) NOT NULL DEFAULT '0' COMMENT '接口是否需要授权访问',
|
||||
`is_need_Token` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否需要appAuthToken',
|
||||
`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态,1-启用,0-禁用',
|
||||
`add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_apiname_version` (`api_name`,`api_version`) USING BTREE
|
||||
) ENGINE=InnoDB COMMENT='接口信息表';
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user