├── .gitignore ├── open-api-common ├── src │ └── main │ │ └── java │ │ └── com │ │ └── open │ │ └── api │ │ ├── enums │ │ ├── EnumInterface.java │ │ └── ApiExceptionEnum.java │ │ ├── annotation │ │ ├── OpenApiService.java │ │ └── OpenApi.java │ │ ├── exception │ │ ├── BusinessException.java │ │ └── BaseException.java │ │ ├── model │ │ ├── ApiModel.java │ │ └── ResultModel.java │ │ └── util │ │ ├── ValidateUtils.java │ │ └── SystemClock.java └── pom.xml ├── README.md ├── open-api-web ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── open │ │ │ │ └── api │ │ │ │ ├── config │ │ │ │ ├── gateway │ │ │ │ │ ├── ApiContainer.java │ │ │ │ │ ├── ApiScanner.java │ │ │ │ │ └── ApiClient.java │ │ │ │ ├── property │ │ │ │ │ └── ApplicationProperty.java │ │ │ │ ├── context │ │ │ │ │ └── ApplicationContextHelper.java │ │ │ │ └── advice │ │ │ │ │ └── ExceptionAdvice.java │ │ │ │ ├── service │ │ │ │ ├── TestOneService.java │ │ │ │ ├── TestTwoService.java │ │ │ │ └── impl │ │ │ │ │ ├── TestTwoServiceImpl.java │ │ │ │ │ └── TestOneServiceImpl.java │ │ │ │ ├── Application.java │ │ │ │ └── web │ │ │ │ ├── bo │ │ │ │ ├── Test2BO.java │ │ │ │ └── Test1BO.java │ │ │ │ └── controller │ │ │ │ └── OpenApiController.java │ │ └── resources │ │ │ ├── banner.txt │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── open │ │ └── api │ │ └── sign │ │ └── GetWayRequestDemo.java └── pom.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea/ 3 | logs/ 4 | *.log 5 | *.iml 6 | target/ 7 | .DS_store 8 | 9 | -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/enums/EnumInterface.java: -------------------------------------------------------------------------------- 1 | package com.open.api.enums; 2 | 3 | public interface EnumInterface { 4 | String getCode(); 5 | 6 | String getMsg(); 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # open-api-project 2 | java版开放接口统一网关鉴权demo项目 3 | 4 | open-api-common 公共基础配置 5 | 6 | open-api-web 开放接口 web 7 | 8 | 9 | 详细介绍请跳转博客谢谢 10 | https://blog.csdn.net/qq_38011415/article/details/88779364 11 | -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/annotation/OpenApiService.java: -------------------------------------------------------------------------------- 1 | package com.open.api.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 开放接口实现类注解 7 | * 8 | * @author 程序员小强 9 | */ 10 | @Target({ElementType.TYPE}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | public @interface OpenApiService { 14 | } 15 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/gateway/ApiContainer.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.gateway; 2 | 3 | import com.open.api.model.ApiModel; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.HashMap; 7 | 8 | /** 9 | * Api 初始化容器 10 | */ 11 | @Service 12 | public class ApiContainer extends HashMap { 13 | } 14 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/service/TestOneService.java: -------------------------------------------------------------------------------- 1 | package com.open.api.service; 2 | 3 | 4 | import com.open.api.annotation.OpenApi; 5 | import com.open.api.web.bo.Test1BO; 6 | 7 | /** 8 | * 测试开放接口1 9 | */ 10 | public interface TestOneService { 11 | 12 | /** 13 | * 方法1 14 | */ 15 | @OpenApi(method = "open.api.test.one.method1", desc = "测试接口1,方法1") 16 | void testMethod1(String requestId, Test1BO test1BO); 17 | } -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/service/TestTwoService.java: -------------------------------------------------------------------------------- 1 | package com.open.api.service; 2 | 3 | 4 | import com.open.api.annotation.OpenApi; 5 | import com.open.api.web.bo.Test2BO; 6 | 7 | /** 8 | * 测试开放接口2 9 | */ 10 | public interface TestTwoService { 11 | 12 | /** 13 | * 方法1 14 | */ 15 | @OpenApi(method = "open.api.test.two.method1", desc = "测试接口1,方法1") 16 | void testMethod1(String requestId, Test2BO test2BO); 17 | } -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/annotation/OpenApi.java: -------------------------------------------------------------------------------- 1 | package com.open.api.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 开放接口注解 7 | * 8 | * @author 程序员小强 9 | */ 10 | @Documented 11 | @Inherited 12 | @Target({ElementType.METHOD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface OpenApi { 15 | 16 | /** 17 | * api 方法名 18 | */ 19 | String method(); 20 | 21 | /** 22 | * 方法描述 23 | */ 24 | String desc() default ""; 25 | } 26 | -------------------------------------------------------------------------------- /open-api-web/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BRIGHT_RED} 2 | ----------------------------------------- 3 | .__ 4 | ____ ______ ____ ____ _____ ______ |__| 5 | / _ \\____ \_/ __ \ / \\__ \ \____ \| | 6 | ( <_> ) |_> > ___/| | \/ __ \| |_> > | 7 | \____/| __/ \___ >___| (____ / __/|__| 8 | |__| \/ \/ \/|__| 9 | 10 | ---------------------------------------- 11 | ${AnsiColor.GREEN} 12 | Spring Boot Version: ${spring-boot.version} ${spring-boot.formatted-version} 13 | Application port: ${server.port} 14 | Root Log Level: ${logging.level.root} -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/exception/BusinessException.java: -------------------------------------------------------------------------------- 1 | package com.open.api.exception; 2 | 3 | 4 | import com.open.api.enums.EnumInterface; 5 | 6 | /** 7 | * 业务-统一异常 8 | * 9 | * @author 程序员小强 10 | */ 11 | public class BusinessException extends BaseException { 12 | 13 | public BusinessException(String message) { 14 | super(message); 15 | } 16 | 17 | public BusinessException(String code, String message) { 18 | super(code, message); 19 | } 20 | 21 | public BusinessException(EnumInterface enums, Object... args) { 22 | super(enums.getCode(), enums.getMsg(), args); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/model/ApiModel.java: -------------------------------------------------------------------------------- 1 | package com.open.api.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * api接口对象 9 | */ 10 | @Data 11 | public class ApiModel { 12 | 13 | /** 14 | * 类 spring bean 15 | */ 16 | private String beanName; 17 | 18 | /** 19 | * 方法对象 20 | */ 21 | private Method method; 22 | 23 | /** 24 | * 业务参数 25 | */ 26 | private String paramName; 27 | 28 | public ApiModel(String beanName, Method method, String paramName) { 29 | this.beanName = beanName; 30 | this.method = method; 31 | this.paramName = paramName; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/Application.java: -------------------------------------------------------------------------------- 1 | package com.open.api; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; 7 | 8 | /** 9 | * 统一网关平台-启动类 10 | * 11 | * @author 程序员小强 12 | */ 13 | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class}) 14 | public class Application { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(Application.class, args); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/property/ApplicationProperty.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.property; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * 公共开关,key值 属性配置 9 | * 10 | * @author 程序员小强 11 | */ 12 | @Data 13 | @Configuration 14 | @ConfigurationProperties(prefix = "open.api.common.key") 15 | public class ApplicationProperty { 16 | 17 | /** 18 | * 是否校验签名 0-不校验 19 | */ 20 | private Boolean isCheckSign = true; 21 | 22 | /** 23 | * 私钥 24 | */ 25 | private String privateKey; 26 | 27 | /** 28 | * 验签公钥 29 | */ 30 | private String publicKey; 31 | } -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/exception/BaseException.java: -------------------------------------------------------------------------------- 1 | package com.open.api.exception; 2 | 3 | import java.text.MessageFormat; 4 | 5 | /** 6 | * 基础异常 7 | * @author 程序员小强 8 | */ 9 | public class BaseException extends RuntimeException { 10 | protected String msg; 11 | protected String code; 12 | 13 | protected BaseException(String message) { 14 | super(message); 15 | } 16 | 17 | protected BaseException(String code, String msgFormat, Object... args) { 18 | super(MessageFormat.format(msgFormat, args)); 19 | this.code = code; 20 | this.msg = MessageFormat.format(msgFormat, args); 21 | } 22 | 23 | public String getMsg() { 24 | return this.msg; 25 | } 26 | 27 | public String getCode() { 28 | return this.code; 29 | } 30 | } -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/web/bo/Test2BO.java: -------------------------------------------------------------------------------- 1 | package com.open.api.web.bo; 2 | 3 | 4 | import lombok.Data; 5 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 6 | import org.apache.commons.lang3.builder.ToStringStyle; 7 | import org.hibernate.validator.constraints.NotBlank; 8 | 9 | import java.io.Serializable; 10 | 11 | @Data 12 | public class Test2BO implements Serializable { 13 | private static final long serialVersionUID = -1L; 14 | 15 | @NotBlank(message = "username 不能为空!") 16 | private String username; 17 | @NotBlank(message = "password 不能为空!") 18 | private String password; 19 | 20 | @Override 21 | public String toString() { 22 | return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/service/impl/TestTwoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.open.api.service.impl; 2 | 3 | 4 | import com.open.api.annotation.OpenApiService; 5 | import com.open.api.web.bo.Test2BO; 6 | import com.open.api.service.TestTwoService; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * 测试开放接口2 13 | */ 14 | @Service 15 | @OpenApiService 16 | public class TestTwoServiceImpl implements TestTwoService { 17 | 18 | /** 19 | * 日志 20 | */ 21 | private static final Logger LOGGER = LoggerFactory.getLogger(TestOneServiceImpl.class); 22 | 23 | /** 24 | * 方法1 25 | */ 26 | @Override 27 | public void testMethod1(String requestId, Test2BO test2BO) { 28 | LOGGER.info("【{}】>> 测试开放接口2 >> 方法1 params={}", requestId, test2BO); 29 | } 30 | } -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/service/impl/TestOneServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.open.api.service.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.open.api.annotation.OpenApiService; 5 | import com.open.api.web.bo.Test1BO; 6 | import com.open.api.service.TestOneService; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * 测试开放接口1 13 | *

14 | * 注解@OpenApiService > 开放接口自定义注解,用于启动时扫描接口 15 | */ 16 | @Service 17 | @OpenApiService 18 | public class TestOneServiceImpl implements TestOneService { 19 | 20 | /** 21 | * 日志 22 | */ 23 | private static final Logger LOGGER = LoggerFactory.getLogger(TestOneServiceImpl.class); 24 | 25 | /** 26 | * 方法1 27 | */ 28 | @Override 29 | public void testMethod1(String requestId, Test1BO test1BO) { 30 | LOGGER.info("【{}】>> 测试开放接口1 >> 方法1 params={}", requestId, JSON.toJSONString(test1BO)); 31 | } 32 | } -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/enums/ApiExceptionEnum.java: -------------------------------------------------------------------------------- 1 | package com.open.api.enums; 2 | 3 | /** 4 | * 异常枚举 5 | * 6 | * @author 程序员小强 7 | */ 8 | public enum ApiExceptionEnum implements EnumInterface { 9 | 10 | /** 11 | * api异常枚举 12 | */ 13 | SYSTEM_ERROR("SYSTEM_ERROR", "系统异常"), 14 | API_NOT_EXIST("API_NOT_EXIST", "API方法不存在"), 15 | INVALID_PUBLIC_PARAM("INVALID_PUBLIC_PARAM", "无效公共参数 >> {0}"), 16 | INVALID_REQUEST_ERROR("INVALID_REQUEST_ERROR", " 请求方式 {0} 错误 ! 请使用 {1} 方式"), 17 | INVALID_PARAM("INVALID_PARAM", "无效参数 >> 参数[{0}] >> 原因[{1}]"), 18 | INVALID_SIGN("INVALID_SIGN", "无效签名"), 19 | ; 20 | 21 | ApiExceptionEnum(String code, String msg) { 22 | this.code = code; 23 | this.msg = msg; 24 | } 25 | 26 | private String code; 27 | 28 | private String msg; 29 | 30 | @Override 31 | public String getCode() { 32 | return code; 33 | } 34 | 35 | @Override 36 | public String getMsg() { 37 | return msg; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/context/ApplicationContextHelper.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.context; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.context.ApplicationContextAware; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.Map; 9 | 10 | /** 11 | * 从applicationContext中得到Bean 工具类 12 | * 13 | * @author 程序员小强 14 | */ 15 | @Component 16 | public class ApplicationContextHelper implements ApplicationContextAware { 17 | private static ApplicationContext context; 18 | 19 | @Override 20 | public void setApplicationContext(ApplicationContext applicationContext) { 21 | context = applicationContext; 22 | } 23 | 24 | public static ApplicationContext getContext() { 25 | return context; 26 | } 27 | 28 | public static Object getBean(String beanName) { 29 | return context != null ? context.getBean(beanName) : null; 30 | } 31 | 32 | public static Map getBeansWithAnnotation(Class var1) { 33 | return context != null ? context.getBeansWithAnnotation(var1) : null; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/web/bo/Test1BO.java: -------------------------------------------------------------------------------- 1 | package com.open.api.web.bo; 2 | 3 | import lombok.Data; 4 | import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 5 | import org.apache.commons.lang3.builder.ToStringStyle; 6 | import org.hibernate.validator.constraints.NotBlank; 7 | import org.hibernate.validator.constraints.NotEmpty; 8 | 9 | import javax.validation.Valid; 10 | import javax.validation.constraints.Size; 11 | import java.io.Serializable; 12 | import java.util.List; 13 | 14 | @Data 15 | public class Test1BO implements Serializable { 16 | private static final long serialVersionUID = -1L; 17 | 18 | @Valid 19 | @NotEmpty(message = "集合不为空!") 20 | @Size(min = 1, message = "最小为{min}") 21 | private List itemList; 22 | 23 | @Data 24 | public static class Item { 25 | @NotBlank(message = "username 不能为空!") 26 | private String username; 27 | 28 | @NotBlank(message = "password 不能为空!") 29 | private String password; 30 | 31 | @NotBlank(message = "realName 不能为空!") 32 | private String realName; 33 | 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); 39 | } 40 | } -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/util/ValidateUtils.java: -------------------------------------------------------------------------------- 1 | package com.open.api.util; 2 | 3 | import com.open.api.enums.ApiExceptionEnum; 4 | import com.open.api.exception.BusinessException; 5 | import com.google.common.base.CaseFormat; 6 | 7 | import javax.validation.ConstraintViolation; 8 | import javax.validation.Validation; 9 | import javax.validation.Validator; 10 | import java.util.Set; 11 | 12 | import static com.google.common.collect.Iterables.getFirst; 13 | 14 | /** 15 | * 业务参数校验 16 | * 17 | * @author 程序员小强 18 | */ 19 | public class ValidateUtils { 20 | private final static Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); 21 | 22 | public static void validate(T object) { 23 | //执行验证 24 | Set> constraintViolations = VALIDATOR.validate(object); 25 | //如果有验证信息,则取出包装成异常返回 26 | ConstraintViolation constraintViolation = getFirst(constraintViolations, null); 27 | if (constraintViolation != null) { 28 | throw new BusinessException(ApiExceptionEnum.INVALID_PARAM, 29 | CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, constraintViolation.getPropertyPath().toString()), 30 | constraintViolation.getMessage()); 31 | 32 | } 33 | } 34 | 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/util/SystemClock.java: -------------------------------------------------------------------------------- 1 | package com.open.api.util; 2 | 3 | import java.util.concurrent.Executors; 4 | import java.util.concurrent.ScheduledExecutorService; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | /** 9 | * 高并发场景下System.currentTimeMillis()的性能问题的优化 10 | * 时间戳打印建议使用 11 | * 12 | * @author 程序员小强 13 | */ 14 | public class SystemClock { 15 | private static final String THREAD_NAME = "system.clock"; 16 | private static final SystemClock MILLIS_CLOCK = new SystemClock(1); 17 | private final long precision; 18 | private final AtomicLong now; 19 | 20 | private SystemClock(long precision) { 21 | this.precision = precision; 22 | now = new AtomicLong(System.currentTimeMillis()); 23 | scheduleClockUpdating(); 24 | } 25 | 26 | public static SystemClock millisClock() { 27 | return MILLIS_CLOCK; 28 | } 29 | 30 | private void scheduleClockUpdating() { 31 | ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> { 32 | Thread thread = new Thread(runnable, THREAD_NAME); 33 | thread.setDaemon(true); 34 | return thread; 35 | }); 36 | scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), precision, precision, TimeUnit.MILLISECONDS); 37 | } 38 | 39 | public long now() { 40 | return now.get(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /open-api-common/src/main/java/com/open/api/model/ResultModel.java: -------------------------------------------------------------------------------- 1 | package com.open.api.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 5 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 开放接口统一返回对象 12 | */ 13 | @Data 14 | @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class ResultModel implements Serializable { 17 | 18 | private static final long serialVersionUID = -1L; 19 | 20 | private boolean success; 21 | private Object data; 22 | private String errorCode; 23 | private String errorMsg; 24 | 25 | /** 26 | * 处理成功构造器 27 | * 28 | * @param data 29 | */ 30 | public ResultModel(Object data) { 31 | this.success = true; 32 | this.data = data; 33 | } 34 | 35 | /** 36 | * 处理失败构造器 37 | * 38 | * @param errorCode 39 | * @param errorMsg 40 | */ 41 | public ResultModel(String errorCode, String errorMsg) { 42 | this.success = false; 43 | this.errorCode = errorCode; 44 | this.errorMsg = errorMsg; 45 | } 46 | 47 | /** 48 | * 返回成功方法 49 | * 50 | * @param data 51 | * @return 52 | */ 53 | public static ResultModel success(Object data) { 54 | return new ResultModel(data); 55 | } 56 | 57 | /** 58 | * 返回失败方法 59 | * 60 | * @param errorCode 61 | * @param errorMsg 62 | * @return 63 | */ 64 | public static ResultModel error(String errorCode, String errorMsg) { 65 | return new ResultModel(errorCode, errorMsg); 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /open-api-web/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8821 2 | 3 | #日志配置 4 | logging.level.root=WARN 5 | logging.level.net.sf=WARN 6 | logging.level.com.open.api=debug 7 | 8 | 9 | #是否校验签名 10 | open.api.common.key.isCheckSign=false 11 | 12 | #开放接口公钥 13 | open.api.common.key.publicKey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwdK0le7UypaYWEWBoQkGTpu2nlYnM+iX8pa7Gz9neSnANfcuxrMgmmXrG+Dw6f3OQpiHl4mbKelyVjJTBLh4cvo1am2OSZvBjefZNshphx4ctBtx6BpGIRwlTvJRsjajMCY3RyF6px+Ehz0zeDBf7w2M6GZnSv2YhPp2YIZZo/01GYVJ4RgzzfkEEKyC+96+shqANHVOaiiG4byMJL8zv9q3kshSNCA1NT8r7toq8wPYhUKwCas/i5GauyRCIX+KhCpD9+/HTkFmr0PUWoNIZ61lRpTMbiTfDWU/5tJ3UPwdk6oVM3ZwkLoBAO8HXHvPk6avCupXq63u4wGrn30eiwIDAQAB 14 | 15 | #开放接口私钥 16 | open.api.common.key.privateKey=MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB0rSV7tTKlphYRYGhCQZOm7aeVicz6JfylrsbP2d5KcA19y7GsyCaZesb4PDp/c5CmIeXiZsp6XJWMlMEuHhy+jVqbY5Jm8GN59k2yGmHHhy0G3HoGkYhHCVO8lGyNqMwJjdHIXqnH4SHPTN4MF/vDYzoZmdK/ZiE+nZghlmj/TUZhUnhGDPN+QQQrIL73r6yGoA0dU5qKIbhvIwkvzO/2reSyFI0IDU1Pyvu2irzA9iFQrAJqz+LkZq7JEIhf4qEKkP378dOQWavQ9Rag0hnrWVGlMxuJN8NZT/m0ndQ/B2TqhUzdnCQugEA7wdce8+Tpq8K6lerre7jAauffR6LAgMBAAECggEBAIv3vF9V5KcUD5oXP6BqIvrbagp3zsGmoywVe7MWm4OdCeguw8HME6xME3fDflaL6cqf2bMuNTYUFnR2zQroqFrno3FjAlDXwPPYTT1JhyODNFlARIbHioNYjvyu8x5OZJRd1KdyXt+XXB5JrQSLcovwbiRZ5xf5gI3vTVMxUkSgTxf2P2smaXLZ2k6epSlvFr8u+SJaGOgjKCvbGf+jXyL0L8kntukNLocnSXU3sfFmAmd87DxPFdXAFDnS09tWOcjHfIZmwjHMX3qVP/2jj1DWOjIW0Ow/VRegbYTpLmSQTMcOUaFRprvwd0ZKaZ0aQMNPqPrqkHzrfQsnfjxY+akCgYEA+vbt40rIBDsN5HCPGbyDBU0/+A3wsGh4nqvY9JKACaMpg/FvyMz37GpL8AOMy/mUCVXjVyMoNUFZf/fEhMblBuYQBmgMVk1IQaVESvUlZ33Vgot0TU8YHY2Hpk541e3vKL0X0X6XLgS6CZ63cMx04uZxoFEWlJJm/qqLru3MQp8CgYEAxbZFVgnQ9XTQlHHgpUiS/R7qHo5joBDzlF+m29CYplI5nJUmntoChnHZ6RBoiPW58A3NJOlfIL6J2+Mwwd6kHWD2DSwVDRfk7Hb5Dw6o+tOf+In+zuPZtopr6L7oiKQtwXtGV88ZhDdqX9z5ge9EYP4Psd0Mfchv5vkJENreqJUCgYEAw8easTQHcXV4UvuURymOtLYc7zBA0f3OC0pYiAM5q0sD+hCBeg6cYmxSLT03u3BKEjZUkge1OEZwwanSPxrCVG1plvXYmgLUGZIKAsfXlDLQO3T7F8xaLcPZTN3u2kUxy4Aocp/k5Ft/nj2ZMX/ut4u6nKxlhyXm/0igi6irLlUCgYAWpalNkLRJ2Yam6mB0Llr/+ZGRzHem9yofndFMLpm9u39z6zXQTmKpqdLvOnzu607QK5SLHNxTsN+zu1NzcaBU6S1mFt2WcV08pOgkjGZYzPLvEkeIxVrD6RkxQOT7+epv1kIZftSKa5qYvoQqGRE5FwEPO6XZpqMCzxX1w0xr/QKBgDeom9MiI9a125jr/n9ghSWfvaxCXgPrdojr8QZrlo028iT711ND8QUwCbb9GDr+pyXesANCm78zhdltfeNFimEUktyS0F8Li2+GbYjTvLNXtwxTKZcOXRR+MC5bMHq4hY/+71NhnGazy3yidHn0doReezqGvkotJuRJSr+l1qmU -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/advice/ExceptionAdvice.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.advice; 2 | 3 | import com.open.api.enums.ApiExceptionEnum; 4 | import com.open.api.exception.BusinessException; 5 | import com.open.api.model.ResultModel; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 10 | import org.springframework.web.HttpRequestMethodNotSupportedException; 11 | import org.springframework.web.bind.MissingServletRequestParameterException; 12 | import org.springframework.web.bind.annotation.ControllerAdvice; 13 | import org.springframework.web.bind.annotation.CrossOrigin; 14 | import org.springframework.web.bind.annotation.ExceptionHandler; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.text.MessageFormat; 19 | 20 | /** 21 | * 统一异常处理 22 | * 23 | * @author 程序员小强 24 | */ 25 | @Slf4j 26 | @ControllerAdvice 27 | @EnableAspectJAutoProxy 28 | public class ExceptionAdvice { 29 | 30 | /** 31 | * 日志 32 | */ 33 | private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionAdvice.class); 34 | 35 | @CrossOrigin 36 | @ResponseBody 37 | @ExceptionHandler(value = Exception.class) 38 | public ResultModel defaultExceptionHandler(Exception exception, HttpServletResponse response) { 39 | ResultModel result; 40 | 41 | try { 42 | LOGGER.warn("全局业务处理异常 >> error = {}", exception.getMessage(), exception); 43 | throw exception; 44 | } catch (BusinessException e) { 45 | result = ResultModel.error(e.getCode(), e.getMsg()); 46 | 47 | } catch (HttpRequestMethodNotSupportedException e) { 48 | String errorMsg = MessageFormat.format(ApiExceptionEnum.INVALID_REQUEST_ERROR.getMsg(), e.getMethod(), e.getSupportedHttpMethods()); 49 | result = ResultModel.error(ApiExceptionEnum.INVALID_REQUEST_ERROR.getCode(), errorMsg); 50 | 51 | } catch (MissingServletRequestParameterException e) { 52 | String errorMsg = MessageFormat.format(ApiExceptionEnum.INVALID_PUBLIC_PARAM.getMsg(), e.getMessage()); 53 | result = ResultModel.error(ApiExceptionEnum.INVALID_PUBLIC_PARAM.getCode(), errorMsg); 54 | } catch (Exception e) { 55 | result = ResultModel.error(ApiExceptionEnum.SYSTEM_ERROR.getCode(), ApiExceptionEnum.SYSTEM_ERROR.getMsg()); 56 | 57 | } 58 | return result; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /open-api-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | open-api-project 7 | com.open.api 8 | 1.0.0 9 | 10 | 4.0.0 11 | 12 | open-api-common 13 | ${project.version} 14 | jar 15 | 16 | 17 | 18 | com.fasterxml.jackson.core 19 | jackson-databind 20 | 21 | 22 | org.apache.commons 23 | commons-lang3 24 | 25 | 26 | commons-collections 27 | commons-collections 28 | 29 | 30 | 31 | com.google.guava 32 | guava 33 | 34 | 35 | 36 | 37 | com.alibaba 38 | fastjson 39 | 40 | 41 | org.slf4j 42 | slf4j-api 43 | 44 | 45 | 46 | 47 | org.hibernate 48 | hibernate-validator 49 | 50 | 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | 56 | 57 | 58 | 59 | com.alipay.sdk 60 | alipay-sdk-java 61 | 62 | 63 | com.alibaba 64 | fastjson 65 | 66 | 67 | 68 | 69 | cn.hutool 70 | hutool-all 71 | 72 | 73 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/web/controller/OpenApiController.java: -------------------------------------------------------------------------------- 1 | package com.open.api.web.controller; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.open.api.config.gateway.ApiClient; 5 | import com.open.api.model.ResultModel; 6 | import com.open.api.util.SystemClock; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.RestController; 14 | import org.springframework.web.util.WebUtils; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import java.util.Map; 18 | 19 | /** 20 | * 统一网关 21 | */ 22 | @RestController 23 | @RequestMapping("/open") 24 | public class OpenApiController { 25 | 26 | private static final Logger LOGGER = LoggerFactory.getLogger(OpenApiController.class); 27 | 28 | @Autowired 29 | private ApiClient apiClient; 30 | 31 | 32 | /** 33 | * 统一网关入口 34 | * 35 | * @param method 请求方法 36 | * @param version 版本 37 | * @param apiRequestId 请求标识(用于日志中分辨是否是同一次请求) 38 | * @param charset 请求编码 39 | * @param signType 签名格式 40 | * @param sign 签名 41 | * @param content 业务内容参数 42 | * @author 程序员小强 43 | */ 44 | @PostMapping("/gateway") 45 | public ResultModel gateway(@RequestParam(value = "app_id", required = true) String appId, 46 | @RequestParam(value = "method", required = true) String method, 47 | @RequestParam(value = "version", required = true) String version, 48 | @RequestParam(value = "api_request_id", required = true) String apiRequestId, 49 | @RequestParam(value = "charset", required = true) String charset, 50 | @RequestParam(value = "sign_type", required = true) String signType, 51 | @RequestParam(value = "sign", required = true) String sign, 52 | @RequestParam(value = "content", required = true) String content, 53 | HttpServletRequest request) throws Throwable { 54 | 55 | Map params = WebUtils.getParametersStartingWith(request, ""); 56 | LOGGER.info("【{}】>> 网关执行开始 >> method={} params = {}", apiRequestId, method, JSON.toJSONString(params)); 57 | long start = SystemClock.millisClock().now(); 58 | 59 | //验签 60 | apiClient.checkSign(params, apiRequestId, charset, signType); 61 | 62 | //请求接口 63 | ResultModel result = apiClient.invoke(method, apiRequestId, content); 64 | 65 | LOGGER.info("【{}】>> 网关执行结束 >> method={},result = {}, times = {} ms", 66 | apiRequestId, method, JSON.toJSONString(result), (SystemClock.millisClock().now() - start)); 67 | 68 | return result; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /open-api-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | open-api-project 7 | com.open.api 8 | 1.0.0 9 | 10 | 11 | open-api-web 12 | ${project.version} 13 | Demo project for Spring Boot 14 | 15 | 16 | 17 | com.open.api 18 | open-api-common 19 | ${project.version} 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-aop 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-logging 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-logging 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-validation 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-log4j2 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-test 54 | 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-test 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-maven-plugin 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-compiler-plugin 72 | 2.3.2 73 | 74 | true 75 | true 76 | true 77 | 1.8 78 | 1.8 79 | UTF-8 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/gateway/ApiScanner.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.gateway; 2 | 3 | 4 | import com.open.api.annotation.OpenApi; 5 | import com.open.api.annotation.OpenApiService; 6 | import com.open.api.config.context.ApplicationContextHelper; 7 | import com.open.api.model.ApiModel; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.CommandLineRunner; 12 | import org.springframework.core.annotation.AnnotationUtils; 13 | import org.springframework.stereotype.Component; 14 | import org.springframework.util.ReflectionUtils; 15 | 16 | import javax.annotation.Resource; 17 | import java.lang.reflect.Method; 18 | import java.util.ArrayList; 19 | import java.util.Map; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * Api接口扫描器 26 | * 27 | * @author 程序员小强 28 | */ 29 | @Component 30 | public class ApiScanner implements CommandLineRunner { 31 | 32 | 33 | private static final Logger LOGGER = LoggerFactory.getLogger(ApiScanner.class); 34 | 35 | /** 36 | * 方法签名拆分正则 37 | */ 38 | private static final Pattern PATTERN = Pattern.compile("\\s+(.*)\\s+((.*)\\.(.*))\\((.*)\\)", Pattern.DOTALL); 39 | 40 | /** 41 | * 参数分隔符 42 | */ 43 | private static final String PARAMS_SEPARATOR = ","; 44 | 45 | /** 46 | * 统计扫描次数 47 | */ 48 | private AtomicInteger atomicInteger = new AtomicInteger(0); 49 | 50 | @Resource 51 | private ApiContainer apiContainer; 52 | 53 | @Override 54 | public void run(String... var1) throws Exception { 55 | //扫描所有使用@OpenApiService注解的类 56 | Map openApiServiceBeanMap = ApplicationContextHelper.getBeansWithAnnotation(OpenApiService.class); 57 | 58 | if (null == openApiServiceBeanMap || openApiServiceBeanMap.isEmpty()) { 59 | LOGGER.info("open api service bean map is empty"); 60 | return; 61 | } 62 | 63 | for (Map.Entry map : openApiServiceBeanMap.entrySet()) { 64 | //获取扫描类下所有方法 65 | Method[] methods = ReflectionUtils.getAllDeclaredMethods(map.getValue().getClass()); 66 | for (Method method : methods) { 67 | atomicInteger.incrementAndGet(); 68 | //找到带有OpenApi 注解的方法 69 | OpenApi openApi = AnnotationUtils.findAnnotation(method, OpenApi.class); 70 | if (null == openApi) { 71 | continue; 72 | } 73 | //获取业务参数对象 74 | String paramName = getParamName(method); 75 | if (StringUtils.isBlank(paramName)) { 76 | LOGGER.warn("Api接口业务参数缺失 >> method = {}", openApi.method()); 77 | continue; 78 | } 79 | 80 | //组建ApiModel- 放入api容器 81 | apiContainer.put(openApi.method(), new ApiModel(map.getKey(), method, paramName)); 82 | LOGGER.info("Api接口加载成功 >> method = {} , desc={}", openApi.method(), openApi.desc()); 83 | } 84 | } 85 | LOGGER.info("Api接口容器加载完毕 >> size = {} loopTimes={}", apiContainer.size(), atomicInteger.get()); 86 | } 87 | 88 | /** 89 | * 获取业务参数对象 90 | * 91 | * @param method 92 | * @return 93 | */ 94 | private String getParamName(Method method) { 95 | ArrayList result = new ArrayList<>(); 96 | final Matcher matcher = PATTERN.matcher(method.toGenericString()); 97 | if (matcher.find()) { 98 | int groupCount = matcher.groupCount() + 1; 99 | for (int i = 0; i < groupCount; i++) { 100 | result.add(matcher.group(i)); 101 | } 102 | } 103 | 104 | //获取参数部分 105 | if (result.size() >= 6) { 106 | String[] params = 107 | StringUtils.splitByWholeSeparatorPreserveAllTokens(result.get(5), PARAMS_SEPARATOR); 108 | if (params.length >= 2) { 109 | return params[1]; 110 | } 111 | } 112 | return null; 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | com.open.api 8 | open-api-project 9 | 1.0.0 10 | pom 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.2.5.RELEASE 16 | 17 | 18 | 19 | 20 | open-api-common 21 | open-api-web 22 | 23 | 24 | 25 | 1.0.0 26 | UTF-8 27 | UTF-8 28 | 2.10.2 29 | 3.4 30 | 3.2.2 31 | 1.2.70 32 | 1.7.25 33 | 21.0 34 | 3.1.0 35 | 4.8.10.ALL 36 | 5.0.0 37 | 1.16.4 38 | 5.4.1.Final 39 | 40 | 41 | 42 | 43 | 44 | 45 | com.fasterxml.jackson.core 46 | jackson-databind 47 | ${fasterxml.version} 48 | 49 | 50 | org.apache.commons 51 | commons-lang3 52 | ${commons-lang3.version} 53 | 54 | 55 | commons-collections 56 | commons-collections 57 | ${commons-collections.version} 58 | 59 | 60 | 61 | 62 | com.alibaba 63 | fastjson 64 | ${fastjson.version} 65 | 66 | 67 | 68 | org.slf4j 69 | slf4j-api 70 | ${slf4j.version} 71 | 72 | 73 | 74 | 75 | com.google.guava 76 | guava 77 | ${guava.version} 78 | 79 | 80 | 81 | 82 | org.hibernate 83 | hibernate-validator 84 | ${hibernate-validator.version} 85 | 86 | 87 | 88 | com.alipay.sdk 89 | alipay-sdk-java 90 | ${alipay-sdk.version} 91 | 92 | 93 | 94 | cn.hutool 95 | hutool-all 96 | ${hutool.version} 97 | 98 | 99 | 100 | 101 | org.projectlombok 102 | lombok 103 | ${lombok.version} 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /open-api-web/src/test/java/com/open/api/sign/GetWayRequestDemo.java: -------------------------------------------------------------------------------- 1 | package com.open.api.sign; 2 | 3 | import cn.hutool.http.HttpUtil; 4 | import com.alipay.api.internal.util.AlipaySignature; 5 | import org.junit.Test; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.TreeMap; 10 | 11 | /** 12 | * @author 程序员小强 13 | */ 14 | public class GetWayRequestDemo { 15 | 16 | /** 17 | * 私钥 18 | */ 19 | private static final String PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB0rSV7tTKlphYRYGhCQZOm7aeVicz6JfylrsbP2d5KcA19y7GsyCaZesb4PDp/c5CmIeXiZsp6XJWMlMEuHhy+jVqbY5Jm8GN59k2yGmHHhy0G3HoGkYhHCVO8lGyNqMwJjdHIXqnH4SHPTN4MF/vDYzoZmdK/ZiE+nZghlmj/TUZhUnhGDPN+QQQrIL73r6yGoA0dU5qKIbhvIwkvzO/2reSyFI0IDU1Pyvu2irzA9iFQrAJqz+LkZq7JEIhf4qEKkP378dOQWavQ9Rag0hnrWVGlMxuJN8NZT/m0ndQ/B2TqhUzdnCQugEA7wdce8+Tpq8K6lerre7jAauffR6LAgMBAAECggEBAIv3vF9V5KcUD5oXP6BqIvrbagp3zsGmoywVe7MWm4OdCeguw8HME6xME3fDflaL6cqf2bMuNTYUFnR2zQroqFrno3FjAlDXwPPYTT1JhyODNFlARIbHioNYjvyu8x5OZJRd1KdyXt+XXB5JrQSLcovwbiRZ5xf5gI3vTVMxUkSgTxf2P2smaXLZ2k6epSlvFr8u+SJaGOgjKCvbGf+jXyL0L8kntukNLocnSXU3sfFmAmd87DxPFdXAFDnS09tWOcjHfIZmwjHMX3qVP/2jj1DWOjIW0Ow/VRegbYTpLmSQTMcOUaFRprvwd0ZKaZ0aQMNPqPrqkHzrfQsnfjxY+akCgYEA+vbt40rIBDsN5HCPGbyDBU0/+A3wsGh4nqvY9JKACaMpg/FvyMz37GpL8AOMy/mUCVXjVyMoNUFZf/fEhMblBuYQBmgMVk1IQaVESvUlZ33Vgot0TU8YHY2Hpk541e3vKL0X0X6XLgS6CZ63cMx04uZxoFEWlJJm/qqLru3MQp8CgYEAxbZFVgnQ9XTQlHHgpUiS/R7qHo5joBDzlF+m29CYplI5nJUmntoChnHZ6RBoiPW58A3NJOlfIL6J2+Mwwd6kHWD2DSwVDRfk7Hb5Dw6o+tOf+In+zuPZtopr6L7oiKQtwXtGV88ZhDdqX9z5ge9EYP4Psd0Mfchv5vkJENreqJUCgYEAw8easTQHcXV4UvuURymOtLYc7zBA0f3OC0pYiAM5q0sD+hCBeg6cYmxSLT03u3BKEjZUkge1OEZwwanSPxrCVG1plvXYmgLUGZIKAsfXlDLQO3T7F8xaLcPZTN3u2kUxy4Aocp/k5Ft/nj2ZMX/ut4u6nKxlhyXm/0igi6irLlUCgYAWpalNkLRJ2Yam6mB0Llr/+ZGRzHem9yofndFMLpm9u39z6zXQTmKpqdLvOnzu607QK5SLHNxTsN+zu1NzcaBU6S1mFt2WcV08pOgkjGZYzPLvEkeIxVrD6RkxQOT7+epv1kIZftSKa5qYvoQqGRE5FwEPO6XZpqMCzxX1w0xr/QKBgDeom9MiI9a125jr/n9ghSWfvaxCXgPrdojr8QZrlo028iT711ND8QUwCbb9GDr+pyXesANCm78zhdltfeNFimEUktyS0F8Li2+GbYjTvLNXtwxTKZcOXRR+MC5bMHq4hY/+71NhnGazy3yidHn0doReezqGvkotJuRJSr+l1qmU"; 20 | 21 | private Map getObjParamMap() { 22 | //创建参数 23 | Map paramMap = new HashMap<>(); 24 | paramMap.put("app_id", "1001"); 25 | paramMap.put("method", "open.api.test.one.method1"); 26 | paramMap.put("version", "1.0"); 27 | paramMap.put("api_request_id", "11111111111"); 28 | paramMap.put("charset", "utf-8"); 29 | paramMap.put("sign_type", "RSA2"); 30 | paramMap.put("content", "{\"item_list\":[{\"password\":\"123\",\"real_name\":\"张三\",\"username\":\"张三\"}]}"); 31 | return paramMap; 32 | } 33 | 34 | /** 35 | * 生成签名测试 36 | */ 37 | @Test 38 | public void genSignTest() { 39 | //参数 40 | Map objParamMap = this.getObjParamMap(); 41 | System.out.println("加签结果:"); 42 | System.out.println(this.getSign(objParamMap)); 43 | } 44 | 45 | /** 46 | * Api 请求示例 47 | */ 48 | @Test 49 | public void getWayRequestDemo() { 50 | //参数 51 | Map objParamMap = this.getObjParamMap(); 52 | //签名 53 | String sign = this.getSign(objParamMap); 54 | System.out.println("加签结果:"); 55 | System.out.println(sign); 56 | 57 | //添加签名到参数 58 | objParamMap.put("sign", sign); 59 | System.out.println("入参内容:"); 60 | System.out.println(objParamMap); 61 | String result = HttpUtil.post("http://localhost:8821/open/gateway", objParamMap,10000); 62 | 63 | System.out.println("返回结果:"); 64 | System.out.println(result); 65 | } 66 | 67 | /** 68 | * 加签 69 | * 70 | * @param paramMap 71 | * @return 72 | */ 73 | private String getSign(Map paramMap) { 74 | //map类型转换 75 | Map map = new HashMap<>(paramMap.size()); 76 | for (String s : paramMap.keySet()) { 77 | map.put(s, paramMap.get(s).toString()); 78 | } 79 | 80 | try { 81 | TreeMap signMap = new TreeMap<>(); 82 | signMap.putAll(map); 83 | 84 | String charset = signMap.get("charset"); 85 | String signType = signMap.get("sign_type"); 86 | 87 | //待签名串 88 | signMap.remove("sign_type"); 89 | signMap.remove("sign"); 90 | String content = AlipaySignature.getSignContent(signMap); 91 | //签名 92 | return AlipaySignature.rsaSign(content, PRIVATE_KEY, charset, signType); 93 | } catch (Exception e) { 94 | throw new RuntimeException("加签异常", e); 95 | } 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /open-api-web/src/main/java/com/open/api/config/gateway/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.open.api.config.gateway; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alipay.api.internal.util.AlipaySignature; 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 8 | import com.open.api.config.context.ApplicationContextHelper; 9 | import com.open.api.config.property.ApplicationProperty; 10 | import com.open.api.enums.ApiExceptionEnum; 11 | import com.open.api.exception.BusinessException; 12 | import com.open.api.model.ApiModel; 13 | import com.open.api.model.ResultModel; 14 | import com.open.api.util.ValidateUtils; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.apache.commons.lang3.exception.ExceptionUtils; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.stereotype.Service; 20 | 21 | import javax.annotation.Resource; 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | import java.util.TreeMap; 26 | 27 | /** 28 | * Api请求客户端 29 | * 30 | * @author 程序员小强 31 | */ 32 | @Service 33 | public class ApiClient { 34 | 35 | /** 36 | * 日志 37 | */ 38 | private static final Logger LOGGER = LoggerFactory.getLogger(ApiClient.class); 39 | 40 | /** 41 | * jackson 序列化工具类 42 | */ 43 | private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); 44 | 45 | /** 46 | * Api本地容器 47 | */ 48 | private final ApiContainer apiContainer; 49 | 50 | public ApiClient(ApiContainer apiContainer) { 51 | this.apiContainer = apiContainer; 52 | } 53 | 54 | 55 | @Resource 56 | private ApplicationProperty applicationProperty; 57 | 58 | /** 59 | * 验签 60 | * 61 | * @param params 请求参数 62 | * @param requestRandomId 请求随机标识(用于日志中分辨是否是同一次请求) 63 | * @param charset 请求编码 64 | * @param signType 签名格式 65 | * @author 程序员小强 66 | */ 67 | public void checkSign(Map params, String requestRandomId, String charset, String signType) { 68 | 69 | try { 70 | //校验签名开关 71 | if (!applicationProperty.getIsCheckSign()) { 72 | LOGGER.warn("【{}】>> 验签开关关闭", requestRandomId); 73 | return; 74 | } 75 | 76 | //map类型转换 77 | Map map = new HashMap<>(params.size()); 78 | for (String s : params.keySet()) { 79 | map.put(s, params.get(s).toString()); 80 | } 81 | 82 | LOGGER.warn("【{}】 >> 验签参数 {}", requestRandomId, map); 83 | boolean checkSign = AlipaySignature.rsaCheckV1(map, applicationProperty.getPublicKey(), charset, signType); 84 | if (!checkSign) { 85 | LOGGER.info("【{}】 >> 验签失败 >> params = {}", requestRandomId, JSON.toJSONString(params)); 86 | throw new BusinessException(ApiExceptionEnum.INVALID_SIGN); 87 | } 88 | LOGGER.warn("【{}】 >> 验签成功", requestRandomId); 89 | 90 | } catch (Exception e) { 91 | LOGGER.error("【{}】 >> 验签异常 >> params = {}, error = {}", 92 | requestRandomId, JSON.toJSONString(params), ExceptionUtils.getStackTrace(e)); 93 | throw new BusinessException(ApiExceptionEnum.INVALID_SIGN); 94 | 95 | } 96 | 97 | } 98 | 99 | 100 | 101 | /** 102 | * Api调用方法 103 | * 104 | * @param method 请求方法 105 | * @param requestRandomId 请求随机标识 106 | * @param content 请求体 107 | * @author 程序员小强 108 | */ 109 | public ResultModel invoke(String method, String requestRandomId, String content) throws Throwable { 110 | //获取api方法 111 | ApiModel apiModel = apiContainer.get(method); 112 | 113 | if (null == apiModel) { 114 | LOGGER.info("【{}】 >> API方法不存在 >> method = {}", requestRandomId, method); 115 | throw new BusinessException(ApiExceptionEnum.API_NOT_EXIST); 116 | } 117 | 118 | //获得spring bean 119 | Object bean = ApplicationContextHelper.getBean(apiModel.getBeanName()); 120 | if (null == bean) { 121 | LOGGER.warn("【{}】 >> API方法不存在 >> method = {}, beanName = {}", requestRandomId, method, apiModel.getBeanName()); 122 | throw new BusinessException(ApiExceptionEnum.API_NOT_EXIST); 123 | } 124 | 125 | //处理业务参数 126 | // 忽略JSON字符串中存在,而在Java中不存在的属性 127 | JSON_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 128 | // 设置下划线序列化方式 129 | JSON_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); 130 | Object result = JSON_MAPPER.readValue(StringUtils.isBlank(content) ? "{}" : content, Class.forName(apiModel.getParamName())); 131 | 132 | //校验参数 133 | ValidateUtils.validate(result); 134 | 135 | //执行对应方法 136 | try { 137 | Object obj = apiModel.getMethod().invoke(bean, requestRandomId, result); 138 | return ResultModel.success(obj); 139 | } catch (Exception e) { 140 | if (e instanceof InvocationTargetException) { 141 | throw ((InvocationTargetException) e).getTargetException(); 142 | } 143 | throw new BusinessException(ApiExceptionEnum.SYSTEM_ERROR); 144 | } 145 | 146 | } 147 | 148 | 149 | } 150 | --------------------------------------------------------------------------------