跳到主要内容

账套验证

获取登录凭证LoginId,以账套身份登录接口系统,后续接口调用需要传用户代号

| 基本信息

信息备注
接口名称账套验证根据语言别、授权凭证等实现账套验证,用户名和密码可为空
请求状态POSTRESTful方式
接口路径http://localhost:23798/api/Company/Login向服务器地址发送POST请求
远程模式http://www.linkerplus.com/api/ext_erp/Company/Login远程模式向服务器地址发送POST请求,需要ERP注册号注册远程模式,并在Headers添加sn参数
支持日期2021-01-18自该日期起支持

| 请求参数

Headers

参数名称是否必须参数值备注
Content-Typeapplication/json用于指定数据的传输类型
snMGR_REST_1001远程模式需添加sn参数,MGR_REST_ERP注册号

Body

参数名称类型是否必须参数值备注其它信息
RSAKEYTYPESstring2RSA加密方式1:ASN,2:XML,3:PEM。为空时默认为2
RSAobject验证信息
CompNostringDEMO账套代号
ClientTypestringTestMgr用户自定义的客户端识别号比如客户是WMS系统,可以自定义ClientType=“WMS”
LanguageIdstring0客户端语言别0:简体 1:繁体 2:英文

| 返回数据

参数名称类型参数值备注
LoginIdstringe835943e-55c2-4bf0-832d-6f4166592941返回的LoginId
CompNostringDEMO帐套代号
LoginIdExpirynumber604800登录超时时间(秒)
ClientTypestringWMS客户端自定义识别号
LanguageIdnumber0客户端语言别

| 调用范例

Request

调用接口请求前需要进行RSA加密的数据
{
"RSAKEYTYPES":"2", // 加密方式
    "RSA":
// 开始加密部分
{
        "CompNo":"DEMO", // 帐套代号
        "ClientType":"WMS", // 客户端自定义识别号
        "LanguageId":0 // 客户端语言别
    }
// 结束加密部分
}
调用接口时需要传递RSA加密的数据
{
"RSAKEYTYPES":"2",
    "RSA":"4F73E4C232C80417E31B01194BF8C856DF0F3CE46536DFEA1AAFD82144F40B3E2F174270E415216448E2D2655E744DE14A86356D849E341D819C65CE986CB50D1AE5013023D64552C6A5112F1E00A5963729C9354F56666088E3088C72EC2F93ECCC7EF196950547247738863855455746CB11807AD6C044C63CFB307122E0A3"
}
RSA加密案列(C#)
namespace EncryptRSATest {
static class Program {
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main() {
JObject RsaJosnObj = new JObject();
RsaJosnObj.Add("CompNo", "DEMO");
RsaJosnObj.Add("ClientType", "WMS");
RsaJosnObj.Add("LanguageId", 0);
string _reqJson = "{\"RSA\":\"" + EncryptRSA(RsaJosnObj.ToString()) + "\"}"; //RSA加密
try {
HttpWebRequest request = (HttpWebRequest) WebRequest.Create("http://localhost:23798/api/Company/Login");
request.Method = "post";
request.ContentType = "application/json";
byte[] buffer = Encoding.UTF8.GetBytes(_reqJson);
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
using(HttpWebResponse response = (HttpWebResponse) request.GetResponse()) {
using(StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) {
JObject rJosnObj = null;
string _resJson = reader.ReadToEnd();
rJosnObj = (JObject) JsonConvert.DeserializeObject(_resJson);
}
}
} catch {}
}

/// <summary>
/// RSA加密
/// </summary>
/// <param name="encryptString">需要加密的字符串</param>
/// <returns></returns>
public static string EncryptRSA(string encryptString) {
string xmlPublicKey = @ "<RSAKeyValue><Modulus>xF/Qn+vx7g5FzsbFPgx1rWwdqOge8VyXJBE90qmJBzn2E5qvb0OhLdk0+xpodopHQ50/W1sD5cw/qNXyrf+lEmpV9QjR1naCDcJAQN+n88eXMXTqdqs/K6ddDEfFGiXcS20rd8sBv3KdnafD5DUJqQcW8a1P8s2OohfX/q24oOU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
CspParameters RSAParams = new CspParameters();
RSAParams.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider _rsacsp = new RSACryptoServiceProvider(1024, RSAParams);
try {
byte[] rsaBts;
string encryptResult = "";
byte[] tempBts = new byte[117];
byte[] unicodeBts = Encoding.UTF8.GetBytes(encryptString);

_rsacsp.FromXmlString(xmlPublicKey);
double len = unicodeBts.Length;
if (len < 117) //加密文本少于117字节
{
rsaBts = _rsacsp.Encrypt(unicodeBts, false);
//encryptResult = Convert.ToBase64String(rsaBts);
encryptResult = BitConverter.ToString(rsaBts).Replace("-", string.Empty);
} else {
int blocks = (int) Math.Ceiling(len / 117);
rsaBts = new byte[blocks * 128];
for (int i = 0; i < blocks; i++) {
if (i < blocks - 1) {
Array.Copy(unicodeBts, i * 117, tempBts, 0, 117);
} else {
tempBts = new byte[(unicodeBts.Length - i * 117)];
Array.Copy(unicodeBts, i * 117, tempBts, 0, (unicodeBts.Length - i * 117));
}
Array.Copy(_rsacsp.Encrypt(tempBts, false), 0, rsaBts, i * 128, 128);
}
//encryptResult = Convert.ToBase64String(rsaBts);
encryptResult = BitConverter.ToString(rsaBts).Replace("-", string.Empty);
}

return encryptResult;
} catch {
return encryptString;
} finally {
_rsacsp.Clear();
}
}
}
}
RSA加密案列(java)
import android.os.Build;

import org.json.JSONException;
import org.json.JSONObject;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.math.BigInteger;
import java.util.Base64;

public class RSAEncryption {

/**
* RSA加密
* @param encryptString 需要加密的字符串
* @return 加密后的16进制字符串
*/
public static String encryptRSA(String encryptString) {
String xmlPublicKey = "<RSAKeyValue><Modulus>xF/Qn+vx7g5FzsbFPgx1rWwdqOge8VyXJBE90qmJBzn2E5qvb0OhLdk0+xpodopHQ50/W1sD5cw/qNXyrf+lEmpV9QjR1naCDcJAQN+n88eXMXTqdqs/K6ddDEfFGiXcS20rd8sBv3KdnafD5DUJqQcW8a1P8s2OohfX/q24oOU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

try {
// 从XML公钥字符串中提取模数和指数
PublicKey publicKey = parsePublicKeyFromXML(xmlPublicKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);

byte[] unicodeBytes = encryptString.getBytes("UTF-8");
String encryptResult;

if (unicodeBytes.length < 117) {
// 加密文本少于117字节
byte[] rsaBytes = cipher.doFinal(unicodeBytes);
encryptResult = bytesToHex(rsaBytes);
} else {
// 分块加密
int blocks = (int) Math.ceil(unicodeBytes.length / 117.0);
byte[] rsaBytes = new byte[blocks * 128];

for (int i = 0; i < blocks; i++) {
byte[] tempBytes;
if (i < blocks - 1) {
tempBytes = new byte[117];
System.arraycopy(unicodeBytes, i * 117, tempBytes, 0, 117);
} else {
int remaining = unicodeBytes.length - i * 117;
tempBytes = new byte[remaining];
System.arraycopy(unicodeBytes, i * 117, tempBytes, 0, remaining);
}

byte[] encryptedBlock = cipher.doFinal(tempBytes);
System.arraycopy(encryptedBlock, 0, rsaBytes, i * 128, 128);
}
encryptResult = bytesToHex(rsaBytes);
}

return encryptResult;
} catch (Exception e) {
e.printStackTrace();
return encryptString;
}
}

/**
* 从XML格式的公钥字符串解析公钥
*/
private static PublicKey parsePublicKeyFromXML(String xmlPublicKey) throws Exception {
// 提取Base64编码的模数和指数
int modulusStart = xmlPublicKey.indexOf("<Modulus>") + 9;
int modulusEnd = xmlPublicKey.indexOf("</Modulus>");
String modulusBase64 = xmlPublicKey.substring(modulusStart, modulusEnd);

int exponentStart = xmlPublicKey.indexOf("<Exponent>") + 10;
int exponentEnd = xmlPublicKey.indexOf("</Exponent>");
String exponentBase64 = xmlPublicKey.substring(exponentStart, exponentEnd);

// 解码Base64
byte[] modulusBytes = Base64.getDecoder().decode(modulusBase64);
byte[] exponentBytes = Base64.getDecoder().decode(exponentBase64);

// 转换为BigInteger
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger exponent = new BigInteger(1, exponentBytes);

// 创建RSAPublicKeySpec并生成公钥
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
}

/**
* 将字节数组转换为16进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}

/**
* 测试方法
*/
public static void main(String[] args) {
String testString = "";
JSONObject test = new JSONObject();
try {
test.put("CompNo", "TM01");
test.put("LanguageId", 0);
} catch (JSONException e) {
throw new RuntimeException(e);
}
testString = test.toString();

String encrypted = encryptRSA(testString);
System.out.println("原始字符串: " + testString);
System.out.println("加密结果: " + encrypted);
System.out.println("加密结果长度: " + encrypted.length());
}
}

RSA加密公钥:

["RSAKEYTYPES":"1";(ASN)]
ASN_PUBLIC_KEY = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXlWyTNXobnOfurhrs0kokpEYnOixeo4rViqLcpD4vMNI6M7J/0cBgDSLO+AgKeam3v7rRXZQ1TId3q636d+Z9/XZMgw5ekTbTCdiodlEIqoT3MRuZPmUZ2YTH+BpXvpZoDu7W3lV4mIp183FQ/6naUVEgaaVix8uc9sEiWdYAyQIDAQAB";

["RSAKEYTYPES":"2";(XML)]
XML_PUBLIC_KEY = @"<RSAKeyValue><Modulus>xF/Qn+vx7g5FzsbFPgx1rWwdqOge8VyXJBE90qmJBzn2E5qvb0OhLdk0+xpodopHQ50/W1sD5cw/qNXyrf+lEmpV9QjR1naCDcJAQN+n88eXMXTqdqs/K6ddDEfFGiXcS20rd8sBv3KdnafD5DUJqQcW8a1P8s2OohfX/q24oOU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

["RSAKEYTYPES":"3";(PEM)]
PEM_PUBLIC_KEY = @"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDV8XjtTJrZGvYB2th3ZJTnatT4
YDFcXim7tTUFQYP+6DSEy06jdt+RkFKjYMrFCj8CuwA0Lm3/QNYK6gdoXwMjMazE
QEj3GInTUfbEMQjUS9oXq20QdxDVTyvldHFMYUP9RbPr9velfoSglaz7ZKLO6JFk
VAkOw2MkDiGZWkKZnQIDAQAB
----END PUBLIC KEY-----
";

Response

{
"LoginId": "e835943e-55c2-4bf0-832d-6f4166592941", // 返回的LoginId
"CompNo": "DEMO", // 帐套代号
"LoginIdExpiry": 604800, // 登录超时时间(秒)
"ClientType": "WMS", // 客户端自定义识别号
"LanguageId": 0 // 客户端语言别
}

| 备注

其它程序调取ERP接口,都需要授权,获得loginId。调用方需要记录下loginId,调取其它ERP接口时传入loginIdUsrNo

需要注意

先判断返回的HttpStatusCode是否等于200,如果是则表示调用成功,否则返回错误信息

登录接口不需要频繁调用,只有当没有获得LoginId或登录已超时后才需要再次调用

登录接口返回的LoginIdExpiry以秒为单位,调用方在调用前先记录本地时间,接口返回后将记录的本地时间以时间戳格式加上LoginIdExpiry的值,即是下次需要再次调用登录接口的时机。

除了登录接口,其它接口调用必须在Headers里面传入LoginId

返回格式

所有接口都以JSON格式返回