parent
9f2e659523
commit
326edf661e
@ -1,29 +1,29 @@ |
||||
* [首页](/?t=1560480225362) |
||||
* [首页](/?t=1560912863431) |
||||
* 开发文档 |
||||
* [快速体验](files/10010_快速体验.md?t=1560480225362) |
||||
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1560480225379) |
||||
* [新增接口](files/10020_新增接口.md?t=1560480225379) |
||||
* [业务参数校验](files/10030_业务参数校验.md?t=1560480225380) |
||||
* [错误处理](files/10040_错误处理.md?t=1560480225380) |
||||
* [编写文档](files/10041_编写文档.md?t=1560480225380) |
||||
* [接口交互详解](files/10050_接口交互详解.md?t=1560480225380) |
||||
* [easyopen支持](files/10070_easyopen支持.md?t=1560480225380) |
||||
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1560480225380) |
||||
* [ISV管理](files/10085_ISV管理.md?t=1560480225380) |
||||
* [自定义路由](files/10086_自定义路由.md?t=1560480225381) |
||||
* [路由授权](files/10090_路由授权.md?t=1560480225381) |
||||
* [接口限流](files/10092_接口限流.md?t=1560480225381) |
||||
* [监控日志](files/10093_监控日志.md?t=1560480225381) |
||||
* [SDK开发](files/10095_SDK开发.md?t=1560480225381) |
||||
* [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1560480225382) |
||||
* [应用授权](files/10097_应用授权.md?t=1560480225382) |
||||
* [更改数据节点名称](files/10099_更改数据节点名称.md?t=1560480225382) |
||||
* [对接前端](files/10100_对接前端.md?t=1560480225382) |
||||
* [自定义过滤器](files/10102_自定义过滤器.md?t=1560480225382) |
||||
* [文件上传](files/10104_文件上传.md?t=1560480225382) |
||||
* [快速体验](files/10010_快速体验.md?t=1560912863434) |
||||
* [项目接入到SOP](files/10011_项目接入到SOP.md?t=1560912863453) |
||||
* [新增接口](files/10020_新增接口.md?t=1560912863453) |
||||
* [业务参数校验](files/10030_业务参数校验.md?t=1560912863453) |
||||
* [错误处理](files/10040_错误处理.md?t=1560912863453) |
||||
* [编写文档](files/10041_编写文档.md?t=1560912863453) |
||||
* [接口交互详解](files/10050_接口交互详解.md?t=1560912863453) |
||||
* [easyopen支持](files/10070_easyopen支持.md?t=1560912863454) |
||||
* [使用签名校验工具](files/10080_使用签名校验工具.md?t=1560912863454) |
||||
* [ISV管理](files/10085_ISV管理.md?t=1560912863454) |
||||
* [自定义路由](files/10086_自定义路由.md?t=1560912863454) |
||||
* [路由授权](files/10090_路由授权.md?t=1560912863454) |
||||
* [接口限流](files/10092_接口限流.md?t=1560912863454) |
||||
* [监控日志](files/10093_监控日志.md?t=1560912863454) |
||||
* [SDK开发](files/10095_SDK开发.md?t=1560912863455) |
||||
* [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1560912863455) |
||||
* [应用授权](files/10097_应用授权.md?t=1560912863455) |
||||
* [更改数据节点名称](files/10099_更改数据节点名称.md?t=1560912863455) |
||||
* [对接前端](files/10100_对接前端.md?t=1560912863455) |
||||
* [自定义过滤器](files/10102_自定义过滤器.md?t=1560912863455) |
||||
* [文件上传](files/10104_文件上传.md?t=1560912863455) |
||||
* 原理分析 |
||||
* [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1560480225382) |
||||
* [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1560480225382) |
||||
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1560480225383) |
||||
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1560480225383) |
||||
* [常见问题](files/90100_常见问题.md?t=1560480225383) |
||||
* [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1560912863455) |
||||
* [原理分析之路由存储](files/90011_原理分析之路由存储.md?t=1560912863456) |
||||
* [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1560912863456) |
||||
* [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1560912863456) |
||||
* [常见问题](files/90100_常见问题.md?t=1560912863456) |
||||
|
@ -0,0 +1,48 @@ |
||||
/** |
||||
* 自动保存输入框的值 |
||||
* @type {{init: InputCache.init}} |
||||
*/ |
||||
var InputCache = { |
||||
init: function () { |
||||
var storage = window.localStorage; |
||||
if (storage) { |
||||
var dataKey = location.host; |
||||
|
||||
$('body').find('input[type="text"]').unbind().change(function () { |
||||
var id = this.id; |
||||
if (id) { |
||||
var data = getCache(); |
||||
data[id] = this.value; |
||||
setCache(data); |
||||
} |
||||
}); |
||||
|
||||
function getCache() { |
||||
var jsonStr = storage.getItem(dataKey); |
||||
if (!jsonStr) { |
||||
jsonStr = '{}'; |
||||
} |
||||
return JSON.parse(jsonStr); |
||||
} |
||||
|
||||
function setCache(jsonObj) { |
||||
var jsonStr = JSON.stringify(jsonObj); |
||||
storage.setItem(dataKey, jsonStr); |
||||
} |
||||
|
||||
// load data
|
||||
var cache = getCache(); |
||||
var docu = document; |
||||
|
||||
for (var id in cache) { |
||||
var dom = docu.getElementById(id); |
||||
if (dom) { |
||||
var oldVal = dom.value; |
||||
var val = cache[id] || oldVal; |
||||
dom.value = val; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,124 @@ |
||||
|
||||
docEvent.bind(function (docItem,layui) { |
||||
selectItem(docItem, layui); |
||||
}); |
||||
|
||||
var treetable; |
||||
|
||||
function selectItem(docItem, layui) { |
||||
var nameVersion = docItem.nameVersion; |
||||
treetable = treetable || layui.treetable; |
||||
$('.sop-name').text(docItem.name); |
||||
$('.sop-version').text(docItem.version); |
||||
$('.sop-summary').text(docItem['summary']); |
||||
$('.sop-description').text(docItem.description || docItem['summary']); |
||||
|
||||
createRequestParameter(docItem); |
||||
createResponseParameter(docItem); |
||||
createResponseCode(docItem); |
||||
|
||||
var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]'); |
||||
$li.addClass('layui-this').siblings().removeClass('layui-this'); |
||||
} |
||||
|
||||
function createRequestParameter(docItem) { |
||||
var data = buildTreeData(docItem.requestParameters); |
||||
createTreeTable('treeTableReq', data); |
||||
} |
||||
|
||||
function createResponseParameter(docItem) { |
||||
var data = buildTreeData(docItem.responseParameters); |
||||
createTreeTable('treeTableResp', data); |
||||
} |
||||
|
||||
function buildTreeData(parameters, parentId) { |
||||
var data = []; |
||||
parentId = parentId || 0; |
||||
for (var i = 0; i < parameters.length; i++) { |
||||
var parameter = parameters[i]; |
||||
parameter.id = parentId * 100 + (i + 1); |
||||
parameter.parentId = parentId; |
||||
data.push(parameter); |
||||
var refs = parameter.refs; |
||||
if (refs && refs.length > 0) { |
||||
var childData = buildTreeData(refs, parameter.id); |
||||
data = data.concat(childData); |
||||
} |
||||
} |
||||
return data; |
||||
} |
||||
|
||||
function createTreeTable(id, data) { |
||||
var el = '#' + id; |
||||
treetable.render({ |
||||
elem: el, |
||||
treeColIndex: 0, |
||||
treeSpid: 0, |
||||
treeIdName: 'id', |
||||
treePidName: 'parentId', |
||||
treeDefaultClose: false, |
||||
treeLinkage: false, |
||||
data: data, |
||||
page: false, |
||||
cols: [[ |
||||
{field: 'name', title: '参数'} |
||||
,{field: 'type', title: '类型', width: 80} |
||||
,{field: 'required', title: '是否必填', width: 100, templet:function (row) { |
||||
return row.required ? '<span style="color: red;">是</span>' : '否'; |
||||
}} |
||||
,{field: 'maxLength', title: '最大长度', width: 100} |
||||
,{field: 'description', title: '描述', width: 200} |
||||
,{field: 'paramExample', title: '示例值', width: 200} |
||||
]] |
||||
}); |
||||
} |
||||
|
||||
function createResponseCode(docItem) { |
||||
var method = docItem.name.replace(/\./g, '_'); |
||||
var responseParameters = docItem.responseParameters; |
||||
var bizResult = buildResult(responseParameters); |
||||
var json = '{\n' + |
||||
' "'+method+'_response": {\n' + |
||||
' "code": "10000",\n' + |
||||
' "msg": "Success",\n' + |
||||
bizResult + |
||||
' }' + |
||||
'}'; |
||||
json = formatJson(json); |
||||
$('#responseExampleJson').text(json); |
||||
|
||||
var errorJson = '{\n' + |
||||
' "'+method+'_response": {\n' + |
||||
' "code": "20000",\n' + |
||||
' "msg": "Service is temporarily unavailable",\n' + |
||||
' "sub_code": "isp.unknow-error",\n' + |
||||
' "sub_msg": "服务暂不可用"\n' + |
||||
' }' + |
||||
'}'; |
||||
errorJson = formatJson(errorJson); |
||||
$('#responseErrorJson').text(errorJson); |
||||
} |
||||
|
||||
function buildResult(parameters) { |
||||
var result = []; |
||||
for (var i = 0; i < parameters.length; i++) { |
||||
var parameter = parameters[i]; |
||||
result.push('\"'+parameter.name+'\": ' + buildExample(parameter)) |
||||
} |
||||
return result.join(","); |
||||
} |
||||
|
||||
function buildExample(parameter) { |
||||
var refs = parameter.refs; |
||||
if (refs) { |
||||
// {...}
|
||||
var content = '{' + buildResult(refs) + '}'; |
||||
if (parameter.type == 'array') { |
||||
// [{...}]
|
||||
content = '[' + content + ']'; |
||||
} |
||||
return content; |
||||
} else { |
||||
return '\"' + parameter.example + '\"'; |
||||
} |
||||
} |
@ -0,0 +1,161 @@ |
||||
|
||||
docEvent.bind(function (docItem,layui) { |
||||
selectItem(docItem, layui); |
||||
}); |
||||
|
||||
layui.use('form', function(){ |
||||
var form = layui.form; |
||||
|
||||
//监听提交
|
||||
form.on('submit(formSend)', function(data){ |
||||
doTest(); |
||||
return false; |
||||
}); |
||||
}); |
||||
|
||||
var $body = $('body'); |
||||
var treetable; |
||||
var method,version; |
||||
var nameVersion; |
||||
|
||||
function selectItem(docItem, layui) { |
||||
resetResultDiv(); |
||||
nameVersion = docItem.nameVersion; |
||||
method = docItem.name; |
||||
version = docItem.version; |
||||
treetable = treetable || layui.treetable; |
||||
$('.sop-name').text(docItem.name); |
||||
$('.sop-version').text(docItem.version); |
||||
$('.sop-summary').text(docItem['summary']); |
||||
$('.sop-description').text(docItem.description || docItem['summary']); |
||||
|
||||
createRequestParameter(docItem); |
||||
|
||||
var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]'); |
||||
$li.addClass('layui-this').siblings().removeClass('layui-this'); |
||||
InputCache.init(); |
||||
} |
||||
|
||||
function createRequestParameter(docItem) { |
||||
var data = buildTreeData(docItem.requestParameters); |
||||
createTreeTable('treeTableReq', data); |
||||
} |
||||
|
||||
function buildTreeData(parameters, parentId) { |
||||
var data = []; |
||||
parentId = parentId || 0; |
||||
for (var i = 0; i < parameters.length; i++) { |
||||
var parameter = parameters[i]; |
||||
parameter.id = parentId * 100 + (i + 1); |
||||
parameter.parentId = parentId; |
||||
data.push(parameter); |
||||
var refs = parameter.refs; |
||||
if (refs && refs.length > 0) { |
||||
var childData = buildTreeData(refs, parameter.id); |
||||
data = data.concat(childData); |
||||
} |
||||
} |
||||
return data; |
||||
} |
||||
|
||||
function createTreeTable(id, data) { |
||||
var el = '#' + id; |
||||
treetable.render({ |
||||
elem: el, |
||||
treeColIndex: 0, |
||||
treeSpid: 0, |
||||
treeIdName: 'id', |
||||
treePidName: 'parentId', |
||||
treeDefaultClose: false, |
||||
treeLinkage: false, |
||||
data: data, |
||||
page: false, |
||||
firstTemplet: function (row) { |
||||
var required = row.required ? '<span style="color: red;">*</span> ' : ''; |
||||
return required + row.name; |
||||
}, |
||||
cols: [[ |
||||
{field: 'name', title: '参数',width: 200} |
||||
,{field: 'val', title: '值', width: 300, templet:function (row) { |
||||
var id = nameVersion + '-' + row.name; |
||||
var requiredTxt = row.required ? 'required lay-verify="required"' : ''; |
||||
return !row.refs ? '<input id="' + id + '" ' + requiredTxt + ' type="text" name="' + row.name + '" class="layui-input test-input"/>' : ''; |
||||
}} |
||||
,{field: 'description', title: '描述'} |
||||
]] |
||||
}); |
||||
} |
||||
|
||||
function doTest() { |
||||
var data = { |
||||
appId: $('#appId').val(), |
||||
privateKey: $('#privateKey').val(), |
||||
method: method, |
||||
version: version |
||||
}; |
||||
var $inputs = $body.find('.test-input'); |
||||
var bizContent = {}; |
||||
$inputs.each(function () { |
||||
bizContent[this.name] = this.value; |
||||
}); |
||||
data.bizContent = JSON.stringify(bizContent); |
||||
$.ajax({ |
||||
url: SopConfig.url + '/sandbox/test' |
||||
, dataType: 'json' |
||||
, data: data |
||||
, method: 'post' |
||||
, success: function (resp) { |
||||
setReqInfo(resp); |
||||
showRespnfo(resp.apiResult); |
||||
} |
||||
, error: function (xhr,status,error) { |
||||
// {"timestamp":"2019-06-19 15:57:36","status":500,"error":"Internal Server Error","message":"appId不能为空","path":"/sandbox/test"}
|
||||
var errorData = xhr.responseJSON; |
||||
if (errorData) { |
||||
setReqInfo(''); |
||||
showRespnfo(errorData.message); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
function showRespnfo(info) { |
||||
var json = formatJson(info); |
||||
setRespInfo(json); |
||||
$('#resultDiv').show(); |
||||
} |
||||
|
||||
function setReqInfo(resp) { |
||||
var txt = ''; |
||||
if (resp) { |
||||
var html = []; |
||||
html.push('【请求参数】:' + resp.params); |
||||
html.push('【待签名内容】:' + resp.beforeSign); |
||||
html.push('【签名(sign)】:' + resp.sign); |
||||
txt = html.join('\r\n') |
||||
} |
||||
$('#req-info-result').val(txt); |
||||
} |
||||
|
||||
function setRespInfo(info) { |
||||
$('#resp-info-result').text(info); |
||||
} |
||||
|
||||
function resetResultDiv() { |
||||
setReqInfo({ |
||||
beforeSign: '', |
||||
params: '', |
||||
privateKey: '', |
||||
sign: '' |
||||
}); |
||||
setRespInfo(''); |
||||
$('.resp-info-content') |
||||
.addClass('layui-show') |
||||
.siblings().removeClass('layui-show'); |
||||
|
||||
$('.resp-info') |
||||
.addClass('layui-this') |
||||
.siblings().removeClass('layui-this'); |
||||
|
||||
$('#resultDiv').hide(); |
||||
} |
@ -0,0 +1,146 @@ |
||||
package com.gitee.sop.websiteserver.controller; |
||||
|
||||
import com.gitee.sop.websiteserver.sign.AlipayApiException; |
||||
import com.gitee.sop.websiteserver.sign.AlipaySignature; |
||||
import lombok.Data; |
||||
import org.apache.commons.lang.StringUtils; |
||||
import org.apache.http.NameValuePair; |
||||
import org.apache.http.client.entity.UrlEncodedFormEntity; |
||||
import org.apache.http.client.methods.CloseableHttpResponse; |
||||
import org.apache.http.client.methods.HttpGet; |
||||
import org.apache.http.impl.client.CloseableHttpClient; |
||||
import org.apache.http.impl.client.HttpClients; |
||||
import org.apache.http.message.BasicNameValuePair; |
||||
import org.apache.http.util.EntityUtils; |
||||
import org.apache.tomcat.util.http.fileupload.IOUtils; |
||||
import org.springframework.beans.factory.annotation.Value; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.stream.Collectors; |
||||
|
||||
/** |
||||
* 沙箱环境代理类 |
||||
* @author tanghc |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("sandbox") |
||||
public class SandboxController { |
||||
|
||||
@Value("${api.url-sandbox}") |
||||
private String url; |
||||
|
||||
@RequestMapping("/test") |
||||
public SandboxResult proxy( |
||||
@RequestParam String appId |
||||
, @RequestParam String privateKey |
||||
, @RequestParam String method |
||||
, @RequestParam String version |
||||
, @RequestParam String bizContent) throws AlipayApiException { |
||||
|
||||
Assert.isTrue(StringUtils.isNotBlank(appId), "appId不能为空"); |
||||
Assert.isTrue(StringUtils.isNotBlank(privateKey), "privateKey不能为空"); |
||||
Assert.isTrue(StringUtils.isNotBlank(method), "method不能为空"); |
||||
|
||||
// 公共请求参数
|
||||
Map<String, String> params = new HashMap<String, String>(); |
||||
params.put("app_id", appId); |
||||
params.put("method", method); |
||||
params.put("format", "json"); |
||||
params.put("charset", "utf-8"); |
||||
params.put("sign_type", "RSA2"); |
||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); |
||||
params.put("version", version); |
||||
|
||||
// 业务参数
|
||||
params.put("biz_content", bizContent); |
||||
|
||||
SandboxResult result = new SandboxResult(); |
||||
|
||||
result.params = buildParamQuery(params); |
||||
|
||||
String content = AlipaySignature.getSignContent(params); |
||||
result.beforeSign = content; |
||||
|
||||
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8"); |
||||
result.sign = sign; |
||||
|
||||
params.put("sign", sign); |
||||
|
||||
String responseData = get(url, params);// 发送请求
|
||||
result.apiResult = responseData; |
||||
return result; |
||||
} |
||||
|
||||
@Data |
||||
public static class SandboxResult { |
||||
private String params; |
||||
private String beforeSign; |
||||
private String sign; |
||||
|
||||
private String apiResult; |
||||
} |
||||
|
||||
/** |
||||
* 发送get请求 |
||||
* @param url |
||||
* @return JSON或者字符串 |
||||
* @throws Exception |
||||
*/ |
||||
public static String get(String url, Map<String, String> params) { |
||||
CloseableHttpClient client = null; |
||||
CloseableHttpResponse response = null; |
||||
try{ |
||||
/** |
||||
* 创建一个httpclient对象 |
||||
*/ |
||||
client = HttpClients.createDefault(); |
||||
|
||||
List<NameValuePair> nameValuePairs = params.entrySet() |
||||
.stream() |
||||
.map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))) |
||||
.collect(Collectors.toList()); |
||||
/** |
||||
* 包装成一个Entity对象 |
||||
*/ |
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs, "UTF-8"); |
||||
//参数转换为字符串
|
||||
String paramsStr = EntityUtils.toString(entity); |
||||
url = url + "?" + paramsStr; |
||||
/** |
||||
* 创建一个post对象 |
||||
*/ |
||||
HttpGet get = new HttpGet(url); |
||||
|
||||
/** |
||||
* 执行post请求 |
||||
*/ |
||||
response = client.execute(get); |
||||
/** |
||||
* 通过EntityUitls获取返回内容 |
||||
*/ |
||||
return EntityUtils.toString(response.getEntity(),"UTF-8"); |
||||
}catch (Exception e){ |
||||
e.printStackTrace(); |
||||
}finally { |
||||
IOUtils.closeQuietly(client); |
||||
IOUtils.closeQuietly(response); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
protected String buildParamQuery(Map<String, String> params) { |
||||
StringBuilder sb = new StringBuilder(); |
||||
for (Map.Entry<String, String> entry : params.entrySet()) { |
||||
sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); |
||||
} |
||||
return sb.toString().substring(1); |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.websiteserver.sign; |
||||
|
||||
|
||||
/** |
||||
* |
||||
* @author runzhi |
||||
*/ |
||||
public class AlipayApiException extends Exception { |
||||
|
||||
private static final long serialVersionUID = -238091758285157331L; |
||||
|
||||
private String errCode; |
||||
private String errMsg; |
||||
|
||||
public AlipayApiException() { |
||||
super(); |
||||
} |
||||
|
||||
public AlipayApiException(String message, Throwable cause) { |
||||
super(message, cause); |
||||
} |
||||
|
||||
public AlipayApiException(String message) { |
||||
super(message); |
||||
} |
||||
|
||||
public AlipayApiException(Throwable cause) { |
||||
super(cause); |
||||
} |
||||
|
||||
public AlipayApiException(String errCode, String errMsg) { |
||||
super(errCode + ":" + errMsg); |
||||
this.errCode = errCode; |
||||
this.errMsg = errMsg; |
||||
} |
||||
|
||||
public String getErrCode() { |
||||
return this.errCode; |
||||
} |
||||
|
||||
public String getErrMsg() { |
||||
return this.errMsg; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,97 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.websiteserver.sign; |
||||
|
||||
/** |
||||
* |
||||
* @author runzhi |
||||
*/ |
||||
public class AlipayConstants { |
||||
|
||||
public static final String SIGN_TYPE = "sign_type"; |
||||
|
||||
public static final String SIGN_TYPE_RSA = "RSA"; |
||||
|
||||
/** |
||||
* sha256WithRsa 算法请求类型 |
||||
*/ |
||||
public static final String SIGN_TYPE_RSA2 = "RSA2"; |
||||
|
||||
public static final String SIGN_ALGORITHMS = "SHA1WithRSA"; |
||||
|
||||
public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA"; |
||||
|
||||
public static final String ENCRYPT_TYPE_AES = "AES"; |
||||
|
||||
public static final String APP_ID = "app_id"; |
||||
|
||||
public static final String FORMAT = "format"; |
||||
|
||||
public static final String METHOD = "method"; |
||||
|
||||
public static final String TIMESTAMP = "timestamp"; |
||||
|
||||
public static final String VERSION = "version"; |
||||
|
||||
public static final String SIGN = "sign"; |
||||
|
||||
public static final String ALIPAY_SDK = "alipay_sdk"; |
||||
|
||||
public static final String ACCESS_TOKEN = "auth_token"; |
||||
|
||||
public static final String APP_AUTH_TOKEN = "app_auth_token"; |
||||
|
||||
public static final String TERMINAL_TYPE = "terminal_type"; |
||||
|
||||
public static final String TERMINAL_INFO = "terminal_info"; |
||||
|
||||
public static final String CHARSET = "charset"; |
||||
|
||||
public static final String NOTIFY_URL = "notify_url"; |
||||
|
||||
public static final String RETURN_URL = "return_url"; |
||||
|
||||
public static final String ENCRYPT_TYPE = "encrypt_type"; |
||||
|
||||
//-----===-------///
|
||||
|
||||
public static final String BIZ_CONTENT_KEY = "biz_content"; |
||||
|
||||
/** 默认时间格式 **/ |
||||
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
||||
|
||||
/** Date默认时区 **/ |
||||
public static final String DATE_TIMEZONE = "GMT+8"; |
||||
|
||||
/** UTF-8字符集 **/ |
||||
public static final String CHARSET_UTF8 = "UTF-8"; |
||||
|
||||
/** GBK字符集 **/ |
||||
public static final String CHARSET_GBK = "GBK"; |
||||
|
||||
/** JSON 应格式 */ |
||||
public static final String FORMAT_JSON = "json"; |
||||
|
||||
/** XML 应格式 */ |
||||
public static final String FORMAT_XML = "xml"; |
||||
|
||||
/** SDK版本号 */ |
||||
public static final String SDK_VERSION = "alipay-sdk-java-3.6.0.ALL"; |
||||
|
||||
public static final String PROD_CODE = "prod_code"; |
||||
|
||||
/** 老版本失败节点 */ |
||||
public static final String ERROR_RESPONSE = "error_response"; |
||||
|
||||
/** 新版本节点后缀 */ |
||||
public static final String RESPONSE_SUFFIX = "_response"; |
||||
|
||||
/** 加密后XML返回报文的节点名字 */ |
||||
public static final String RESPONSE_XML_ENCRYPT_NODE_NAME = "response_encrypted"; |
||||
|
||||
/** 批量请求id **/ |
||||
public static final String BATCH_REQUEST_ID = "batch_request_id"; |
||||
|
||||
} |
@ -0,0 +1,611 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.websiteserver.sign; |
||||
|
||||
|
||||
import org.apache.commons.codec.binary.Base64; |
||||
|
||||
import javax.crypto.Cipher; |
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.io.StringWriter; |
||||
import java.security.KeyFactory; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.spec.InvalidKeySpecException; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* |
||||
* @author runzhi |
||||
*/ |
||||
public class AlipaySignature { |
||||
|
||||
/** RSA最大加密明文大小 */ |
||||
private static final int MAX_ENCRYPT_BLOCK = 117; |
||||
|
||||
/** RSA最大解密密文大小 */ |
||||
private static final int MAX_DECRYPT_BLOCK = 128; |
||||
|
||||
|
||||
|
||||
/** |
||||
* |
||||
* @param sortedParams |
||||
* @return |
||||
*/ |
||||
public static String getSignContent(Map<String, String> sortedParams) { |
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(sortedParams.keySet()); |
||||
Collections.sort(keys); |
||||
int index = 0; |
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = sortedParams.get(key); |
||||
if (StringUtils.areNotEmpty(key, value)) { |
||||
content.append((index == 0 ? "" : "&") + key + "=" + value); |
||||
index++; |
||||
} |
||||
} |
||||
return content.toString(); |
||||
} |
||||
|
||||
/** |
||||
* rsa内容签名 |
||||
* |
||||
* @param content |
||||
* @param privateKey |
||||
* @param charset |
||||
* @return |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String rsaSign(String content, String privateKey, String charset, |
||||
String signType) throws AlipayApiException { |
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||
|
||||
return rsaSign(content, privateKey, charset); |
||||
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||
|
||||
return rsa256Sign(content, privateKey, charset); |
||||
} else { |
||||
|
||||
throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* sha256WithRsa 加签 |
||||
* |
||||
* @param content |
||||
* @param privateKey |
||||
* @param charset |
||||
* @return |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String rsa256Sign(String content, String privateKey, |
||||
String charset) throws AlipayApiException { |
||||
|
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(privateKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||
|
||||
signature.initSign(priKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
byte[] signed = signature.sign(); |
||||
|
||||
return new String(Base64.encodeBase64(signed)); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* sha1WithRsa 加签 |
||||
* |
||||
* @param content |
||||
* @param privateKey |
||||
* @param charset |
||||
* @return |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String rsaSign(String content, String privateKey, |
||||
String charset) throws AlipayApiException { |
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(privateKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||
|
||||
signature.initSign(priKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
byte[] signed = signature.sign(); |
||||
|
||||
return new String(Base64.encodeBase64(signed)); |
||||
} catch (InvalidKeySpecException ie) { |
||||
throw new AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); |
||||
} |
||||
} |
||||
|
||||
public static String rsaSign(Map<String, String> params, String privateKey, |
||||
String charset) throws AlipayApiException { |
||||
String signContent = getSignContent(params); |
||||
|
||||
return rsaSign(signContent, privateKey, charset); |
||||
|
||||
} |
||||
|
||||
public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, |
||||
InputStream ins) throws Exception { |
||||
if (ins == null || StringUtils.isEmpty(algorithm)) { |
||||
return null; |
||||
} |
||||
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||
|
||||
byte[] encodedKey = StreamUtil.readText(ins).getBytes(); |
||||
|
||||
encodedKey = Base64.decodeBase64(encodedKey); |
||||
|
||||
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); |
||||
} |
||||
|
||||
public static String getSignCheckContentV1(Map<String, String> params) { |
||||
if (params == null) { |
||||
return null; |
||||
} |
||||
|
||||
params.remove("sign"); |
||||
params.remove("sign_type"); |
||||
|
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(params.keySet()); |
||||
Collections.sort(keys); |
||||
|
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = params.get(key); |
||||
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||
} |
||||
|
||||
return content.toString(); |
||||
} |
||||
|
||||
public static String getSignCheckContentV2(Map<String, String> params) { |
||||
if (params == null) { |
||||
return null; |
||||
} |
||||
|
||||
params.remove("sign"); |
||||
|
||||
StringBuffer content = new StringBuffer(); |
||||
List<String> keys = new ArrayList<String>(params.keySet()); |
||||
Collections.sort(keys); |
||||
|
||||
for (int i = 0; i < keys.size(); i++) { |
||||
String key = keys.get(i); |
||||
String value = params.get(key); |
||||
content.append((i == 0 ? "" : "&") + key + "=" + value); |
||||
} |
||||
|
||||
return content.toString(); |
||||
} |
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||
String charset) throws AlipayApiException { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV1(params); |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
} |
||||
|
||||
public static boolean rsaCheckV1(Map<String, String> params, String publicKey, |
||||
String charset,String signType) throws AlipayApiException { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV1(params); |
||||
|
||||
return rsaCheck(content, sign, publicKey, charset,signType); |
||||
} |
||||
|
||||
public static boolean rsaCheckV2(Map<String, String> params, String publicKey, |
||||
String charset) throws AlipayApiException { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV2(params); |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
} |
||||
|
||||
public static boolean rsaCheckV2(Map<String, String> params, String publicKey, |
||||
String charset,String signType) throws AlipayApiException { |
||||
String sign = params.get("sign"); |
||||
String content = getSignCheckContentV2(params); |
||||
|
||||
return rsaCheck(content, sign, publicKey, charset,signType); |
||||
} |
||||
|
||||
public static boolean rsaCheck(String content, String sign, String publicKey, String charset, |
||||
String signType) throws AlipayApiException { |
||||
|
||||
if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { |
||||
|
||||
return rsaCheckContent(content, sign, publicKey, charset); |
||||
|
||||
} else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { |
||||
|
||||
return rsa256CheckContent(content, sign, publicKey, charset); |
||||
|
||||
} else { |
||||
|
||||
throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); |
||||
} |
||||
|
||||
} |
||||
|
||||
public static boolean rsa256CheckContent(String content, String sign, String publicKey, |
||||
String charset) throws AlipayApiException { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_SHA256RSA_ALGORITHMS); |
||||
|
||||
signature.initVerify(pubKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException( |
||||
"RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); |
||||
} |
||||
} |
||||
|
||||
public static boolean rsaCheckContent(String content, String sign, String publicKey, |
||||
String charset) throws AlipayApiException { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509("RSA", |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
|
||||
java.security.Signature signature = java.security.Signature |
||||
.getInstance(AlipayConstants.SIGN_ALGORITHMS); |
||||
|
||||
signature.initVerify(pubKey); |
||||
|
||||
if (StringUtils.isEmpty(charset)) { |
||||
signature.update(content.getBytes()); |
||||
} else { |
||||
signature.update(content.getBytes(charset)); |
||||
} |
||||
|
||||
return signature.verify(Base64.decodeBase64(sign.getBytes())); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException( |
||||
"RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); |
||||
} |
||||
} |
||||
|
||||
public static PublicKey getPublicKeyFromX509(String algorithm, |
||||
InputStream ins) throws Exception { |
||||
KeyFactory keyFactory = KeyFactory.getInstance(algorithm); |
||||
|
||||
StringWriter writer = new StringWriter(); |
||||
StreamUtil.io(new InputStreamReader(ins), writer); |
||||
|
||||
byte[] encodedKey = writer.toString().getBytes(); |
||||
|
||||
encodedKey = Base64.decodeBase64(encodedKey); |
||||
|
||||
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); |
||||
} |
||||
|
||||
/** |
||||
* 验签并解密 |
||||
* <p> |
||||
* <b>目前适用于公众号</b><br> |
||||
* params参数示例: |
||||
* <br>{ |
||||
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||
* sign_type=RSA, |
||||
* <br>charset=UTF-8 |
||||
* <br>} |
||||
* </p> |
||||
* @param params |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param isCheckSign 是否验签 |
||||
* @param isDecrypt 是否解密 |
||||
* @return 解密后明文,验签失败则异常抛出 |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||
String cusPrivateKey, boolean isCheckSign, |
||||
boolean isDecrypt) throws AlipayApiException { |
||||
String charset = params.get("charset"); |
||||
String bizContent = params.get("biz_content"); |
||||
if (isCheckSign) { |
||||
if (!rsaCheckV2(params, alipayPublicKey, charset)) { |
||||
throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); |
||||
} |
||||
} |
||||
|
||||
if (isDecrypt) { |
||||
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||
} |
||||
|
||||
return bizContent; |
||||
} |
||||
|
||||
/** |
||||
* 验签并解密 |
||||
* <p> |
||||
* <b>目前适用于公众号</b><br> |
||||
* params参数示例: |
||||
* <br>{ |
||||
* <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, |
||||
* <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, |
||||
* sign_type=RSA, |
||||
* <br>charset=UTF-8 |
||||
* <br>} |
||||
* </p> |
||||
* @param params |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param isCheckSign 是否验签 |
||||
* @param isDecrypt 是否解密 |
||||
* @return 解密后明文,验签失败则异常抛出 |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String checkSignAndDecrypt(Map<String, String> params, String alipayPublicKey, |
||||
String cusPrivateKey, boolean isCheckSign, |
||||
boolean isDecrypt, String signType) throws AlipayApiException { |
||||
String charset = params.get("charset"); |
||||
String bizContent = params.get("biz_content"); |
||||
if (isCheckSign) { |
||||
if (!rsaCheckV2(params, alipayPublicKey, charset,signType)) { |
||||
throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); |
||||
} |
||||
} |
||||
|
||||
if (isDecrypt) { |
||||
return rsaDecrypt(bizContent, cusPrivateKey, charset); |
||||
} |
||||
|
||||
return bizContent; |
||||
} |
||||
|
||||
/** |
||||
* 加密并签名<br> |
||||
* <b>目前适用于公众号</b> |
||||
* @param bizContent 待加密、签名内容 |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||
* @param isSign 是否签名,true-签名 false-不签名 |
||||
* @return 加密、签名后xml内容字符串 |
||||
* <p> |
||||
* 返回示例: |
||||
* <alipay> |
||||
* <response>密文</response> |
||||
* <encryption_type>RSA</encryption_type> |
||||
* <sign>sign</sign> |
||||
* <sign_type>RSA</sign_type> |
||||
* </alipay> |
||||
* </p> |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||
String cusPrivateKey, String charset, boolean isEncrypt, |
||||
boolean isSign) throws AlipayApiException { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (StringUtils.isEmpty(charset)) { |
||||
charset = AlipayConstants.CHARSET_GBK; |
||||
} |
||||
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||
if (isEncrypt) {// 加密
|
||||
sb.append("<alipay>"); |
||||
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||
sb.append("<response>" + encrypted + "</response>"); |
||||
sb.append("<encryption_type>RSA</encryption_type>"); |
||||
if (isSign) { |
||||
String sign = rsaSign(encrypted, cusPrivateKey, charset); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>RSA</sign_type>"); |
||||
} |
||||
sb.append("</alipay>"); |
||||
} else if (isSign) {// 不加密,但需要签名
|
||||
sb.append("<alipay>"); |
||||
sb.append("<response>" + bizContent + "</response>"); |
||||
String sign = rsaSign(bizContent, cusPrivateKey, charset); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>RSA</sign_type>"); |
||||
sb.append("</alipay>"); |
||||
} else {// 不加密,不加签
|
||||
sb.append(bizContent); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 加密并签名<br> |
||||
* <b>目前适用于公众号</b> |
||||
* @param bizContent 待加密、签名内容 |
||||
* @param alipayPublicKey 支付宝公钥 |
||||
* @param cusPrivateKey 商户私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @param isEncrypt 是否加密,true-加密 false-不加密 |
||||
* @param isSign 是否签名,true-签名 false-不签名 |
||||
* @return 加密、签名后xml内容字符串 |
||||
* <p> |
||||
* 返回示例: |
||||
* <alipay> |
||||
* <response>密文</response> |
||||
* <encryption_type>RSA</encryption_type> |
||||
* <sign>sign</sign> |
||||
* <sign_type>RSA</sign_type> |
||||
* </alipay> |
||||
* </p> |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String encryptAndSign(String bizContent, String alipayPublicKey, |
||||
String cusPrivateKey, String charset, boolean isEncrypt, |
||||
boolean isSign,String signType) throws AlipayApiException { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (StringUtils.isEmpty(charset)) { |
||||
charset = AlipayConstants.CHARSET_GBK; |
||||
} |
||||
sb.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>"); |
||||
if (isEncrypt) {// 加密
|
||||
sb.append("<alipay>"); |
||||
String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); |
||||
sb.append("<response>" + encrypted + "</response>"); |
||||
sb.append("<encryption_type>RSA</encryption_type>"); |
||||
if (isSign) { |
||||
String sign = rsaSign(encrypted, cusPrivateKey, charset, signType); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>"); |
||||
sb.append(signType); |
||||
sb.append("</sign_type>"); |
||||
} |
||||
sb.append("</alipay>"); |
||||
} else if (isSign) {// 不加密,但需要签名
|
||||
sb.append("<alipay>"); |
||||
sb.append("<response>" + bizContent + "</response>"); |
||||
String sign = rsaSign(bizContent, cusPrivateKey, charset, signType); |
||||
sb.append("<sign>" + sign + "</sign>"); |
||||
sb.append("<sign_type>"); |
||||
sb.append(signType); |
||||
sb.append("</sign_type>"); |
||||
sb.append("</alipay>"); |
||||
} else {// 不加密,不加签
|
||||
sb.append(bizContent); |
||||
} |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 公钥加密 |
||||
* |
||||
* @param content 待加密内容 |
||||
* @param publicKey 公钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @return 密文内容 |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String rsaEncrypt(String content, String publicKey, |
||||
String charset) throws AlipayApiException { |
||||
try { |
||||
PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(publicKey.getBytes())); |
||||
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||
cipher.init(Cipher.ENCRYPT_MODE, pubKey); |
||||
byte[] data = StringUtils.isEmpty(charset) ? content.getBytes() |
||||
: content.getBytes(charset); |
||||
int inputLen = data.length; |
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||
int offSet = 0; |
||||
byte[] cache; |
||||
int i = 0; |
||||
// 对数据分段加密
|
||||
while (inputLen - offSet > 0) { |
||||
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { |
||||
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK); |
||||
} else { |
||||
cache = cipher.doFinal(data, offSet, inputLen - offSet); |
||||
} |
||||
out.write(cache, 0, cache.length); |
||||
i++; |
||||
offSet = i * MAX_ENCRYPT_BLOCK; |
||||
} |
||||
byte[] encryptedData = Base64.encodeBase64(out.toByteArray()); |
||||
out.close(); |
||||
|
||||
return StringUtils.isEmpty(charset) ? new String(encryptedData) |
||||
: new String(encryptedData, charset); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException("EncryptContent = " + content + ",charset = " + charset, |
||||
e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 私钥解密 |
||||
* |
||||
* @param content 待解密内容 |
||||
* @param privateKey 私钥 |
||||
* @param charset 字符集,如UTF-8, GBK, GB2312 |
||||
* @return 明文内容 |
||||
* @throws AlipayApiException |
||||
*/ |
||||
public static String rsaDecrypt(String content, String privateKey, |
||||
String charset) throws AlipayApiException { |
||||
try { |
||||
PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, |
||||
new ByteArrayInputStream(privateKey.getBytes())); |
||||
Cipher cipher = Cipher.getInstance(AlipayConstants.SIGN_TYPE_RSA); |
||||
cipher.init(Cipher.DECRYPT_MODE, priKey); |
||||
byte[] encryptedData = StringUtils.isEmpty(charset) |
||||
? Base64.decodeBase64(content.getBytes()) |
||||
: Base64.decodeBase64(content.getBytes(charset)); |
||||
int inputLen = encryptedData.length; |
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
||||
int offSet = 0; |
||||
byte[] cache; |
||||
int i = 0; |
||||
// 对数据分段解密
|
||||
while (inputLen - offSet > 0) { |
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) { |
||||
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); |
||||
} else { |
||||
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); |
||||
} |
||||
out.write(cache, 0, cache.length); |
||||
i++; |
||||
offSet = i * MAX_DECRYPT_BLOCK; |
||||
} |
||||
byte[] decryptedData = out.toByteArray(); |
||||
out.close(); |
||||
|
||||
return StringUtils.isEmpty(charset) ? new String(decryptedData) |
||||
: new String(decryptedData, charset); |
||||
} catch (Exception e) { |
||||
throw new AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,139 @@ |
||||
/** |
||||
* Alipay.com Inc. |
||||
* Copyright (c) 2004-2012 All Rights Reserved. |
||||
*/ |
||||
package com.gitee.sop.websiteserver.sign; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.InputStreamReader; |
||||
import java.io.OutputStream; |
||||
import java.io.Reader; |
||||
import java.io.StringWriter; |
||||
import java.io.Writer; |
||||
|
||||
/** |
||||
* |
||||
* @author runzhi |
||||
*/ |
||||
public class StreamUtil { |
||||
private static final int DEFAULT_BUFFER_SIZE = 8192; |
||||
|
||||
public static void io(InputStream in, OutputStream out) throws IOException { |
||||
io(in, out, -1); |
||||
} |
||||
|
||||
public static void io(InputStream in, OutputStream out, int bufferSize) throws IOException { |
||||
if (bufferSize == -1) { |
||||
bufferSize = DEFAULT_BUFFER_SIZE; |
||||
} |
||||
|
||||
byte[] buffer = new byte[bufferSize]; |
||||
int amount; |
||||
|
||||
while ((amount = in.read(buffer)) >= 0) { |
||||
out.write(buffer, 0, amount); |
||||
} |
||||
} |
||||
|
||||
public static void io(Reader in, Writer out) throws IOException { |
||||
io(in, out, -1); |
||||
} |
||||
|
||||
public static void io(Reader in, Writer out, int bufferSize) throws IOException { |
||||
if (bufferSize == -1) { |
||||
bufferSize = DEFAULT_BUFFER_SIZE >> 1; |
||||
} |
||||
|
||||
char[] buffer = new char[bufferSize]; |
||||
int amount; |
||||
|
||||
while ((amount = in.read(buffer)) >= 0) { |
||||
out.write(buffer, 0, amount); |
||||
} |
||||
} |
||||
|
||||
public static OutputStream synchronizedOutputStream(OutputStream out) { |
||||
return new SynchronizedOutputStream(out); |
||||
} |
||||
|
||||
public static OutputStream synchronizedOutputStream(OutputStream out, Object lock) { |
||||
return new SynchronizedOutputStream(out, lock); |
||||
} |
||||
|
||||
public static String readText(InputStream in) throws IOException { |
||||
return readText(in, null, -1); |
||||
} |
||||
|
||||
public static String readText(InputStream in, String encoding) throws IOException { |
||||
return readText(in, encoding, -1); |
||||
} |
||||
|
||||
public static String readText(InputStream in, String encoding, int bufferSize) |
||||
throws IOException { |
||||
Reader reader = (encoding == null) ? new InputStreamReader(in) : new InputStreamReader(in, |
||||
encoding); |
||||
|
||||
return readText(reader, bufferSize); |
||||
} |
||||
|
||||
public static String readText(Reader reader) throws IOException { |
||||
return readText(reader, -1); |
||||
} |
||||
|
||||
public static String readText(Reader reader, int bufferSize) throws IOException { |
||||
StringWriter writer = new StringWriter(); |
||||
|
||||
io(reader, writer, bufferSize); |
||||
return writer.toString(); |
||||
} |
||||
|
||||
private static class SynchronizedOutputStream extends OutputStream { |
||||
private OutputStream out; |
||||
private Object lock; |
||||
|
||||
SynchronizedOutputStream(OutputStream out) { |
||||
this(out, out); |
||||
} |
||||
|
||||
SynchronizedOutputStream(OutputStream out, Object lock) { |
||||
this.out = out; |
||||
this.lock = lock; |
||||
} |
||||
|
||||
@Override |
||||
public void write(int datum) throws IOException { |
||||
synchronized (lock) { |
||||
out.write(datum); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] data) throws IOException { |
||||
synchronized (lock) { |
||||
out.write(data); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] data, int offset, int length) throws IOException { |
||||
synchronized (lock) { |
||||
out.write(data, offset, length); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void flush() throws IOException { |
||||
synchronized (lock) { |
||||
out.flush(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
synchronized (lock) { |
||||
out.close(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,171 @@ |
||||
package com.gitee.sop.websiteserver.sign; |
||||
|
||||
/** |
||||
* 字符串工具类。 |
||||
* |
||||
* @author carver.gu |
||||
* @since 1.0, Sep 12, 2009 |
||||
*/ |
||||
public abstract class StringUtils { |
||||
|
||||
private StringUtils() {} |
||||
|
||||
/** |
||||
* 检查指定的字符串是否为空。 |
||||
* <ul> |
||||
* <li>SysUtils.isEmpty(null) = true</li> |
||||
* <li>SysUtils.isEmpty("") = true</li> |
||||
* <li>SysUtils.isEmpty(" ") = true</li> |
||||
* <li>SysUtils.isEmpty("abc") = false</li> |
||||
* </ul> |
||||
* |
||||
* @param value 待检查的字符串 |
||||
* @return true/false |
||||
*/ |
||||
public static boolean isEmpty(String value) { |
||||
int strLen; |
||||
if (value == null || (strLen = value.length()) == 0) { |
||||
return true; |
||||
} |
||||
for (int i = 0; i < strLen; i++) { |
||||
if ((Character.isWhitespace(value.charAt(i)) == false)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 检查对象是否为数字型字符串,包含负数开头的。 |
||||
*/ |
||||
public static boolean isNumeric(Object obj) { |
||||
if (obj == null) { |
||||
return false; |
||||
} |
||||
char[] chars = obj.toString().toCharArray(); |
||||
int length = chars.length; |
||||
if(length < 1) { |
||||
return false; |
||||
} |
||||
|
||||
int i = 0; |
||||
if(length > 1 && chars[0] == '-') { |
||||
i = 1; |
||||
} |
||||
|
||||
for (; i < length; i++) { |
||||
if (!Character.isDigit(chars[i])) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 检查指定的字符串列表是否不为空。 |
||||
*/ |
||||
public static boolean areNotEmpty(String... values) { |
||||
boolean result = true; |
||||
if (values == null || values.length == 0) { |
||||
result = false; |
||||
} else { |
||||
for (String value : values) { |
||||
result &= !isEmpty(value); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* 把通用字符编码的字符串转化为汉字编码。 |
||||
*/ |
||||
public static String unicodeToChinese(String unicode) { |
||||
StringBuilder out = new StringBuilder(); |
||||
if (!isEmpty(unicode)) { |
||||
for (int i = 0; i < unicode.length(); i++) { |
||||
out.append(unicode.charAt(i)); |
||||
} |
||||
} |
||||
return out.toString(); |
||||
} |
||||
|
||||
/** |
||||
* 过滤不可见字符 |
||||
*/ |
||||
public static String stripNonValidXMLCharacters(String input) { |
||||
if (input == null || ("".equals(input))) { |
||||
return ""; |
||||
} |
||||
StringBuilder out = new StringBuilder(); |
||||
char current; |
||||
for (int i = 0; i < input.length(); i++) { |
||||
current = input.charAt(i); |
||||
if ((current == 0x9) || (current == 0xA) || (current == 0xD) |
||||
|| ((current >= 0x20) && (current <= 0xD7FF)) |
||||
|| ((current >= 0xE000) && (current <= 0xFFFD)) |
||||
|| ((current >= 0x10000) && (current <= 0x10FFFF))) { |
||||
out.append(current); |
||||
} |
||||
} |
||||
return out.toString(); |
||||
} |
||||
|
||||
public static String leftPad(String str, int size, char padChar) { |
||||
if (str == null) { |
||||
return null; |
||||
} else { |
||||
int pads = size - str.length(); |
||||
if (pads <= 0) { |
||||
return str; |
||||
} else { |
||||
return pads > 8192 ? leftPad(str, size, String.valueOf(padChar)) : padding(pads, padChar).concat(str); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static String leftPad(String str, int size, String padStr) { |
||||
if (str == null) { |
||||
return null; |
||||
} else { |
||||
if (isEmpty(padStr)) { |
||||
padStr = " "; |
||||
} |
||||
|
||||
int padLen = padStr.length(); |
||||
int strLen = str.length(); |
||||
int pads = size - strLen; |
||||
if (pads <= 0) { |
||||
return str; |
||||
} else if (padLen == 1 && pads <= 8192) { |
||||
return leftPad(str, size, padStr.charAt(0)); |
||||
} else if (pads == padLen) { |
||||
return padStr.concat(str); |
||||
} else if (pads < padLen) { |
||||
return padStr.substring(0, pads).concat(str); |
||||
} else { |
||||
char[] padding = new char[pads]; |
||||
char[] padChars = padStr.toCharArray(); |
||||
|
||||
for(int i = 0; i < pads; ++i) { |
||||
padding[i] = padChars[i % padLen]; |
||||
} |
||||
|
||||
return (new String(padding)).concat(str); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static String padding(int repeat, char padChar) throws IndexOutOfBoundsException { |
||||
if (repeat < 0) { |
||||
throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat); |
||||
} else { |
||||
char[] buf = new char[repeat]; |
||||
|
||||
for(int i = 0; i < buf.length; ++i) { |
||||
buf[i] = padChar; |
||||
} |
||||
|
||||
return new String(buf); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue