本文档说明如何使用e签宝官方SDK(paas-sdk-3.1.4.jar)实现OAuth2.0鉴权方式的手写签署功能。
e签宝使用OAuth2.0客户端凭证模式(client_credentials)进行API鉴权:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 应用 │ │ e签宝API │ │ e签宝 │
│ │ │ 网关 │ │ 认证服务 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ 1. 请求access_token │ │
│ (AppId + AppSecret) │ │
├─────────────────────────────>│ │
│ ├─────────────────────────────>│
│ │ │
│ │ 2. 返回access_token │
│ │<─────────────────────────────┤
│ 3. 返回access_token │ │
│<─────────────────────────────┤ │
│ │ │
│ 4. 使用access_token调用API │ │
├─────────────────────────────>│ │
│ │ │
│ 5. 返回API响应 │ │
│<─────────────────────────────┤ │
│ │ │
请求方式: GET
请求URL: {apiUrl}/v1/oauth2/access_token
请求参数:
appId: 应用IDsecret: 应用密钥grantType: 固定值 client_credentials请求头:
X-Tsign-Open-App-Id: {appId}
X-Tsign-Open-App-Secret: {secret}
Content-Type: application/json
响应示例:
{
"code": 0,
"message": "成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 7200,
"refreshToken": "refresh_token_string"
}
}
Token有效期: 通常为7200秒(2小时),需要定期刷新
所有API请求需要在请求头中携带access_token:
请求头:
X-Tsign-Open-App-Id: {appId}
X-Tsign-Open-Token: {access_token}
Content-Type: application/json; charset=UTF-8
┌─────────────────────────────────────────────────────────────┐
│ 步骤1: OAuth2.0获取访问令牌 │
│ POST /v1/oauth2/access_token │
│ 输入: AppId + AppSecret │
│ 输出: access_token │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤2: 创建或获取签署人账号 │
│ POST /v3/accounts/createByThirdPartyUserId │
│ 输入: mobile + name + idNumber │
│ 输出: accountId │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤3: 上传合同文件(如需) │
│ POST /v3/files/getUploadUrl │
│ 输入: fileName + fileSize │
│ 输出: uploadUrl + fileId │
│ 然后: PUT uploadUrl (上传文件) │
│ 最后: POST /v3/files/{fileId}/uploadComplete │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤4: 创建签署流程 │
│ POST /v3/signflows │
│ 输入: docs + signers (包含手写签署区域) │
│ 输出: flowId │
│ │
│ 关键配置: │
│ - signFieldStyle: 1 (手写签名) │
│ - signFieldType: 5 (签名类型) │
│ - autoExecute: false (手动签署) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤5: 启动签署流程 │
│ POST /v3/signflows/{flowId}/start │
│ 输入: flowId │
│ 输出: 成功/失败 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤6: 获取手写签署链接 │
│ GET /v3/signflows/{flowId}/signers/{accountId}/signUrl │
│ 输入: flowId + accountId │
│ 输出: signUrl (签署人通过此链接进行手写签名) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤7: 签署人进行手写签署 │
│ 打开signUrl,在签署页面进行手写签名 │
│ - 可以在手机或PC端进行手写 │
│ - 支持触摸屏手写和鼠标手写 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤8: 查询签署状态 │
│ GET /v3/signflows/{flowId} │
│ 输入: flowId │
│ 输出: flowStatus (0-草稿, 1-签署中, 2-签署完成, ...) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 步骤9: 下载已签署文档 │
│ GET /v3/signflows/{flowId}/documents │
│ 输入: flowId │
│ 输出: 已签署的PDF文档下载地址 │
└─────────────────────────────────────────────────────────────┘
package com.loan.system.utils;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* e签宝OAuth2.0鉴权工具类
* 使用OAuth2.0客户端凭证模式(client_credentials)获取访问令牌
*/
@Slf4j
public class EsignSdkUtil {
private static volatile String cachedToken;
private static volatile long tokenExpireTime;
/**
* 获取OAuth2.0访问令牌
*/
public static String getAccessToken(String appId, String appSecret, String apiUrl) {
// 检查缓存的token是否仍然有效
if (cachedToken != null && System.currentTimeMillis() < (tokenExpireTime - 5 * 60 * 1000)) {
return cachedToken;
}
String url = apiUrl + "/v1/oauth2/access_token";
String getUrl = url + "?appId=" + appId + "&secret=" + appSecret + "&grantType=client_credentials";
HttpResponse response = HttpRequest.get(getUrl)
.header("X-Tsign-Open-App-Id", appId)
.header("X-Tsign-Open-App-Secret", appSecret)
.execute();
JSONObject result = JSONUtil.parseObj(response.body());
if (result.getInt("code") == 0) {
JSONObject data = result.getJSONObject("data");
String token = data.getStr("token");
Integer expiresIn = data.getInt("expiresIn", 7200);
cachedToken = token;
tokenExpireTime = System.currentTimeMillis() + expiresIn * 1000L;
return token;
}
return null;
}
}
在 EsignServiceImpl.createSignFlow() 方法中,关键配置如下:
// 创建手写签署区域
private List<Map<String, Object>> createSignFields(int signOrder, String signerName) {
List<Map<String, Object>> fields = new ArrayList<>();
Map<String, Object> field = new HashMap<>();
field.put("fileId", 1); // 文档序号
field.put("signerName", signerName);
field.put("autoExecute", false); // 不自动执行,需要手动签署
field.put("signFieldType", 5); // 5-签名类型
field.put("signFieldStyle", 1); // 1-手写签名(重要!)
field.put("signFieldStyleType", 1); // 1-手写签名样式
field.put("page", 1); // 页码
field.put("x", 100); // X坐标
field.put("y", 200); // Y坐标
field.put("width", 200); // 宽度
field.put("height", 50); // 高度
fields.add(field);
return fields;
}
@Override
public String getCustomerSignUrl(String flowId, String accountId, String mobile, String name) {
// 使用OAuth2.0鉴权获取签署链接
String url = esignProperties.getApiUrl() + "/v3/signflows/" + flowId
+ "/signers/" + accountId + "/signUrl";
JSONObject params = new JSONObject();
params.put("accountId", accountId);
params.put("urlType", 1); // 1-移动端H5,2-PC端
// sendRequest方法内部使用OAuth2.0 token
JSONObject result = sendRequest(url, params);
if (result != null && result.getInt("code") == 0) {
return result.getJSONObject("data").getStr("signUrl");
}
return null;
}
| 参数 | 值 | 说明 |
|---|---|---|
signFieldStyle |
1 |
手写签名样式(必须为1才能手写签署) |
signFieldType |
5 |
签名类型 |
signFieldStyleType |
1 |
手写签名样式类型 |
autoExecute |
false |
不自动执行,需要签署人手动签署 |
urlType |
1 或 2 |
签署链接类型:1-移动端H5,2-PC端 |
接口: POST /v3/signflows
请求体关键字段:
{
"autoExecute": false,
"businessScene": "合同签署",
"docs": [
{
"fileId": "文件ID",
"fileName": "合同.pdf"
}
],
"signers": [
{
"signerAccount": {
"signerAccountId": "签署人账号ID",
"mobile": "手机号",
"name": "姓名"
},
"signOrder": 1,
"signFields": [
{
"fileId": 1,
"signFieldStyle": 1,
"signFieldType": 5,
"signFieldStyleType": 1,
"page": 1,
"x": 100,
"y": 200,
"width": 200,
"height": 50
}
]
}
]
}
接口: GET /v3/signflows/{flowId}/signers/{accountId}/signUrl
请求参数:
accountId: 签署人账号IDurlType: 链接类型(1-移动端,2-PC端)响应:
{
"code": 0,
"data": {
"signUrl": "https://..."
}
}
接口: POST /v3/signflows/{flowId}/start
启动后,签署人才能通过签署链接进行签署。
当前代码使用HTTP方式直接调用e签宝API,但使用了OAuth2.0标准鉴权方式:
EsignSdkUtil 工具类获取OAuth2.0 token如果希望完全使用官方SDK(paas-sdk-3.1.4.jar),可以参考以下方式:
初始化SDK客户端:
// 根据SDK文档初始化ServiceClient
ServiceClient client = EsignsdkServiceFactory.buildServiceClient(oauth2Config);
调用SDK方法:
// 使用SDK的方法替代HTTP请求
client.createSignFlow(params);
client.getSignUrl(flowId, accountId);
注意: SDK的具体API请参考e签宝官方文档或联系技术支持。
在 application-dev.yaml 中配置:
system:
esign:
app-id: 7439093682 # e签宝应用ID
app-secret: da96bb269786338c156050cfa23eba4f # e签宝应用密钥
api-url: https://smlopenapi.esign.cn # API地址(沙箱环境)
# api-url: https://openapi.esign.cn # 生产环境
enabled: true
问题: API调用返回401或token过期错误
解决:
EsignSdkUtil.clearCachedToken() 清除缓存,重新获取问题: 签署链接打开后没有手写签署选项
解决:
signFieldStyle=1(手写签名)signFieldType=5(签名类型)问题: 手写签署区域位置不对
解决:
x、y、width、height 参数