api接口签名加密请求,从springmvc4项目搭建开始

一步一步教你如何实现API,详细注释

知兮丶青 加密 · 接口 · spring
阅读(1929) 2017-12-24
api接口签名加密请求,从springmvc4项目搭建开始
api接口签名加密请求,从springmvc4项目搭建开始

本文一步一步教你如何实现简单API接口请求。


很多业务需要用到简单的api接口请求。通俗的说,例如客户端,客户端不做逻辑处理,只是简单的发起业务请求完成操作。复杂的说,由于互联网发展迅速,大到各种语言php、java、.net之间通讯,各种移动设备层出不穷,分布式架构不断应用,各种客户端Client到处都是;小到内部通讯接口业务、提供合作方某业务接口等等;为了不重复开发,统一管理,这时候需要一个统一的机制,所以api诞生了,更有标准架构设计风格RESTful。


那么如何设计一个简单api请求签名接口呢?

这里不谈WebService,不谈OAuth2.0,只谈简单的api签名接口。

下面来跟我一步一步实现,真正的从无到有介绍。


本请求接口的实现,是模拟支付宝支付接口签名方案,支付宝支付接口签名方式大家也比较熟悉,对简单接口请求来说已经太够用了。



api接口设计方式和思路

1、公开性

接口是公网可访问的,不希望被随便请求,需要token签名认证才能调用,接口提供方提供接口密钥。

2、安全性

模拟支付宝接口签名方式签名,请求参数按照key=value&key=value方式拼接的未签名原始字符串(含时间戳),再对原始字符串进行签名(加密钥)。如:md5(id=1&timestamp=1514020967 + 密钥)

3、模式性

请求

请求参数 + 时间戳 + 签名(请求参数+时间戳+密钥)

接收

md5(接收参数(去除签名) + 密钥) == 接收参数(签名)

后再比较 接收参数(时间戳) 跟 当前系统时间戳 的时效性



准备工作

本项目采用maven搭建,框架采用springmvc4.3。

我的JDK版本:jdk 1.7

我的开发工具:Intellij IDEA 6.1

我的测试工具:Postman



搭建简单springmvc项目

如果你已有项目,请跳过该步骤。

1、打开IDEA新建项目,选择maven -> 勾选Create from archetype -> 选择maven-archetype->webapp

1新建maven项目.png


2、输入递属项目命名空间 和 项目名称

2填写项目名.png


3、选择maven版本

3maven版本.png


4、输入项目名称 和 项目目录位置

4项目名和路径.png


5、配置项目,打开Project Structure,配置api模块,在main下添加java目录,标记为Sources

5打开ProjectStructure.png6配置Sources目录.png


6、配置tomcat,按+号选择Tomcat Server -> Local。输入名称,选择Deployment再点击右侧+号选择api:war exploded

7配置tomca1一.png

8配置tomca1二.png


7、简单配置springmvc

pom.xml

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.weizhixi</groupId>
    <artifactId>api</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <name>api Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <!-- SpringMVC 相关依赖 -->
        <spring.version>4.3.1.RELEASE</spring.version>
        <jackson.version>2.5.0</jackson.version>

        <!-- JSP 相关依赖 -->
        <jsp.version>2.1</jsp.version>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.0.1</servlet.version>

        <!-- 其他 相关依赖 -->
        <slf4j.version>1.7.5</slf4j.version>
        <commons-codec.version>1.9</commons-codec.version>
        <commons-lang3.version>3.2.1</commons-lang3.version>
        <junit.version>3.8.1</junit.version>
    </properties>

    <dependencies>
        <!-- SpringMVC 相关依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!-- JSP 相关依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- 其他 相关依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>${commons-codec.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>api</finalName>
    </build>
</project>


log4j.properties

### FATAL, ERROR, WARN, INFO, DEBUG
log4j.rootLogger=INFO,stdout,D

### stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.COnversionPattern= %d{ABSOLUTE} %5p %c{1}:%L - %m%n

### logFile ###
### save error to another file ###
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=logs/log.log
log4j.appender.D.Append=true
log4j.appender.D.Threshold=INFO
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.COnversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%t\:%r] - [%p] %m%n

log4j.logger.com.weizhixi=DEBU


spring-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:cOntext="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
      http://www.springframework.org/schema/util
      http://www.springframework.org/schema/util/spring-util-4.1.xsd">

   <!--扫描包-->
   <context:component-scan base-package="com.weizhixi.*" />

   <!--依赖注入-->
   <mvc:annotation-driven>
      <mvc:message-converters register-defaults="true">
         <!--防止请求和响应乱码 默认-->
         <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="defaultCharset" value="UTF-8" />
            <property name="writeAcceptCharset" value="false" />
         </bean>
         <!--防止请求和响应乱码 JSON-->
         <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="defaultCharset" value="UTF-8" />
         </bean>
      </mvc:message-converters>
   </mvc:annotation-driven>

   <!--静态资源访问-->
   <mvc:default-servlet-handler/>
   
   <!--视图解释类-->
   <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/"></property>
      <!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑-->
      <property name="suffix" value=".jsp"></property>
      <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
   </bean>

</beans>


web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!--设置字符编码 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置SpringMVC -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>


简单项目springmvc4建立完成,这时候可以点击右侧栏MavenProjects->Lifecycle->clean/compile/package打包,和右上角tomcat运行了,接下来进入开发工作。




,

API接口实现

1、自定义业务异常类(BussException) - 用于抛出业务类异常

package com.weizhixi.util;

/**
 * 自定义业务异常
 * Created by cxq on 2017-12-23.
 */
public class BussException extends Exception {
    private static final long serialVersionUID = 7125646661082731971L;

    private String errorMessage;

    private String errorCode;

    public BussException() {
        super();
    }

    public BussException(String message) {
        super(message);
        this.errorMessage = message;
    }

    public BussException(String code, String message) {
        super(message);
        this.errorCode = code;
        this.errorMessage = message;
    }

    public BussException(Throwable cause) {
        super(cause);
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public String getErrorCode(){
        return this.errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

}


2、JSON结构集(JsonResult) - 用于接口返回JSON数据给客户端

package com.weizhixi.vo;

import java.io.Serializable;

/**
 * JSON结果集
 * Created by cxq on 2017-12-23.
 */
public class JsonResult<T> implements Serializable {
    private static final long serialVersionUID = -1382917362444380590L;
    /**成功标识*/
    private Boolean success;
    /**错误代号*/
    private String errorCode;
    /**错误信息*/
    private String errorMsg;
    /**错误信息追踪*/
    private String errorTrace;
    /**结果*/
    private T data;

    public JsonResult(String errorCode, String errorMsg, String errorTrace){
        this.success = false;
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
        this.errorTrace = errorTrace;
        this.data = null;
    }

    public JsonResult(String errorCode, String errorMsg){
        this.success = false;
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
        this.data = null;
    }

    public JsonResult(T data) {
        this.success = true;
        this.data = data;
    }

    public JsonResult(){
        this.success = true;
        this.data  = null;
    }

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getErrorTrace() {
        return errorTrace;
    }

    public void setErrorTrace(String errorTrace) {
        this.errorTrace = errorTrace;
    }

}


3、用户值对象(UserVo) - 用于测试

package com.weizhixi.vo;

import java.io.Serializable;

/**
 * 用户Vo
 * Created by cxq on 2017-12-23.
 */
public class UserVo implements Serializable{
    private static final long serialVersionUID = -3184783144438300884L;

    /** id */
    private Integer id;
    /** 姓名 */
    private String name;
    /** 标签 */
    private Integer[] tags;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer[] getTags() {
        return tags;
    }

    public void setTags(Integer[] tags) {
        this.tags = tags;
    }
}


4、简单HTTP请求(HttpRequest) - 用于测试

package com.weizhixi.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 测试请求,只是简单的模拟GET/POST请求.你也可以用HttpClient或已有的请求方式
 * Created by cxq on 2017-12-24.
 */
public class HttpRequest {

    /**
     * 为了测试,只是简单的模拟GET请求,按你实际的来
     * @param reqUrl
     * @return String 响应结果
     */
    public static String get(String reqUrl){
        try {
            URL url = new URL(reqUrl);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setDoOutput(false);
            urlConnection.setDoInput(true);
            urlConnection.setRequestMethod("GET");
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConnection.setRequestProperty("Connection", "Keep-Alive");
            urlConnection.setRequestProperty("Charset", "UTF-8");
            urlConnection.setConnectTimeout(10000);
            urlConnection.setReadTimeout(10000);
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
            StringBuffer sb = new StringBuffer();
            String line = in.readLine();
            while(line != null){
                sb.append(line);
                line = in.readLine();
            }
            in.close();
            urlConnection.disconnect();
            return sb.toString();
        }catch(Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * 为了测试,只是简单的模拟POST请求,按你实际的来
     * @param reqUrl
     * @param reqParams
     * @return String 响应结果
     */
    public static String post(String reqUrl, String reqParams){
        try {
            URL url = new URL(reqUrl);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setDoOutput(true);
            urlConnection.setDoInput(true);
            urlConnection.setRequestMethod("POST");
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            urlConnection.setRequestProperty("Connection", "Keep-Alive");
            urlConnection.setRequestProperty("Charset", "UTF-8");
            urlConnection.setConnectTimeout(10000);
            urlConnection.setReadTimeout(10000);
            DataOutputStream dos = new DataOutputStream(urlConnection.getOutputStream());
            dos.writeBytes(reqParams);
            dos.flush();
            dos.close();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
            StringBuffer sb = new StringBuffer();
            String line = in.readLine();
            while(line != null){
                sb.append(line);
                line = in.readLine();
            }
            in.close();
            urlConnection.disconnect();
            return sb.toString();
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }

}


,

5、自定义签名类(Sign)- 用户请求签名及认证,是签名核心类

package com.weizhixi.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * 签名类,模拟支付宝接口签名机制,部分摘录于支付宝Demo
 * Created by cxq on 2017-12-23.
 */
public class Sign {
    private static Logger logger = LoggerFactory.getLogger(Sign.class);

    /** 加密密钥 */
    private final static String SECRET_KEY = "weizhixi";

    /** 字符编码 */
    private final static String INPUT_CHARSET = "UTF-8";

    /** 超时时间 */
    private final static int TIME_OUT = 15;



    //TODO 公用
    /**
     * 请求参数Map转换验证Map
     * @param requestParams 请求参数Map
     * @param charset 是否要转utf8编码
     * @return
     * @throws UnsupportedEncodingException
     */
    public static Map<String,String> toVerifyMap(Map<String, String[]> requestParams, boolean charset) {
        Map<String,String> params = new HashMap<String,String>();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            if(charset)
                valueStr = getContentString(valueStr, INPUT_CHARSET);
            params.put(name, valueStr);
        }
        return params;
    }

    /**
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {
        return createLinkString(params, false);
    }

    /**
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @param encode 是否需要UrlEncode
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params, boolean encode) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (encode)
                value = urlEncode(value, INPUT_CHARSET);
            if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

    /**
     * 编码转换
     * @param content
     * @param charset
     * @return
     * @throws UnsupportedEncodingException
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /**
     * 编码转换
     * @param content
     * @param charset
     * @return
     */
    private static String getContentString(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return new String(content.getBytes());
        }
        try {
            return new String(content.getBytes("ISO-8859-1"), charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /**
     * URL转码
     * @param content
     * @param charset
     * @return
     */
    private static String urlEncode(String content, String charset) {
        try {
            return URLEncoder.encode(content, charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }



    //TODO 签名
    /**
     * 生成要请求的签名参数数组
     * @param sParaTemp 需要签名的参数Map
     * @return 要请求的签名参数数组
     */
    public static Map<String, String> signMap(Map<String, String[]> sParaTemp) {
        //请求参数Map转换验证Map,并生成要请求的签名参数数组
        return sign(toVerifyMap(sParaTemp, false));
    }

    /**
     * 生成要请求的签名参数数组
     * @param sParaTemp 需要签名的参数
     * @return 要请求的签名参数数组
     */
    public static Map<String, String> sign(Map<String, String> sParaTemp) {
        //时间戳加入签名参数组中
        sParaTemp.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));
        //除去数组中的空值和签名参数
        Map<String, String> sPara = paraFilter(sParaTemp);
        //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        String prestr = createLinkString(sPara);
        //生成签名结果
        String mysign = DigestUtils.md5Hex(getContentBytes(prestr + SECRET_KEY, INPUT_CHARSET));
        //签名结果加入请求提交参数组中
        sPara.put("sign", mysign);
        return sPara;
    }

    /**
     * 生成要请求的签名参数字符串“参数=参数值”&链接
     * @param sParaTemp 需要签名的参数Map
     * @return 请求的签名参数字符串
     */
    public static String signStringMap(Map<String, String[]> sParaTemp) {
        //生成要请求的签名参数数组
        Map<String, String> sign = signMap(sParaTemp);
        //生成要请求的签名参数字符串“参数=参数值”&链接
        return createLinkString(sign, true);
    }

    /**
     * 生成要请求的签名参数字符串“参数=参数值”&链接
     * @param sParaTemp 需要签名的参数
     * @return
     */
    public static String signString(Map<String, String> sParaTemp) {
        //生成要请求的签名参数数组
        Map<String, String> sign = sign(sParaTemp);
        //生成要请求的签名参数字符串“参数=参数值”&链接
        return createLinkString(sign, true);
    }



    //TODO 验证签名
    /**
     * 根据反馈回来的信息,生成签名结果
     * @param paramsMap 通知返回来的请求参数Map
     * @return 验证结果
     */
    public static boolean verifyMap(Map<String, String[]> paramsMap) {
        //请求参数Map转换验证Map,并根据反馈回来的信息,生成签名结果
        return verify(toVerifyMap(paramsMap, false));
    }

    /**
     * 根据反馈回来的信息,生成签名结果
     * @param params 通知返回来的参数数组
     * @return 验证结果
     */
    public static boolean verify(Map<String, String> params) {
        String sign = "";
        if(params.get("sign") != null) {sign = params.get("sign");}
        String timestamp = "";
        if(params.get("timestamp") != null) {timestamp = params.get("timestamp");}
        //过滤空值、sign
        Map<String, String> sParaNew = paraFilter(params);
        //获取待签名字符串
        String preSignStr = createLinkString(sParaNew);
        //获得签名验证结果
        String mysign = DigestUtils.md5Hex(getContentBytes(preSignStr + SECRET_KEY, INPUT_CHARSET));
        logger.debug("mysign:"+mysign+" sign:"+sign+" timestamp:"+(System.currentTimeMillis()/1000));
        if(mysign.equals(sign)) {
            //时间不能为空
            if((timestamp.trim()).equals(""))
                return false;
            //是否超时
            if(((System.currentTimeMillis()/1000) - Long.valueOf(timestamp)) > TIME_OUT)
                return false;
            return true;
        } else {
            return false;
        }
    }

}


,

6、新建控制器(IndexController)- 用于演示及用法

package com.weizhixi.controller;

import com.weizhixi.util.BussException;
import com.weizhixi.util.HttpRequest;
import com.weizhixi.util.Sign;
import com.weizhixi.vo.JsonResult;
import com.weizhixi.vo.UserVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 控制器
 * Created by cxq on 2017-12-23.
 */
@Controller
@RequestMapping(value = "/api/v1/index")
public class IndexController {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    //模拟接收签名请求GET
    //传入参数 id timestamp sign
    @RequestMapping(value = "/find", method = RequestMethod.GET)
    @ResponseBody
    public JsonResult<Object> find(HttpServletRequest request, @RequestParam Integer id) {
        logger.debug("访问了控制器IndexController.find");
        JsonResult<Object> result = new JsonResult<Object>();
        try {
            //认证签名
            if(!Sign.verifyMap(request.getParameterMap()))
                throw new BussException("签名错误");

            logger.debug("正确签名 - 正常访问");
            //这里是业务 - 模拟findById(id)
            UserVo vo = new UserVo();
            vo.setId(id);
            vo.setName("微知兮");

            result.setData(vo);
        }catch(BussException be){
            logger.debug(be.getMessage());
            result.setSuccess(false);
            result.setErrorMsg(be.getMessage());
        }catch(Exception e){
            logger.error("ERROR:", e);
            result.setSuccess(false);
            result.setErrorMsg(e.getMessage());
        }
        return result;
    }

    //模拟接收签名请求POST
    //传入参数 UserVo:id name tags timestamp sign
    @RequestMapping(value = "/save", method = RequestMethod.POST)
    @ResponseBody
    public JsonResult<Object> save(HttpServletRequest request, UserVo vo) {
        logger.debug("访问了控制器IndexController.save");
        JsonResult<Object> result = new JsonResult<Object>();
        try {
            //认证签名
            if(!Sign.verifyMap(request.getParameterMap()))
                throw new BussException("签名错误");

            logger.debug("正确签名 - 正常访问");
            //这里是业务 - 模拟 save(vo)

            result.setData(vo);
        }catch(BussException be){
            logger.debug(be.getMessage());
            result.setSuccess(false);
            result.setErrorMsg(be.getMessage());
        }catch(Exception e){
            logger.error("ERROR:", e);
            result.setSuccess(false);
            result.setErrorMsg(e.getMessage());
        }
        return result;
    }

}

注释的很多了,就不再啰嗦了。



使用演示

下面运行项目,测试下请求这2个接口。

打开:Postman 模拟GET、POST请求。

请求:

http://localhost:8080/api/v1/index/find?id=1&timestamp=1514020967&sign=012a62ef8f26fce6cddf4c3691656a89

响应:

{
    "success": true,
    "errorCode": null,
    "errorMsg": null,
    "errorTrace": null,
    "data": {
        "id": 1,
        "name": "微知兮"
    }
}

请求:

http://localhost:8080/api/v1/index/save

参数:

field = value

id = 1

name = 微知兮

tags = 1

tags = 2

timestamp = 1514027981

sign = 776880607e2a8161499ba67da8fb505e

响应:

{
    "success": true,
    "errorCode": null,
    "errorMsg": null,
    "errorTrace": null,
    "data": {
        "id": 1,
        "name": "微知兮",
        "tags": [
            1,
            2
        ]
    }
}

Postman运行效果.png


再模拟下程序请求:

以下代码加入控制器就可以了

//模拟调用接口API获取参数 - GET
@RequestMapping(value = "/get", method = RequestMethod.GET)
@ResponseBody
public String get(HttpServletRequest request){
    Map<String,String> map = new HashMap<String, String>();
    map.put("id","1");
    //生成签名Get请求参数
    String params = Sign.signString(map);

    //这里只是个参考和测试,只是简单的模拟GET请求 你也可以用HttpClient
    String inputLine = HttpRequest.get("http://localhost:8080/api/v1/index/find?"+params);
    logger.info(inputLine);
    return inputLine;
}

//模拟调用接口API获取参数 - POST
@RequestMapping(value = "/post", method = RequestMethod.GET)
@ResponseBody
public String post(HttpServletRequest request){
    Map<String,String> map = new HashMap<String, String>();
    map.put("id","1");
    map.put("name","微知兮");
    map.put("tags","1,2,3");
    //生成签名Post请求参数
    String params = Sign.signString(map);

    //这里只是个参考和测试,只是简单的模拟POST请求 你也可以用HttpClient
    String inputLine = HttpRequest.post("http://localhost:8080/api/v1/index/save", params);
    logger.info(inputLine);
    return inputLine;
}

再地址栏敲入:

http://localhost:8080/api/v1/index/get


http://localhost:8080/api/v1/index/post

发现一切happy


至此,全部已经介绍完毕,已附上完整例子,如需要请自行下载,附上php版本例子。


php例子请查看:http://www.weizhixi.com/user/index/article/id/58.html


本例子仅供学习研究,由于写的比较仓促,如有不妥当的地方欢迎指出。



zip icon
api接口签名加密请求(完整版).zip 003c8f71341f08cd636ff89c54c87f1a

已下载:515

已下载:488

原创文章,转载请注明出处:https://www.weizhixi.com/article/40.html