发布时间:2023-07-05 08:00
用户需要按照开放平台的协议规范拼装一个正确的URL,通过Https请求到开放平台既能够获取到所需数据。主要流程包含:填写参数、生成签名、拼装HTTPS请求、发起请求、得到响应结果、解析结果。
参数名称 |
参数类型 |
是否必传 |
参数描述 |
method |
String |
是 |
API接口名称 |
access_token |
String |
是 |
采用OAuth授权方式是必填参数 |
app_key |
String |
是 |
应用的app_key |
sign |
String |
是 |
详见下文“5.签名算法”描述 |
timestamp |
String |
是 |
时间戳,格式为yyyy-MM-dd HH:mm:ss,例如:2019-05-01 00:00:00。API服务端允许客户端请求时间误差为10分钟 |
format |
String |
否 |
暂时只支持json |
v |
String |
是 |
API协议版本,参考接口文档版本 |
360buy_param_json |
String |
是 |
需要将应用级参数作为一个整体对象以json的形式拼接传递 |
应用级参数(更多API应用参数参考 接口文档)
为了防止API在调用过程中被恶意者拦截随意篡改,调用API是需要传入签名参数,开放平台服务端会根据请求参数对签名进行验证,判断请求参数是否合法。开放平台签名规则过程如下:
将所有请求参数按照字母先后顺序排列,例如:access_token,app_key,method,timestamp,v,360buy_param_json ,
排序为360buy_param_json,access_token,app_key,method,timestamp,v
把所有参数名和参数值进行拼接,例如:360buy_param_jsonxxxaccess_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx
把appSecret夹在字符串(上一步拼接串)的两端,例如:appSecret+XXXX+appSecret
使用MD5进行加密,再转化成大写。
服务端基于SpringBoot框架编写,入口放在Controller,业务逻辑写在Service。同时考虑安全性和方便排查问题,会加入输入性校验和访问日志。
接口只有一个入口,即Controller代码如下:
@Controller public class RouterController { @Resource private RouterService routerService; @Resource private OpenApiLogService openApiLogService; /** * API接口路由器,接口入口 * * @param request * @param zrsc_param_json * @return */ @RequestMapping(value = \"routerjson\", method = RequestMethod.POST) @ResponseBody public String routerjson(HttpServletRequest request, String zrsc_param_json) { if (zrsc_param_json==null||\"\".equals(zrsc_param_json)) { return JsonUtils.objToJson(APIMessageVo.fail(APIErrorEnum.FAIL_PARA_LOSE.getCode(), APIErrorEnum .FAIL_PARA_LOSE.getName())); } APIMessageVo aPIMessageVo=openApiLogService.secrityCheck(request); if(aPIMessageVo.isSuccess()){//安全检测成功 aPIMessageVo=routerService.router(request, zrsc_param_json); openApiLogService.insert(request,aPIMessageVo); } return JsonUtils.objToJson(aPIMessageVo); } }
业务逻辑层,简单举例说明,不同业务有所不同。
public APIMessageVo router(HttpServletRequest request, String zrsc_param_json) { String access_token = request.getParameter(\"access_token\"); String app_key = request.getParameter(\"app_key\"); String method = request.getParameter(\"method\"); String sign = request.getParameter(\"sign\"); APIMessageVo checkResult=this.routerParaCheck(request, zrsc_param_json,access_token,app_key,method,sign); if(!checkResult.isSuccess()){//入参检测失败 return checkResult; } if (APPInterfaceNameEnum.API_ADDRESS_ADDRESS2PROVICECITY_GET.getName().equals(method)) {//获取省市区街道 return this.address2provincecity(zrsc_param_json); } else {//接口不存在 return APIMessageVo.fail(APIErrorEnum.FAIL_NOT_FOUND_INTERFACE.getCode(), APIErrorEnum.FAIL_NOT_FOUND_INTERFACE.getName()); } } private APIMessageVo routerParaCheck(HttpServletRequest request, String zrsc_param_json, String access_token, String app_key, String method, String sign){ //***************参数校验*************** if (StringUtils.isBlank(access_token) || StringUtils.isBlank(app_key) || StringUtils.isBlank(method) || StringUtils.isBlank(sign)) { return APIMessageVo.fail(APIErrorEnum.FAIL_PARA_LOSE.getCode(), APIErrorEnum.FAIL_PARA_LOSE.getName()); } if(!APP_KEY.equals(app_key)){ return APIMessageVo.fail(APIErrorEnum.FAIL_NOT_EXIST_APP_ID.getCode(), APIErrorEnum.FAIL_NOT_EXIST_APP_ID.getName()); } //***************sign校验*************** try { //获取request中的参数 MapsysParams = getSysParams(request, zrsc_param_json); //SDK参数加密 String signNew = SDKSignUtils.sign(sysParams, APP_SECRET); //判断参数是否被更改 if (!sign.equals(signNew)) { return APIMessageVo.fail(APIErrorEnum.FAIL_ERR_APP_SECRET.getCode(), APIErrorEnum.FAIL_ERR_APP_SECRET.getName()); } } catch (Exception e) { return APIMessageVo.fail(APIErrorEnum.FAIL_EXCEPTION.getCode(), APIErrorEnum.FAIL_EXCEPTION.getName()); } return APIMessageVo.success(); }
APIErrorEnum
public enum APIErrorEnum { FAIL_NOTAUTH(201,\"没有授权\"), FAIL_TOKEN_EXPIRE(202,\"Token过期\"), FAIL_PARA_LOSE(203,\"缺少参数\"), FAIL_NOT_REALAUTH(204,\"没有实名认证通过\"), FAIL_NOT_METHOD(205,\"没有权限访问这个接口\"), FAIL_PARA_ERR(206,\"参数的值,不正确\"), FAIL_NOT_EXIST_ACCOUNT(207,\"用户账号不存在\"), FAIL_NOT_FOUND_APPLY(208,\"应用不存在\"), FAIL_NOT_PASS_APPROVAL_APPLY(209,\"应用审批未通过\"), FAIL_NOT_EXIST_APP_ID(210,\"APP_ID不存在\"), FAIL_NOT_FOUND_INTERFACE(211,\"接口不存在\"), FAIL_ERR_APP_SECRET(212,\"appSecret错误\"), FAIL_CALL_FREQUENTLY(214,\"调用太频繁\"), FAIL_EXCEPTION(290,\"未知错误\"); private int code; private String name; APIErrorEnum(int code,String name) { this.code = code; this.name = name; } public int getCode() { return code; } public String getName() { return name; } public void setCode(int code) { this.code = code; } public void setName(String name) { this.name = name; } }
APIMessageVo
public class APIMessageVo { public static final Integer SUCCESS = 100;//成功 private boolean success;// 处理是否成功 private Integer code = SUCCESS;//状态码 private String message = \"成功\";// 附加消息, 如处理结果失败时的原因等 private Object data=\"\";// 可以附带返回一些结果数据 public APIMessageVo() { //default } public APIMessageVo(boolean success, Integer code) { this(success, code, \"成功\", null); } public APIMessageVo(boolean success, Integer code, String message) { this(success, code, message, null); } public APIMessageVo(boolean success, String message, Object data) { this.success = success; this.message = message; this.data = data; } public APIMessageVo(boolean success, Integer code, String message, Object data) { this.success = success; this.code = code; this.message = message; this.data = data; } public APIMessageVo(boolean success, Object data) { this.success = success; this.data = data; } public static APIMessageVo fail(Integer code) { return new APIMessageVo(false, code); } public static APIMessageVo fail(Integer code,String message) { return new APIMessageVo(false,code, message); } public static APIMessageVo fail(Integer code,String message, Object data) { return new APIMessageVo(false,code, message, data); } public static APIMessageVo success() { return new APIMessageVo(true, SUCCESS); } public static APIMessageVo success(String message) { return new APIMessageVo(true, message); } public static APIMessageVo success(String message, Object data) { return new APIMessageVo(true, SUCCESS, message, data); } public static APIMessageVo success(Object data) { return new APIMessageVo(true, \"成功\", data); } }
JsonUtils
public class JsonUtils { // 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); /** * 对象转Json * * @param obj 对象 * @return json串 */ public static String objToJson(Object obj) { try { return MAPPER.writeValueAsString(obj); } catch (JsonProcessingException e) { log.error(\"Json转换异常:{}\", e); } return \"Json转换异常\"; } /** * json转对象 * @param jsonData json串 * @param beanType 对象 * @return 对象 */ public staticT jsonToPojo(String jsonData, Class beanType) { try { T obj = MAPPER.readValue(jsonData, beanType); return obj; } catch (Exception e) { log.error(\"Json转换异常:{}\", e); } return null; } }
DefaultZrscClient
public class DefaultZrscClient implements ZrscClient { private String serverUrl; private String accessToken; private int connectTimeout; private int readTimeout; private String appKey; private String fuzz; private String appSecret; public DefaultZrscClient(String serverUrl, String accessToken, String appKey, String appSecret) { this.connectTimeout = 8000; this.readTimeout = 8000; this.serverUrl = serverUrl; this.accessToken = accessToken; this.appKey = appKey; this.appSecret = appSecret; } publicT execute(ZrscRequest request) throws ZrscException { try { String url = this.buildUrl(request); Map params = new HashMap(); String json = request.getAppJsonParams(); params.put(\"zrsc_param_json\", json); if (request.getOtherParams() != null) { params.put(\"other\", request.getOtherParams()); } String rsp = HttpUtil.doPost(url, params, this.connectTimeout, this.readTimeout,this.accessToken); T resp = this.parse(rsp, request.getResponseClass()); StringBuffer sb = new StringBuffer(); sb.append(url).append(\"&\").append(\"zrsc_param_json\").append(\"=\").append(json); resp.setUrl(sb.toString()); return resp; } catch (Exception var8) { var8.printStackTrace(); throw new ZrscException(\"出现异常,请重试\"); } } private String buildUrl(ZrscRequest request) throws Exception { Map sysParams = request.getSysParams(); Map pmap = new TreeMap(); pmap.put(\"zrsc_param_json\", request.getAppJsonParams()); sysParams.put(\"method\", request.getApiMethod()); sysParams.put(\"access_token\", this.accessToken); sysParams.put(\"app_key\", this.appKey); pmap.putAll(sysParams); String sign = this.sign(pmap, this.appSecret); sysParams.put(\"sign\", sign); StringBuilder sb = new StringBuilder(this.serverUrl); sb.append(\"?\"); sb.append(HttpUtil.buildQuery(sysParams, \"UTF-8\")); return sb.toString(); } private T parse(String rsp, Class responseClass) throws ZrscException { Parser parser; if (this.serverUrl.endsWith(\"json\")) { parser = ParserFactory.getJsonParser(); } else { parser = ParserFactory.getXmlParser(); } return parser.parse(rsp, responseClass); } private String sign(Map pmap, String appSecret) throws Exception { StringBuilder sb = new StringBuilder(appSecret); Iterator i$ = pmap.entrySet().iterator(); while(i$.hasNext()) { Map.Entry entry = (Map.Entry)i$.next(); String name = (String)entry.getKey(); String value = (String)entry.getValue(); if (StringUtil.areNotEmpty(new String[]{name, value})) { sb.append(name).append(value); } } sb.append(appSecret); String result = CodecUtil.md5(sb.toString()); return result; } }
HttpUtil
public class HttpUtil { public static final String DEFAULT_CHARSET = \"UTF-8\"; private static final String METHOD_POST = \"POST\"; private HttpUtil() { throw new UnsupportedOperationException(); } public static String buildQuery(Mapparams, String charset) throws Exception { if (params != null && !params.isEmpty()) { StringBuilder query = new StringBuilder(); Set > entries = params.entrySet(); boolean hasParam = false; Iterator i$ = entries.iterator(); while(i$.hasNext()) { Entry entry = (Entry)i$.next(); String name = (String)entry.getKey(); String value = (String)entry.getValue(); if (StringUtil.areNotEmpty(new String[]{name, value})) { if (hasParam) { query.append(\"&\"); } else { hasParam = true; } query.append(name).append(\"=\").append(URLEncoder.encode(value, charset)); } } return query.toString(); } else { return null; } } public static String doPost(String url, Map params, int connectTimeout, int readTimeout,String token) throws Exception { return doPost(url, params, \"UTF-8\", connectTimeout, readTimeout,token); } public static String doPost(String url, Map params, String charset, int connectTimeout, int readTimeout,String token) throws Exception { String ctype = \"application/x-www-form-urlencoded;charset=\" + charset; //String ctype = \"application/json;charset=\" + charset; String query = buildQuery(params, charset); byte[] content = new byte[0]; if (query != null) { content = query.getBytes(charset); } return doPost(url, ctype, content, connectTimeout, readTimeout,token); } public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout,String token) throws IOException { HttpURLConnection conn = null; OutputStream out = null; String rsp = null; try { conn = getConnection(new URL(url), \"POST\", ctype,token); conn.setConnectTimeout(connectTimeout); conn.setReadTimeout(readTimeout); out = conn.getOutputStream(); out.write(content); rsp = getResponseAsString(conn); } finally { if (out != null) { out.close(); } if (conn != null) { conn.disconnect(); } } return rsp; } private static HttpURLConnection getConnection(URL url, String method, String ctype,String token) throws IOException { HttpURLConnection conn = null; if (\"https\".equals(url.getProtocol())) { SSLContext ctx = null; try { ctx = SSLContext.getInstance(\"TLS\"); ctx.init(new KeyManager[0], new DefaultTrustManager[]{new DefaultTrustManager()}, new SecureRandom()); } catch (Exception var6) { throw new IOException(var6); } HttpsURLConnection connHttps = (HttpsURLConnection)url.openConnection(); connHttps.setSSLSocketFactory(ctx.getSocketFactory()); connHttps.setHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }); conn = connHttps; } else { conn = (HttpURLConnection)url.openConnection(); } ((HttpURLConnection)conn).setRequestMethod(method); ((HttpURLConnection)conn).setDoInput(true); ((HttpURLConnection)conn).setDoOutput(true); ((HttpURLConnection)conn).setRequestProperty(\"Accept\", \"text/xml,text/javascript,text/html\"); ((HttpURLConnection)conn).setRequestProperty(\"User-Agent\", \"kcb-sdk-java\"); ((HttpURLConnection)conn).setRequestProperty(\"Content-Type\", ctype); ((HttpURLConnection)conn).setRequestProperty(\"Authorization\", token); return (HttpURLConnection)conn; } protected static String getResponseAsString(HttpURLConnection conn) throws IOException { String charset = getResponseCharset(conn.getContentType()); InputStream es = conn.getErrorStream(); if (es == null) { return getStreamAsString(conn.getInputStream(), charset); } else { String msg = getStreamAsString(es, charset); if (StringUtil.isEmpty(msg)) { throw new IOException(conn.getResponseCode() + \":\" + conn.getResponseMessage()); } else { throw new IOException(msg); } } } private static String getStreamAsString(InputStream stream, String charset) throws IOException { try { BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset)); StringWriter writer = new StringWriter(); char[] chars = new char[256]; boolean var5 = false; int count; while((count = reader.read(chars)) > 0) { writer.write(chars, 0, count); } String var6 = writer.toString(); return var6; } finally { if (stream != null) { stream.close(); } } } private static String getResponseCharset(String ctype) { String charset = \"UTF-8\"; if (!StringUtil.isEmpty(ctype)) { String[] params = ctype.split(\";\"); String[] arr$ = params; int len$ = params.length; for(int i$ = 0; i$ < len$; ++i$) { String param = arr$[i$]; param = param.trim(); if (param.startsWith(\"charset\")) { String[] pair = param.split(\"=\", 2); if (pair.length == 2 && !StringUtil.isEmpty(pair[1])) { charset = pair[1].trim(); } break; } } } return charset; } private static byte[] getTextEntry(String fieldName, String fieldValue, String charset) throws IOException { StringBuilder entry = new StringBuilder(); entry.append(\"Content-Disposition:form-data;name=\\\"\"); entry.append(fieldName); entry.append(\"\\\"\\r\\nContent-Type:text/plain\\r\\n\\r\\n\"); entry.append(fieldValue); return entry.toString().getBytes(charset); } private static byte[] getFileEntry(String fieldName, String fileName, String mimeType, String charset) throws IOException { StringBuilder entry = new StringBuilder(); entry.append(\"Content-Disposition:form-data;name=\\\"\"); entry.append(fieldName); entry.append(\"\\\";filename=\\\"\"); entry.append(fileName); entry.append(\"\\\"\\r\\nContent-Type:\"); entry.append(mimeType); entry.append(\"\\r\\n\\r\\n\"); return entry.toString().getBytes(charset); } }
@Test void getAddress() throws IOException { Address2provicecityRequest request=new Address2provicecityRequest(); request.setAddress(\"阿胶街东首路北\"); ZrscClient client = new DefaultZrscClient(serverUrl, accessToken, appKey, appSecret); try { Address2provicecityResponse response= client.execute(request); System.out.println(\"data=\"+JsonUtil.toJson(response.getData())); } catch (Exception e) { e.printStackTrace(); } }
运行结果:
data={\"source\":\"阿胶街东首路北\",\"province\":\"山东省\",\"city\":\"聊城市\",\"area\":\"东阿县\",\"street\":\"新城街道\",\"addressInfo\":\"阿胶街东首路北\"}
京东开放平台中的传参和签名算法比较有代表性,此博客,只是分享开放平台开放的主要组成部分,希望大家可以多多关注脚本之家的其他内容!