一个基于SpringBoot的签名组件

本文档详细介绍了如何使用一个基于SpringBoot的API签名组件,该组件支持application/x-www-form-urlencoded和application/json两种传参方式的签名验证。组件通过配置属性类、自动装配类、签名帮助类等实现签名功能,并提供了从配置文件或自定义数据源获取签名密钥的能力。在使用时,只需引入组件并配置相关属性,即可在API调用中进行签名验证。
摘要由CSDN通过智能技术生成

这是一个集成了API签名功能的spring boot组件,提供了application/x-www-form-urlencoded、application/json两种传参方式的签名验证功能,只需要在application.yml中做简单配置即可开箱即用。下面介绍其主要代码和使用方法。


项目结构如下:
在这里插入图片描述
Github地址: https://github.com/xydang2019/sign-spring-boot-starter.git

1、主要代码

1.1、第三方依赖

<?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>

  <groupId>com.bzyd.sign</groupId>
  <artifactId>sign-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>sign-spring-boot-starter</name>

  <properties>
    <java.version>1.8</java.version>
    <fastjson.version>1.2.67</fastjson.version>
    <commons-codec.version>1.13</commons-codec.version>
    <lombok.version>1.18.12</lombok.version>
    <commons-lang3.version>3.9</commons-lang3.version>
  </properties>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath/>
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>${fastjson.version}</version>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>${commons-codec.version}</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>${commons-lang3.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!--打包时忽略启动类-->
        <configuration>
          <skip>true</skip>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

1.2、配置属性类

import static com.bzyd.sign.common.constants.SignConstant.CONFIG_PREFIX;
@Data
@ConfigurationProperties(prefix = CONFIG_PREFIX)
public class SignProperties {

    /**
     * 是否启用
     */
    private boolean enabled;

    /**
     * 默认Content-Type,
     * 1、application/x-www-form-urlencoded
     * 2、application/json
     */
    private String defaultContentType = SignEnum.FORM.getTag();

    /**
     * 验证路径
     */
    private String[] includePaths;

    /**
     * 排除路径
     */
    private String[] excludePaths;

    /**
     * 签名密钥
     */
    @NestedConfigurationProperty
    private List<SignSecretConfig> secret;
}

配置前缀:

public class SignConstant {
    public static final String CONFIG_PREFIX = "com.bzyd.sign";
}

签名密钥类:

@Data
@Accessors(chain = true)
public class SignSecretConfig {

    /**
     * 客户端id
     */
    private String appId;

    /**
     * 签名密钥
     */
    private String secretKey;
}

1.3、自动装配类

自动装配类中主要配置签名拦截器以及请求体读取过滤器(之所以配置此过滤器,是因为以application/json的传参方式时,InputStream只能读取一次,详见下方请求包装器类RequestWrapper详解)。

import static com.bzyd.sign.common.constants.SignConstant.CONFIG_PREFIX;

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(SignInterceptor.class)
@EnableConfigurationProperties(SignProperties.class)
@ConditionalOnProperty(prefix = CONFIG_PREFIX,value = "enabled",havingValue = "true")
public class SignAutoConfigure implements WebMvcConfigurer {

    @Autowired
    private SignProperties signProperties;

    @Autowired(required = false)
    private SignHelper signHelper;

    @Autowired(required = false)
    private List<SignSecretConfigHolder> signSecretConfigHolders;

    //配置签名拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        if (signHelper == null){
            signHelper = new DefaultSignHelper();
        }

        if (signSecretConfigHolders == null){
            signSecretConfigHolders = new ArrayList<>();
        }
        signSecretConfigHolders.add(new DefaultSignSecretConfigHolder(signProperties.getSecret()));

        registry.addInterceptor(new SignInterceptor(signProperties.getDefaultContentType(),signHelper,signSecretConfigHolders))
                .addPathPatterns(signProperties.getIncludePaths())
                .excludePathPatterns(signProperties.getExcludePaths());
    }


    //配置请求体读取过滤器
    @Bean
    public FilterRegistrationBean getFilterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new RequestBodyReadFilter(signProperties.getExcludePaths()));
        bean.setName("RequestBodyReadFilter");
        String[] strArr = signProperties.getIncludePaths();
        for (int i = 0; i < strArr.length; i++) {
            strArr[i] = strArr[i].replace("**","*");
        }
        bean.addUrlPatterns(strArr);
        return bean;
    }
}

META-INF/spring.factories中写入自动装配类:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bzyd.sign.autoconfigure.SignAutoConfigure

1.4、签名帮助类

public interface SignHelper {

    /**
     * 获取签名
     * @param body 参数
     * @param signKey 密钥
     * @return
     */
    String getSign(JSONObject body, String signKey);

    /**
     * 验证签名
     * @param params 参数
     * @param signKey 密钥
     * @return
     */
    boolean checkSign(JSONObject params, String signKey);
}

此接口主要提供了获取签名以及签名两个方法,需要自定义签名规则的,可以实现此接口即可。另外本组件提供了一套默认的签名规则,详见默认签名帮助类:

public class DefaultSignHelper implements SignHelper {

    private DateTimeFormatter df1;
    private DateTimeFormatter df2;

    public DefaultSignHelper() {
        df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        df2 = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
    }

    @Override
    public String getSign(JSONObject body, String signKey) {

        //对参数进行ASCII码从小到大排序(字典序)
        List<Map.Entry<String, Object>> akeys = new ArrayList<>(body.entrySet());
        Collections.sort(akeys,new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
                return o1.getKey().compareTo(o2.getKey());
            }
        });

        StringBuffer sb = new StringBuffer();
        sb.append(signKey + ":");
        for(Map.Entry<String, Object> item : akeys){
            String key = item.getKey();
            Object val = item.getValue();

            if(null != val && !"".equals(val.toString())
                    && !"sign".equals(key)) {
                sb.append(key + ":" + val.toString() + ":");
            }
        }

        sb.append(signKey);
        String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
        return sign;
    }

    @Override
    public boolean checkSign(JSONObject params, String signKey) {
        try {
            if(params.get("sign") != null) {
                String sign = params.getString("sign");
                String time = params.getString("time");
                String bodyStr = params.getString("body");
                JSONObject body = JSON.parseObject(bodyStr);

                String signTime = df2.format(df1.parse(time));
                body.put("signTime", signTime);

                String _sign = getSign(body,signKey);
                if(_sign.equals(sign)){
                    return true;
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }
}

默认签名规则:
1、设接口所有请求内容或请求处理结果(body)的数据为集合M,向集合M添加字段signTime,其值为请求时间(格式:yyyyMMddhhmm,精准到分)。
2、将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),按照key:value的格式生成键值对(即key1:value1:key2:value2:key3:value3…)拼接成字符串stringA。
3、在stringA最前与最后都拼接上秘钥signKey(分配得到)得到stringB字符串(signKey:key1:value1:key2:value2…key9:value9:signKey),并对stringB进行MD5运算,再将得到的字符串所有字符转换为大写,得到最终sign值。

注意:
① 参数名ASCII码从小到大排序(字典序);
② 如果参数的值为空不参与签名;
③ 参数名区分大小写;
④ 传送的sign参数不参与签名。

1.5、签名密钥持有器类

public interface SignSecretConfigHolder {

    /**
     * 获取签名密钥配置信息
     * @return
     */
    List<SignSecretConfig> getSignSecretConfig();
}

该接口的作用是获取签名密钥,需要自定义密钥获取方式(如从数据库获取)的实现此接口即可,本组件默认提供从配置文件获取的方式:
在application.yml配置密钥信息如下

com:
  bzyd:
    sign:
      enabled: true
      default-content-type: application/json
      include-paths: /test/**
      exclude-paths:
      secret:
        - app-id: 1
          secret-key: bzyd111
        - app-id: 2
          secret-key: bzyd222

默认签名密钥持有器类:

public class DefaultSignSecretConfigHolder implements SignSecretConfigHolder {

    private List<SignSecretConfig> signSecretConfigList;

    public DefaultSignSecretConfigHolder(List<SignSecretConfig> signSecretConfigList) {
        this.signSecretConfigList = signSecretConfigList;
    }

    @Override
    public List<SignSecretConfig> getSignSecretConfig() {
        return this.signSecretConfigList;
    }
}

若需从数据库中获取密钥配置,可实现SignSecretConfigHolder接口,例如:

@Component
public class CustomSignSecretConfigHolder implements SignSecretConfigHolder {
    @Override
    public List<SignSecretConfig> getSignSecretConfig() {
        //自定义签名密钥持有器,可从缓存或者数据库中获取密钥
        //以下是伪代码
        return new ArrayList<SignSecretConfig>() {
            {
                add(new SignSecretConfig().setAppId("1").setSecretKey("bzyd111"));
                add(new SignSecretConfig().setAppId("2").setSecretKey("bzyd222"));
                add(new SignSecretConfig().setAppId("3").setSecretKey("bzyd333"));
            }
        };
    }
}

1.6、签名拦截器类

public class SignInterceptor implements HandlerInterceptor {

    //默认Content-Type
    private String defaultContentType;

    //签名帮助类
    private SignHelper signHelper;

    //签名密钥,key为appId,value为secretKey
    private Map<String, String> signSecretMap;

    public SignInterceptor(String defaultContentType, SignHelper signHelper, List<SignSecretConfigHolder> signSecretConfigHolders) {
        this.defaultContentType = defaultContentType;
        this.signHelper = signHelper;
        this.signSecretMap = new HashMap<>();
        if (signSecretConfigHolders != null && !signSecretConfigHolders.isEmpty()) {
            signSecretConfigHolders.forEach(signSecretConfigHolder -> {
                List<SignSecretConfig> signSecretConfigList = signSecretConfigHolder.getSignSecretConfig();
                if (signSecretConfigList != null && !signSecretConfigList.isEmpty()) {
                    for (SignSecretConfig signSecretConfig : signSecretConfigList) {
                        this.signSecretMap.put(signSecretConfig.getAppId(), signSecretConfig.getSecretKey());
                    }
                }
            });
        }
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
            AuthPassport authPassport = ((HandlerMethod) handler).getMethodAnnotation(AuthPassport.class);
            JSONObject params = null;//请求参数

            if (authPassport != null){
                if (authPassport.type().equals(SignEnum.FORM)) {
                    params = RequestUtil.getFormReqParams(request);
                }

                if (authPassport.type().equals(SignEnum.JSON)) {
                    params = RequestUtil.getJsonReqParams(request);
                }

            }else {
                if (SignEnum.FORM.getTag().equals(defaultContentType)) {
                    params = RequestUtil.getFormReqParams(request);
                }

                if (SignEnum.JSON.getTag().equals(defaultContentType)) {
                    params = RequestUtil.getJsonReqParams(request);
                }
            }

            if (params == null){
                throw new RuntimeException("Failed to get request parameters...");
            }

            String appId = params.getString("appId");
            String secretKey = this.signSecretMap.get(appId);
            if (signHelper.checkSign(params, secretKey)) {
                return true;
            } else {
                throw new SignException();
            }
        }
        return false;
    }
}

1.7、签名枚举类

public enum SignEnum {

    /**
     * 以application/x-www-form-urlencoded的形式传输
     */
    FORM("application/x-www-form-urlencoded"),

    /**
     * 以application/json的形式传输
     */
    JSON("application/json");

    private final String tag;

    SignEnum(String tag) {
        this.tag = tag;
    }

    public String getTag() {
        return tag;
    }
}

本组件提供两种传参方式的签名认证,即application/x-www-form-urlencoded表单形式和application/json形式,可通过default-content-type属性进行配置,当不配置时默认的形式是application/x-www-form-urlencoded。
另外,本组件也提供了授权注解类:

@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthPassport {
    SignEnum type();
}

API上若使用了此注解,则优先按此注解中设置的content-type进行校验,当没有使用此注解时则按照配置文件中设置的默认content-type进行校验(具体代码详见上方签名拦截器类)。

@AuthPassport(type = SignEnum.FORM)
@RequestMapping(value = "/testPostForm",method = RequestMethod.POST)
public Object testPostForm(ApiParamsForm params){
    UserVO userVO = params.getBody(UserVO.class);
    System.out.println("测试form形式提交的post请求,userVO: " + userVO);
    return ok();
}

1.8、请求包装器类

由于请求数据流只能被读取一次,在拦截器读取了request的数据,在Controller里面@RequestBody注解获取Json就会失败就读取不到数据。解决方法就是通过重写HttpServletRequestWrapper把request的流保存下来,然后通过过滤器把保存下来的request再填充进去,这样就可以多次读取request了,此方法只对application/json方式提交的请求有效。

public class RequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }

    /**
     * 获取请求Body
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    /**
     * Description: 复制输入流
     */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

1.9、请求体过滤器类

public class RequestBodyReadFilter implements Filter {

    /**
     * 过滤路径列表
     */
    private List<String> excludeList;

    /**
     * Ant匹配模式
     */
    private AntPathMatcher pathMatcher;

    public RequestBodyReadFilter(String[] excludePaths) {
        this.excludeList = Arrays.asList(excludePaths);
        this.pathMatcher = new AntPathMatcher();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 读取流后,将流继续写出去
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        if (!isExcludeUri(httpServletRequest)) {
            // 这里将原始request传入,读出流并存储
            ServletRequest requestWrapper = new RequestWrapper(httpServletRequest);
            // 这里将原始request替换为包装后的request,此后所有进入controller的request均为包装后的
            filterChain.doFilter(requestWrapper, servletResponse);//
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    /**
     * 判断是否排除掉过滤
     *
     * @param httpServletRequest
     * @return
     */
    private boolean isExcludeUri(HttpServletRequest httpServletRequest) {
        String contextPath = httpServletRequest.getContextPath();
        String uri = httpServletRequest.getRequestURI();
        String urlStr = uri.replace(contextPath, "");

        for (String pattern : this.excludeList) {
            if (pathMatcher.match(pattern, urlStr)) {
                return true;
            }
        }

        return false;
    }
}

1.10、请求工具类

public class RequestUtil {

    /**
     * 获取请求参数(application/x-www-form-urlencoded方式)
     *
     * @param request
     * @return
     */
    public static JSONObject getFormReqParams(HttpServletRequest request) {

        Map properties = request.getParameterMap();
        JSONObject result = new JSONObject();
        Iterator entries = properties.entrySet().iterator();
        Entry entry;
        String name = "";
        String value = "";
        while (entries.hasNext()) {
            entry = (Entry) entries.next();
            name = (String) entry.getKey();

            Object valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else if (valueObj instanceof String[]) {
                String[] values = (String[]) valueObj;
                for (int i = 0; i < values.length; i++) {
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length() - 1);
            } else {
                value = valueObj.toString();
            }

            result.put(name, value);
        }
        return result;
    }

    /**
     * 获取请求参数(application/json方式)
     *
     * @param request
     * @return
     */
    public static JSONObject getJsonReqParams(HttpServletRequest request) {
        JSONObject params = null;
        try {
            params = JSON.parseObject(request.getInputStream(), Charset.forName("UTF-8"), JSONObject.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return params;
    }
}

2、使用方法

2.1、引入组件

<dependency>
  <groupId>com.bzyd.sign</groupId>
  <artifactId>sign-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

2.2、配置属性

com:
  bzyd:
    sign:
      enabled: true
      default-content-type: application/json
      include-paths: /test/**
      exclude-paths:
#      secret:
#        - app-id: 1
#          secret-key: bzyd111
#        - app-id: 2
#          secret-key: bzyd222

2.3、测试请求

@RestController
@RequestMapping("/test")
public class TestController extends BaseController {

    @AuthPassport(type = SignEnum.FORM)
    @RequestMapping(value = "/testPostForm",method = RequestMethod.POST)
    public Object testPostForm(ApiParamsForm params){
        UserVO userVO = params.getBody(UserVO.class);
        System.out.println("测试form形式提交的post请求,userVO: " + userVO);
        return ok();
    }

    @AuthPassport(type = SignEnum.JSON)
    @RequestMapping(value = "/testPostJson",method = RequestMethod.POST)
    public Object testPostJson(@RequestBody ApiParamsJson<UserVO> params){
        UserVO userVO = params.getBody();
        System.out.println("测试json形式提交的post请求,userVO: " + userVO);
        return ok();
    }
}

UserVO类:

@Data
@Accessors(chain = true)
public class UserVO {
    private Integer id;
    private String name;
}

ApiParamsForm、ApiParamsJson是组件中提供的两种传参方式的参数接收对象:

@Data
public class ApiParamsForm implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 签名
     */
    private String sign;

    /**
     * 时间字符串
     */
    private String time;

    /**
     * 请求参数
     */
    private String body;

    /**
     * 客户端id
     */
    private String appId;

    public <T>T getBody(Class<T> t) {
        return JSON.parseObject(body,t);
    }
}
@Data
public class ApiParamsJson<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 签名
     */
    private String sign;

    /**
     * 时间字符串
     */
    private String time;

    /**
     * 请求参数
     */
    private T body;

    /**
     * 客户端id
     */
    private String appId;
}

2.4、调用API

客户端类:

@Slf4j
public class TestSignSpringBootStarterDefaultClient {
    private String baseUrl;
    private String appId;
    private String secretKey;

    private TestSignSpringBootStarterDefaultClient(String baseUrl, String appId, String secretKey) {
        this.baseUrl = baseUrl;
        this.appId = appId;
        this.secretKey = secretKey;
    }

    private static volatile TestSignSpringBootStarterDefaultClient client = null;

    public static TestSignSpringBootStarterDefaultClient getInstance(String baseUrl, String appId, String secretKey){
        if (client == null){
            synchronized (TestSignSpringBootStarterDefaultClient.class){
                if (client == null){
                    client = new TestSignSpringBootStarterDefaultClient(baseUrl,appId,secretKey);
                }
            }
        }
        return client;
    }

    public TestSignSpringBootStarterResponse doPostJson(TestSignSpringBootStarterRequest request){
        try {
            DateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            DateFormat sdf2 = new SimpleDateFormat("yyyyMMddHHmm");

            JSONObject body = request.getBody();
            if (body != null) {
                Date now = new Date();
                body.put("signTime", sdf2.format(now));
                String sign = SignUtil.sign(body, secretKey);
                body.remove("signTime");

                JSONObject params = new JSONObject();
                params.put("body", body);
                params.put("time", sdf1.format(now));
                params.put("sign", sign);
                params.put("appId", appId);

                log.info("调用接口【" + request.getMethod() + "】请求参数:" + params.toString());
                String result = HttpConnectionPoolUtil.post(baseUrl + request.getMethod(), JSON.toJSONString(params));

                if (StringUtils.isNotEmpty(result)) {
                    log.info("调用接口【" + request.getMethod() + "】成功,返回结果:" + result);
                    return JSONObject.parseObject(result, TestSignSpringBootStarterResponse.class);
                } else {
                    log.info("调用接口【" + request.getMethod() + "】失败,result is null");
                }
            } else {
                log.info("调用接口【" + request.getMethod() + "】失败,body is null");
            }
        } catch (Exception e) {
            log.error("Exception", e);
        }

        return null;
    }

    public TestSignSpringBootStarterResponse doPostForm(TestSignSpringBootStarterRequest request){
        try {
            DateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            DateFormat sdf2 = new SimpleDateFormat("yyyyMMddHHmm");

            JSONObject body = request.getBody();
            if (body != null) {
                Date now = new Date();
                body.put("signTime", sdf2.format(now));
                String sign = SignUtil.sign(body, secretKey);
                body.remove("signTime");

                JSONObject params = new JSONObject();
                params.put("body", JSON.toJSONString(body));
                params.put("time", sdf1.format(now));
                params.put("sign", sign);
                params.put("appId", appId);

                log.info("调用接口【" + request.getMethod() + "】请求参数:" + params.toString());
                String result = HttpConnectionPoolUtil.post(baseUrl + request.getMethod(), params);

                if (StringUtils.isNotEmpty(result)) {
                    log.info("调用接口【" + request.getMethod() + "】成功,返回结果:" + result);
                    return JSONObject.parseObject(result, TestSignSpringBootStarterResponse.class);
                } else {
                    log.info("调用接口【" + request.getMethod() + "】失败,result is null");
                }
            } else {
                log.info("调用接口【" + request.getMethod() + "】失败,body is null");
            }
        } catch (Exception e) {
            log.error("Exception", e);
        }

        return null;
    }

}

测试方法:

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestSignSpringBootStarter {
    @Test
    public void test1() throws Exception{
        String baseUrl = "http://localhost:8080";
        String appId = "1";
        String secretKey = "bzyd111";
        TestSignSpringBootStarterDefaultClient client = TestSignSpringBootStarterDefaultClient.getInstance(baseUrl,appId,secretKey);

        JSONObject body = new JSONObject();
        body.put("id", "100");
        body.put("name", "Tom");
        TestSignSpringBootStarterRequest request = new TestSignSpringBootStarterRequest("/test/testPostForm", body);
        TestSignSpringBootStarterResponse response = client.doPostForm(request);
        System.out.println(response);
    }

    @Test
    public void test2() throws Exception{
        String baseUrl = "http://localhost:8080";
        String appId = "2";
        String secretKey = "bzyd222";
        TestSignSpringBootStarterDefaultClient client = TestSignSpringBootStarterDefaultClient.getInstance(baseUrl,appId,secretKey);

        JSONObject body = new JSONObject();
        body.put("id", "100");
        body.put("name", "Tom");
        TestSignSpringBootStarterRequest request = new TestSignSpringBootStarterRequest("/test/testPostJson", body);
        TestSignSpringBootStarterResponse response = client.doPostJson(request);
        System.out.println(response);
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex·Guangzhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值