mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
- 新增ISV用户平台
- 新增门户网站(portal) - 新增`C++`,`Rust`语言SDK
This commit is contained in:
5
sop-sdk/sdk-rust/readme.md
Normal file
5
sop-sdk/sdk-rust/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# sdk for rust
|
||||
|
||||
使用方式见:`sdk-test/src/main.rs`
|
||||
|
||||
封装步骤参考 `sdk/src/request/memberinfoget.rs` 和 `sdk/src/response/memberinfoget.rs`
|
1
sop-sdk/sdk-rust/sdk-test/.gitignore
vendored
Normal file
1
sop-sdk/sdk-rust/sdk-test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
1558
sop-sdk/sdk-rust/sdk-test/Cargo.lock
generated
Normal file
1558
sop-sdk/sdk-rust/sdk-test/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
sop-sdk/sdk-rust/sdk-test/Cargo.toml
Normal file
11
sop-sdk/sdk-rust/sdk-test/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "sdk-test"
|
||||
version = "0.1.0"
|
||||
authors = ["thc"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# 依赖sdk模块
|
||||
sdk = { path = "../sdk"}
|
1
sop-sdk/sdk-rust/sdk-test/aa.txt
Normal file
1
sop-sdk/sdk-rust/sdk-test/aa.txt
Normal file
@@ -0,0 +1 @@
|
||||
hello你好123
|
1
sop-sdk/sdk-rust/sdk-test/bb.txt
Normal file
1
sop-sdk/sdk-rust/sdk-test/bb.txt
Normal file
@@ -0,0 +1 @@
|
||||
文件bb的内容
|
76
sop-sdk/sdk-rust/sdk-test/src/main.rs
Normal file
76
sop-sdk/sdk-rust/sdk-test/src/main.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
extern crate sdk;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use sdk::client::OpenClient;
|
||||
use sdk::http::UploadFile;
|
||||
use sdk::request::BaseRequest;
|
||||
use sdk::request::memberinfoget::MemberInfoGetRequest;
|
||||
use sdk::response::memberinfoget::MemberInfoGetResponse;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
// 创建请求客户端
|
||||
let client = OpenClient {
|
||||
// 应用ID
|
||||
app_id: "2020051325943082302177280",
|
||||
// 应用私钥
|
||||
private_key: "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=",
|
||||
// 请求地址
|
||||
url: "http://localhost:7071/prod/gw68uy85",
|
||||
};
|
||||
// 业务参数
|
||||
let mut biz_model = HashMap::new();
|
||||
biz_model.insert("name", "jim".to_string());
|
||||
biz_model.insert("address", "xx".to_string());
|
||||
biz_model.insert("age", "22".to_string());
|
||||
|
||||
let mut files = vec![];
|
||||
|
||||
// 添加上传文件
|
||||
/*let mod_dir = env::current_dir().unwrap().display().to_string();
|
||||
files = vec![
|
||||
UploadFile { name:"file1", path: format!("{}/{}", &mod_dir, "aa.txt")},
|
||||
UploadFile { name:"file2", path: format!("{}/{}", &mod_dir, "bb.txt") },
|
||||
];*/
|
||||
|
||||
|
||||
// 创建请求,设置业务参数
|
||||
let request = MemberInfoGetRequest {
|
||||
base: BaseRequest { biz_model: biz_model, files: files }
|
||||
};
|
||||
|
||||
// 发送请求
|
||||
let response:MemberInfoGetResponse = client.execute(request);
|
||||
|
||||
// 成功
|
||||
if response.sub_code.len() == 0 {
|
||||
println!("resp:{:#?}", response)
|
||||
} else {
|
||||
println!("error:{:#?}", response)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sdk::sign::{HashType, SignUtil};
|
||||
use sdk::http::HttpTool;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let content = "123";
|
||||
let private_key = "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=";
|
||||
let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256);
|
||||
println!("sign:{}", sign);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_http() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("aaa", "bbb");
|
||||
let response = HttpTool::get("http://baidu.com", &map, &HashMap::new());
|
||||
println!("response:{:#?}", response);
|
||||
}
|
||||
|
||||
}
|
1
sop-sdk/sdk-rust/sdk/.gitignore
vendored
Normal file
1
sop-sdk/sdk-rust/sdk/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
1551
sop-sdk/sdk-rust/sdk/Cargo.lock
generated
Normal file
1551
sop-sdk/sdk-rust/sdk/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
sop-sdk/sdk-rust/sdk/Cargo.toml
Normal file
23
sop-sdk/sdk-rust/sdk/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "sdk"
|
||||
version = "0.1.0"
|
||||
authors = ["thc"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# 时间api
|
||||
chrono = "0.4"
|
||||
# rsa库
|
||||
rsa = "0.3.0"
|
||||
# 加密库
|
||||
rust-crypto = "^0.2"
|
||||
# base64库
|
||||
base64 = "0.12.3"
|
||||
# HTTP客户端 https://github.com/seanmonstar/reqwest
|
||||
reqwest = { version = "0.10", features = ["blocking", "json"] }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
# 处理json https://serde.rs/
|
||||
serde = { version = "1.0.114", features = ["derive"] }
|
||||
serde_json = "1.0.56"
|
112
sop-sdk/sdk-rust/sdk/src/client.rs
Normal file
112
sop-sdk/sdk-rust/sdk/src/client.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use chrono::Local;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::http::HttpTool;
|
||||
use crate::request::{Request, RequestType};
|
||||
use crate::sign::{SignType, SignUtil};
|
||||
use serde_json::Value;
|
||||
|
||||
pub struct OpenClient {
|
||||
/// 应用ID
|
||||
pub app_id: &'static str,
|
||||
/// 应用私钥,PKCS#1
|
||||
pub private_key: &'static str,
|
||||
/// 请求url
|
||||
pub url: &'static str,
|
||||
}
|
||||
|
||||
impl OpenClient {
|
||||
|
||||
/// 发送请求
|
||||
///
|
||||
/// - request: 请求对象
|
||||
///
|
||||
/// 返回结果
|
||||
pub fn execute<T: DeserializeOwned>(&self, request: impl Request) -> T {
|
||||
self.execute_token(request, "")
|
||||
}
|
||||
|
||||
///发送请求
|
||||
///
|
||||
/// - request: 请求对象
|
||||
/// - token:token
|
||||
///
|
||||
/// 返回结果
|
||||
pub fn execute_token<T: DeserializeOwned>(&self, request: impl Request, token: &'static str) -> T {
|
||||
let struct_obj: T;
|
||||
|
||||
let request_type = request.get_request_type();
|
||||
let headers = &OpenClient::get_default_headers();
|
||||
let all_params = &self.build_params(&request, token);
|
||||
|
||||
if request.get_base().files.len() > 0 {
|
||||
let base = request.get_base();
|
||||
let files = &base.files;
|
||||
let resp = HttpTool::post_file(self.url, all_params, files, headers);
|
||||
struct_obj = self.parse_response(resp, &request);
|
||||
} else {
|
||||
match request_type {
|
||||
RequestType::Get => {
|
||||
let resp = HttpTool::get(self.url, all_params, headers);
|
||||
struct_obj = self.parse_response(resp, &request);
|
||||
}
|
||||
RequestType::PostForm => {
|
||||
let resp = HttpTool::post_form(self.url, all_params, headers);
|
||||
struct_obj = self.parse_response(resp, &request);
|
||||
}
|
||||
RequestType::PostJson => {
|
||||
let resp = HttpTool::post_json(self.url, all_params, headers);
|
||||
struct_obj = self.parse_response(resp, &request);
|
||||
}
|
||||
RequestType::PostFile => {
|
||||
let base = request.get_base();
|
||||
let files = &base.files;
|
||||
let resp = HttpTool::post_file(self.url, all_params, files, headers);
|
||||
struct_obj = self.parse_response(resp, &request);
|
||||
}
|
||||
}
|
||||
}
|
||||
struct_obj
|
||||
}
|
||||
|
||||
fn get_default_headers() -> HashMap<&'static str, &'static str> {
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("Accept-Encoding", "identity");
|
||||
headers
|
||||
}
|
||||
|
||||
fn parse_response<T: DeserializeOwned>(&self, resp: reqwest::blocking::Response, request: &impl Request) -> T {
|
||||
let root: HashMap<String, Value> = resp.json().expect("error");
|
||||
request.parse_response(root)
|
||||
}
|
||||
|
||||
/// 构建请求参数
|
||||
fn build_params(&self, request: &impl Request, token: &str) -> HashMap<&'static str, String> {
|
||||
let method = request.get_method();
|
||||
let version = request.get_version();
|
||||
|
||||
let mut all_params = HashMap::new();
|
||||
all_params.insert("app_id", self.app_id.to_string());
|
||||
all_params.insert("method", method.to_string());
|
||||
all_params.insert("charset", "UTF-8".to_string());
|
||||
all_params.insert("timestamp", Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
|
||||
all_params.insert("version", version.to_string());
|
||||
|
||||
// 添加业务参数
|
||||
for entry in &request.get_base().biz_model {
|
||||
all_params.insert(entry.0, entry.1.to_string());
|
||||
}
|
||||
|
||||
if !token.is_empty() {
|
||||
all_params.insert("app_auth_token", token.to_string());
|
||||
}
|
||||
|
||||
// 创建签名
|
||||
let sign = SignUtil::create_sign(&all_params, self.private_key, SignType::RSA2);
|
||||
all_params.insert("sign", sign);
|
||||
|
||||
all_params
|
||||
}
|
||||
}
|
158
sop-sdk/sdk-rust/sdk/src/http.rs
Normal file
158
sop-sdk/sdk-rust/sdk/src/http.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Cargo.toml:
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.10", features = ["blocking", "json"] }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
serde = { version = "1.0.114", features = ["derive"] }
|
||||
serde_json = "1.0.56"
|
||||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use reqwest::blocking::Response;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, HeaderName};
|
||||
use std::str::FromStr;
|
||||
use serde::Serialize;
|
||||
|
||||
/// HTTP请求工具
|
||||
pub struct HttpTool {}
|
||||
|
||||
/// 上传文件对象
|
||||
pub struct UploadFile {
|
||||
/// 上传文件表单名称
|
||||
pub name: &'static str,
|
||||
/// 文件全路径
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl HttpTool {
|
||||
|
||||
/// get请求
|
||||
///
|
||||
/// - url:请求url
|
||||
/// - params:请求参数
|
||||
/// - headers:请求header
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut params = HashMap::new();
|
||||
/// params.insert("name", "Jim");
|
||||
/// params.insert("age", "12");
|
||||
/// let resp = HttpTool::get(url, ¶ms, &HashMap::new());
|
||||
/// ```
|
||||
/// return: Response对象
|
||||
pub fn get<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let res = client
|
||||
.get(url)
|
||||
.query(params)
|
||||
.headers(super::http::HttpTool::get_headers_map(headers))
|
||||
.send();
|
||||
let resp = res.expect("request error");
|
||||
resp
|
||||
}
|
||||
|
||||
/// post模拟表单请求
|
||||
///
|
||||
/// - url:请求url
|
||||
/// - params:请求参数
|
||||
/// - headers:请求header
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut params = HashMap::new();
|
||||
/// params.insert("name", "Jim");
|
||||
/// params.insert("age", "12");
|
||||
/// let resp = HttpTool::post_form(url, ¶ms, &HashMap::new());
|
||||
/// ```
|
||||
/// return: Response对象
|
||||
pub fn post_form<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.form(params)
|
||||
.headers(super::http::HttpTool::get_headers_map(headers))
|
||||
.send();
|
||||
let resp = res.expect("request error");
|
||||
resp
|
||||
}
|
||||
|
||||
/// post发送json
|
||||
///
|
||||
/// - url:请求url
|
||||
/// - params:请求参数
|
||||
/// - headers:请求header
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut params = HashMap::new();
|
||||
/// params.insert("name", "Jim");
|
||||
/// params.insert("age", "12");
|
||||
/// let resp = HttpTool::post_json(url, ¶ms, &HashMap::new());
|
||||
/// ```
|
||||
/// return: Response对象
|
||||
pub fn post_json<T: Serialize + ?Sized>(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let res = client
|
||||
.post(url)
|
||||
.json(params)
|
||||
.headers(super::http::HttpTool::get_headers_map(headers))
|
||||
.send();
|
||||
let resp = res.expect("request error");
|
||||
resp
|
||||
}
|
||||
|
||||
/// post上传文件
|
||||
///
|
||||
/// - url:请求url
|
||||
/// - params:请求参数
|
||||
/// - files:上传文件
|
||||
/// - headers:请求header
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut params = HashMap::new();
|
||||
/// params.insert("name", String::from("Jim"));
|
||||
/// params.insert("age",String::from("12"));
|
||||
///
|
||||
/// // 设置上传文件
|
||||
/// let mut files = vec![
|
||||
/// UploadFile { name:"file1", path: String::from("/User/xx/aa.txt") },
|
||||
/// UploadFile { name:"file2", path: String::from("/User/xx/bb.txt") },
|
||||
/// ];
|
||||
///
|
||||
/// let resp = HttpTool::post_file(url, ¶ms, &files, &HashMap::new());
|
||||
/// ```
|
||||
/// return: Response对象
|
||||
pub fn post_file(url: &str, params: &HashMap<&str, String>, files: &Vec<UploadFile>, headers: &HashMap<&str, &str>) -> Response {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let mut form = reqwest::blocking::multipart::Form::new();
|
||||
// 添加普通参数
|
||||
for entry in params {
|
||||
form = form.text(entry.0.to_string(), entry.1.to_string());
|
||||
}
|
||||
// 添加文件
|
||||
for file in files {
|
||||
form = form.file(file.name.to_string(), file.path.to_string()).unwrap();
|
||||
}
|
||||
let res = client
|
||||
.post(url)
|
||||
.multipart(form)
|
||||
.headers(super::http::HttpTool::get_headers_map(headers))
|
||||
.send();
|
||||
let resp = res.expect("request error");
|
||||
resp
|
||||
}
|
||||
|
||||
fn get_headers_map(headers: &HashMap<&str, &str>) -> HeaderMap {
|
||||
let mut header_map = HeaderMap::new();
|
||||
for entry in headers {
|
||||
header_map.insert(HeaderName::from_str(entry.0).unwrap(), HeaderValue::from_str(entry.1).unwrap());
|
||||
}
|
||||
header_map
|
||||
}
|
||||
}
|
14
sop-sdk/sdk-rust/sdk/src/lib.rs
Normal file
14
sop-sdk/sdk-rust/sdk/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
pub mod sign;
|
||||
pub mod client;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod http;
|
||||
extern crate chrono;
|
||||
|
||||
/*#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}*/
|
26
sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs
Normal file
26
sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use crate::request::{BaseRequest, Request, RequestType};
|
||||
|
||||
pub struct MemberInfoGetRequest {
|
||||
// 固定这么写
|
||||
pub base: BaseRequest
|
||||
}
|
||||
|
||||
impl Request for MemberInfoGetRequest {
|
||||
|
||||
fn get_method(&self) -> &str {
|
||||
"member.info.get"
|
||||
}
|
||||
|
||||
fn get_version(&self) -> &str {
|
||||
"1.0"
|
||||
}
|
||||
|
||||
fn get_request_type(&self) -> RequestType {
|
||||
RequestType::Get
|
||||
}
|
||||
|
||||
// 固定这么写
|
||||
fn get_base(&self) -> &BaseRequest {
|
||||
&self.base
|
||||
}
|
||||
}
|
45
sop-sdk/sdk-rust/sdk/src/request/mod.rs
Normal file
45
sop-sdk/sdk-rust/sdk/src/request/mod.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::http::UploadFile;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
pub mod memberinfoget;
|
||||
|
||||
pub enum RequestType {
|
||||
Get,
|
||||
PostJson,
|
||||
PostForm,
|
||||
PostFile,
|
||||
}
|
||||
|
||||
pub trait Request {
|
||||
/// 返回接口名称
|
||||
fn get_method(&self) -> &str;
|
||||
|
||||
/// 返回版本号
|
||||
fn get_version(&self) -> &str;
|
||||
|
||||
/// 返回请求方式
|
||||
fn get_request_type(&self) -> RequestType;
|
||||
|
||||
/// 返回base
|
||||
fn get_base(&self) -> &BaseRequest;
|
||||
|
||||
fn parse_response<T: DeserializeOwned>(&self, root: HashMap<String, Value>) -> T {
|
||||
let mut data = root.get("error_response");
|
||||
if data.is_none() {
|
||||
let data_name = self.get_method().replace(".", "_") + "_response";
|
||||
data = root.get(data_name.as_str());
|
||||
}
|
||||
let value = serde_json::to_value(data.unwrap()).unwrap();
|
||||
serde_json::from_value(value).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseRequest {
|
||||
pub biz_model: HashMap<&'static str, String>,
|
||||
pub files: Vec<UploadFile>
|
||||
}
|
36
sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs
Normal file
36
sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
extern crate serde;
|
||||
|
||||
use serde::{Deserialize};
|
||||
|
||||
// 响应参数
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MemberInfoGetResponse {
|
||||
// ~~~ 固定部分 ~~~
|
||||
pub code: String,
|
||||
pub msg: String,
|
||||
// json中可能没有sub_code属性,因此需要加上 #[serde(default)]
|
||||
// 详见:https://serde.rs/field-attrs.html
|
||||
#[serde(default)]
|
||||
pub sub_code: String,
|
||||
#[serde(default)]
|
||||
pub sub_msg: String,
|
||||
// ~~~ 固定部分 ~~~
|
||||
|
||||
// 下面是业务字段
|
||||
#[serde(default)]
|
||||
pub id: u32,
|
||||
|
||||
#[serde(default)]
|
||||
pub name: String,
|
||||
|
||||
pub member_info: MemberInfo
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MemberInfo {
|
||||
#[serde(default)]
|
||||
pub is_vip: i8,
|
||||
|
||||
#[serde(default)]
|
||||
pub vip_endtime: String
|
||||
}
|
1
sop-sdk/sdk-rust/sdk/src/response/mod.rs
Normal file
1
sop-sdk/sdk-rust/sdk/src/response/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod memberinfoget;
|
108
sop-sdk/sdk-rust/sdk/src/sign.rs
Normal file
108
sop-sdk/sdk-rust/sdk/src/sign.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
extern crate rsa;
|
||||
extern crate crypto;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use rsa::{RSAPrivateKey, PaddingScheme};
|
||||
use rsa::Hash;
|
||||
|
||||
use crypto::sha2::Sha256;
|
||||
use crypto::digest::Digest;
|
||||
use std::iter::repeat;
|
||||
|
||||
pub struct SignUtil {}
|
||||
|
||||
pub enum SignType {
|
||||
RSA,
|
||||
RSA2,
|
||||
}
|
||||
|
||||
pub enum HashType {
|
||||
Sha1,
|
||||
Sha256
|
||||
}
|
||||
|
||||
impl SignUtil {
|
||||
|
||||
pub fn create_sign(all_params: &HashMap<&str, String>, private_key: &str, sign_type: SignType) -> String {
|
||||
let content = SignUtil::get_sign_content(all_params);
|
||||
// println!("content:{}", content);
|
||||
let hash_type;
|
||||
match sign_type {
|
||||
SignType::RSA => hash_type = HashType::Sha1,
|
||||
SignType::RSA2 => hash_type = HashType::Sha256,
|
||||
}
|
||||
|
||||
SignUtil::rsa_sign(content.as_str(), private_key, hash_type)
|
||||
}
|
||||
|
||||
/// RSA签名
|
||||
///
|
||||
/// - content: 签名内容
|
||||
/// - private_key: 私钥,PKCS#1
|
||||
/// - hash_type: hash类型
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use sdk::sign::{SignUtil, HashType};
|
||||
///
|
||||
/// let content = "123";
|
||||
/// let private_key = "your private key";
|
||||
/// let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256);
|
||||
///
|
||||
/// println!("sign:{}", sign);
|
||||
/// ```
|
||||
/// return: 返回base64字符串
|
||||
pub fn rsa_sign(content: &str, private_key: &str, hash_type: HashType) -> String {
|
||||
// 格式化私钥
|
||||
let der_encoded = private_key
|
||||
.lines()
|
||||
.filter(|line| !line.starts_with("-"))
|
||||
.fold(String::new(), |mut data, line| {
|
||||
data.push_str(&line);
|
||||
data
|
||||
});
|
||||
let der_bytes = base64::decode(der_encoded).expect("failed to decode base64 content");
|
||||
// 获取私钥对象
|
||||
let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key");
|
||||
|
||||
// 创建一个Sha256对象
|
||||
let mut hasher = Sha256::new();
|
||||
// 对内容进行摘要
|
||||
hasher.input_str(content);
|
||||
// 将摘要结果保存到buf中
|
||||
let mut buf: Vec<u8> = repeat(0).take((hasher.output_bits()+7)/8).collect();
|
||||
hasher.result(&mut buf);
|
||||
|
||||
// 对摘要进行签名
|
||||
let hash;
|
||||
match hash_type {
|
||||
HashType::Sha1 => hash = Hash::SHA1,
|
||||
HashType::Sha256 => hash = Hash::SHA2_256
|
||||
}
|
||||
let sign_result = private_key.sign(PaddingScheme::PKCS1v15Sign {hash: Option::from(hash) }, &buf);
|
||||
// 签名结果转化为base64
|
||||
let vec = sign_result.expect("create sign error for base64");
|
||||
|
||||
base64::encode(vec)
|
||||
}
|
||||
|
||||
fn get_sign_content(all_params: &HashMap<&str, String>) -> String {
|
||||
let mut content = Vec::new();
|
||||
let keys = all_params.keys();
|
||||
let mut sorted_keys = Vec::new();
|
||||
for key in keys {
|
||||
sorted_keys.push(key);
|
||||
}
|
||||
// 排序
|
||||
sorted_keys.sort();
|
||||
for key in sorted_keys {
|
||||
let val = all_params.get(key);
|
||||
if val.is_some() {
|
||||
content.push(key.to_string() + "=" + val.unwrap());
|
||||
}
|
||||
}
|
||||
content.join("&")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user