• 沒有找到結果。

2.4.1 Java SDK 使用说明

操作场景

使用Java语言进行后端服务签名时,您需要先获取SDK,然后导入工程,最后参考校 验后端签名示例校验签名是否一致。

本章节以IntelliJ IDEA 2018.3.5版本为例介绍。

说明

Java SDK仅支持hmac和basic类型的后端服务签名。

前提条件

● 准备待用的签名密钥的Key和Secret。

● 已在控制台创建签名密钥,并绑定API,具体请参见配置后端服务的签名校验。

● 已获取后端签名示例代码,您可以在ROMA Connect实例控制台的“服务集成 APIC > API管理”页面中,选择“签名密钥”页签,单击“下载SDK”下载签名示 例代码。

● 获取并安装2018.3.5或以上版本的IntelliJ IDEA,如果未安装,请至IntelliJ IDEA 官方网站下载。

● 已安装Java Development Kit 1.8.111或以上版本,如果未安装,请至Oracle官方 下载页面下载。

导入工程

步骤1 打开IntelliJ IDEA,在菜单栏选择“File > New > Project from Existing Sources”,选 择解压后的“apigateway-backend-signature-demo\pom.xml”文件,单击“OK”。

2-43 Select File or Directory to Import

步骤2 保持默认设置,单击“Next > Next > Next > Next > Finish”,完成工程导入。

步骤3 在右侧Maven页签,双击“compile”进行编译。

2-44 编译工程

返回“BUILD SUCCESS”,表示编译成功。

步骤4 右键单击BackendSignatureApplication,选择“Run”运行服务。

2-45 运行服务

“ApigatewaySignatureFilter.java”为示例代码,请根据实际情况修改参数后使用。

具体代码说明请参考校验hmac类型后端签名示例。

----结束

校验 hmac 类型后端签名示例

说明

● 示例演示如何编写一个基于Spring boot的服务器,作为API的后端,并且实现一个Filter,对 APIC的请求做签名校验。

● API绑定hmac类型签名密钥后,发给后端的请求中会添加签名信息。

步骤1 编写一个Controller,路径为/hmac。

// HelloController.java

@RestController

@EnableAutoConfiguration public class HelloController { @RequestMapping("/hmac") private String hmac() {

return "Hmac authorization success";

} }

步骤2 编写一个Filter,匹配所有路径和方法。将允许的签名key和secret对放入一个Map中。

public class ApigatewaySignatureFilter implements Filter { private static Map<String, String> secrets = new HashMap<>();

static {

secrets.put("signature_key1", "signature_secret1");

secrets.put("signature_key2", "signature_secret2");

}

@Override

public void init(FilterConfig filterConfig) throws ServletException { }

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) { //签名校验代码

...

} }

步骤3 doFilter函数为签名校验代码。校验流程如下:由于filter中需要读取body,为了使得 body可以在后续的filter和controller中再次读取,把request包装起来传给后续的filter 和controller。包装类的具体实现可见RequestWrapper.java。

RequestWrapper request = new RequestWrapper((HttpServletRequest) servletRequest);

步骤4 使用正则表达式解析Authorization头,得到signingKey和signedHeaders。

private static final Pattern authorizationPattern = Pattern.compile("SDK-HMAC-SHA256\\s+Access=([^,]+),\

\s?SignedHeaders=([^,]+),\\s?Signature=(\\w+)");

...

String authorization = request.getHeader("Authorization");

if (authorization == null || authorization.length() == 0) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization not found.");

return;

}

Matcher m = authorizationPattern.matcher(authorization);

if (!m.find()) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization format incorrect.");

return;

}String signingKey = m.group(1);

String signingSecret = secrets.get(signingKey);

if (signingSecret == null) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signing key not found.");

return;

}String[] signedHeaders = m.group(2).split(";");

例如,Authorization头为:

SDK-HMAC-SHA256 Access=signature_key1, SignedHeaders=host;x-sdk-date, Signature=e11adf65a20d1b82c25419b5********8d0ba12fed1ceb13ed00

则解析的结果为:

signingKey=signature_key1 signedHeaders=host;x-sdk-date

步骤5 通过signingKey找到signingSecret,如果不存在signingKey,则返回认证失败。

String signingSecret = secrets.get(signingKey);

if (signingSecret == null) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signing key not found.");

return;

}

步骤6 新建一个Request对象,将请求method、url、query、signedHeaders对应的请求头放 入其中。判断是否需要设置body并设置。

需要读取body的条件为:不存在值为UNSIGNED-PAYLOAD的x-sdk-content-sha256 头。Request apiRequest = new DefaultRequest();

apiRequest.setHttpMethod(HttpMethodName.valueOf(request.getMethod()));

String url = request.getRequestURL().toString();

String queryString = request.getQueryString();

try {

apiRequest.setEndpoint((new URL(url)).toURI());

Map<String, String> parametersmap = new HashMap<>();

if (null != queryString && !"".equals(queryString)) { String[] parameterarray = queryString.split("&");

for (String p : parameterarray) {

parametersmap.put(URLDecoder.decode(key, "UTF-8"), URLDecoder.decode(value, "UTF-8"));

}

apiRequest.setParameters(parametersmap); //set query }

} catch (URISyntaxException e) { e.printStackTrace();

}

boolean needbody = true;

String dateHeader = null;

for (int i = 0; i < signedHeaders.length; i++) {

String headerValue = request.getHeader(signedHeaders[i]);

if (headerValue == null || headerValue.length() == 0) {

((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED, "signed header" + signedHeaders[i] + " not found.");

} else {

apiRequest.addHeader(signedHeaders[i], headerValue);//set header if (signedHeaders[i].toLowerCase().equals("x-sdk-content-sha256") &&

headerValue.equals("UNSIGNED-PAYLOAD")) { needbody = false;

}

if (signedHeaders[i].toLowerCase().equals("x-sdk-date")) { dateHeader = headerValue;

} } }

if (needbody) {

apiRequest.setContent(new ByteArrayInputStream(request.getBody())); //set body }

步骤7 校验签名是否过期。从X-Sdk-Date头中取出时间,判断与服务器时间是否相差在15分 钟以内。如果signedHeaders中不包含X-Sdk-Date,也返回认证失败。

private static final DateTimeFormatter timeFormatter =

DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss'Z'").withZoneUTC();

...

if (dateHeader == null) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Header x-sdk-date not found.");

return;

}long date = timeFormatter.parseMillis(dateHeader);

long duration = Math.abs(DateTime.now().getMillis() - date);

if (duration > 15 * 60 * 1000) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signature expired.");

return;

}

步骤8 将Authorization头也放入Request对象中,调用verify方法校验请求签名。如果校验通 过,则执行下一个filter,否则返回认证失败。

DefaultSigner signer = (DefaultSigner) SignerFactory.getSigner();

boolean verify = signer.verify(apiRequest, new BasicCredentials(signingKey, signingSecret));

if (verify) {

chain.doFilter(request, response);

} else {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "verify authroization failed.");

}

步骤9 注册filter和路径的映射关系。

@Configuration

public class FilterConfig { @Bean

public FilterRegistrationBean registApigatewaySignatureFilter() { FilterRegistrationBean registration = new FilterRegistrationBean();

registration.setFilter(new ApigatewaySignatureFilter());

registration.addUrlPatterns("/hmac");

registration.setName("ApigatewaySignatureFilter");

return registration;

} }

步骤10 运行服务器,验证代码正确性。下面示例使用JavaScript SDK中的html签名工具生成 签名。

填入如图所示字段后,单击“Send request”,复制生成的curl命令,并在命令行中执 行,服务器返回“Hello World!”。

如果使用错误的Key和Secret访问,服务器返回401认证不通过。

----结束

校验 basic 类型后端签名示例

说明

● 示例演示如何编写一个基于Spring boot的服务器,作为API的后端,并且实现一个Filter,对 APIC的请求做签名校验。

● API绑定basic类型签名密钥后,发给后端的请求中会添加basic认证信息,其中basic认证的用 户名为签名秘钥的key,密码为签名秘钥的secret。

步骤1 编写一个Controller,路径为/basic。

// HelloController.java

@RestController

@EnableAutoConfiguration public class HelloController { @RequestMapping("/basic") private String basic() {

return "Basic authorization success";

} }

步骤2 编写一个Filter,按照basic认证的规则,Authorization头格式为"Basic

"+base64encode(username+":"+password)。以下为根据规则编写的校验代码。

// BasicAuthFilter.java

public class BasicAuthFilter implements Filter {

private static final String CREDENTIALS_PREFIX = "Basic ";

private static Map<String, String> secrets = new HashMap<>();

static {

secrets.put("signature_key1", "signature_secret1");

secrets.put("signature_key2", "signature_secret2");

}

@Override

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) { HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

try {

String credentials = request.getHeader("Authorization");

if (credentials == null || credentials.length() == 0) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization not found.");

return;

}

if (!credentials.startsWith(CREDENTIALS_PREFIX)) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization format incorrect.");

return;

}

String authInfo = credentials.substring(CREDENTIALS_PREFIX.length());

String decoded;

try {

decoded = new String(Base64.getDecoder().decode(authInfo));

} catch (IllegalArgumentException e) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization format incorrect.");

return;

}

String[] spl = decoded.split(":", 2);

if (spl.length < 2) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization format incorrect.");

return;

}

String signingSecret = secrets.get(spl[0]);

if (signingSecret == null) {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Username not found.");

return;

}

if (signingSecret.equals(spl[1])) { chain.doFilter(request, response);

} else {

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Incorrect username or password");

}

} catch (Exception e) { e.printStackTrace();

try {

response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

} catch (IOException e1) { }

} } }

步骤3 注册filter和路径的映射关系。

@Configuration

public class FilterConfig { @Bean

public FilterRegistrationBean registBasicAuthFilter() {

FilterRegistrationBean registration = new FilterRegistrationBean();

registration.setFilter(new BasicAuthFilter());

registration.addUrlPatterns("/basic");

registration.setName("BasicAuthFilter");

return registration;

} }

步骤4 运行服务器,验证代码正确性。将用户名和密码生成basic认证的Authorization头域传 给请求接口。如果使用错误的用户名和密码访问,服务器返回401认证不通过。

----结束

2.4.2 Python SDK 使用说明

操作场景

使用Python语言进行后端服务签名时,您需要先获取SDK,然后导入工程,最后参考 校验后端签名示例校验签名是否一致。

本章节以IntelliJ IDEA 2018.3.5版本为例介绍。

说明

Python SDK仅支持hmac类型的后端服务签名。

准备环境

● 准备待用的签名密钥的Key和Secret。

● 已在控制台创建签名密钥,并绑定API,具体请参见配置后端服务的签名校验。

● 已获取后端签名SDK,您可以在ROMA Connect实例控制台的“服务集成 APIC >

API调用”页面中下载Python SDK。

● 获取并安装2.7或3.X版本的Python安装包,如果未安装,请至Python官方下载页 面下载。

● 获取并安装2018.3.5或以上版本的IntelliJ IDEA,如果未安装,请至IntelliJ IDEA 官方网站下载。

● 已在IntelliJ IDEA中安装Python插件,如果未安装,请按照图2-46所示安装。

2-46 安装 Python 插件

导入工程

步骤1 打开IntelliJ IDEA,在菜单栏选择“File > New > Project”。

弹出“New Project”对话框,选择“Python”,单击“Next”。

2-47 New Project

步骤2 再次单击“Next”,弹出以下对话框。单击“...”,在弹出的对话框中选择解压后的 SDK路径,单击“Finish”。

2-48 选择解压后的 SDK 路径

步骤3 完成工程创建后,目录结构如下。

2-49 目录结构

步骤4 单击“Edit Configurations”,弹出“Run/Debug Configurations”对话框。

2-50 Edit Configurations

步骤5 单击“+”,选择“Flask server”。

2-51 选择 Flask server

步骤6 “Taget type”选择“Script path”,“Target”选择工程下的

“backend_signature.py”文件,单击“OK”,完成工程配置。

----结束

校验后端签名示例

说明

● 示例演示如何编写一个基于Flask的服务器,作为API的后端,并且实现一个wrapper,对 APIC的请求做签名校验。

● API绑定签名密钥后,发给后端的请求中才会添加签名信息。

步骤1 编写一个返回“Hello World!”的接口,方法为GET、POST、PUT和DELETE,且使用 requires_apigateway_signature的wrapper。

app = Flask(__name__)

@app.route("/<id>", methods=['GET', 'POST', 'PUT', 'DELETE'])

@requires_apigateway_signature() def hello(id):

return "Hello World!"

步骤2 实现requires_apigateway_signature。将允许的签名key和secret对放入一个dict中。

def requires_apigateway_signature():

def wrapper(f):

r'SDK-HMAC-SHA256\s+Access=([^,]+),\s?SignedHeaders=([^,]+),\s?Signature=(\w+)') BasicDateFormat = "%Y%m%dT%H%M%SZ"

步骤3 wrapped函数为签名校验代码。校验流程如下:使用正则表达式解析Authorization 头。得到key和signedHeaders。

if "authorization" not in request.headers:

return 'Authorization not found.', 401 authorization = request.headers['authorization']

m = authorizationPattern.match(authorization) if m is None:

return 'Authorization format incorrect.', 401 signingKey = m.group(1)

signedHeaders = m.group(2).split(";")

例如,Authorization头为:

SDK-HMAC-SHA256 Access=signature_key1, SignedHeaders=host;x-sdk-date, Signature=e11adf65a20d1b82c25419b5********8d0ba12fed1ceb13ed00

则解析的结果为:

signingKey=signature_key1 signedHeaders=host;x-sdk-date

步骤4 通过key找到secret,如果不存在key,则返回认证失败。

if signingKey not in secrets:

return 'Signing key not found.', 401 signingSecret = secrets[signingKey]

步骤5 新建一个HttpRequest对象,将请求method、url、query、signedHeaders对应的请求 头放入其中。判断是否需要设置body并设置。

需要读取body的条件为:不存在值为UNSIGNED-PAYLOAD的x-sdk-content-sha256 头。

r = signer.HttpRequest() r.method = request.method r.uri = request.path r.query = {}

for k in request.query_string.decode('utf-8').split('&'):

spl = k.split("=", 1)

needbody = True dateHeader = None for k in signedHeaders:

if k not in request.headers:

return 'Signed header ' + k + ' not found', 401 v = request.headers[k]

if k.lower() == 'x-sdk-content-sha256' and v == 'UNSIGNED-PAYLOAD':

needbody = False if k.lower() == 'x-sdk-date':

dateHeader = v r.headers[k] = v if needbody:

r.body = request.get_data()

步骤6 校验签名是否过期。从X-Sdk-Date头中取出时间,判断与服务器时间是否相差在15分 钟以内。如果signedHeaders中不包含X-Sdk-Date,也返回认证失败。

if dateHeader is None:

return 'Header x-sdk-date not found.', 401 t = datetime.strptime(dateHeader, BasicDateFormat) if abs(t - datetime.utcnow()) > timedelta(minutes=15):

return 'Signature expired.', 401

步骤7 调用verify方法校验请求签名。判断校验是否通过。

sig = signer.Signer() sig.Key = signingKey sig.Secret = signingSecret if not sig.Verify(r, m.group(3)):

return 'Verify authroization failed.', 401

步骤8 运行服务器,验证代码正确性。下面示例使用JavaScript SDK中的html签名工具生成 签名。

填入如图所示字段后,单击“Send request”,复制生成的curl命令,并在命令行中执 行,服务器返回200。

如果使用错误的Key和Secret访问,服务器返回401认证不通过。

----结束

2.4.3 C# SDK 使用说明

操作场景

使用C#语言进行后端服务签名时,您需要先获取SDK,然后打开工程,最后参考校验 后端签名示例校验签名是否一致。

说明

C# SDK仅支持hmac类型的后端服务签名。

准备环境

● 准备待用的签名密钥的Key和Secret。

● 已在控制台创建签名密钥,并绑定API,具体请参见配置后端服务的签名校验。

● 已获取后端签名SDK,您可以在ROMA Connect实例控制台的“服务集成 APIC >

API调用”页面中下载C# SDK。

● 获取并安装2019 version 16.8.4及以上版本的Visual Studio,如果未安装,请至

Visual Studio官方网站下载。

打开工程

双击SDK包中的“csharp.sln”文件,打开工程。工程中包含如下3个项目:

● apigateway-signature:实现签名算法的共享库,可用于.Net Framework与.Net Core项目。

● backend-signature:后端服务签名示例,请根据实际情况修改参数后使用。具体 代码说明请参考校验后端签名示例。

● sdk-request:签名算法的调用示例。

校验后端签名示例

说明

● 示例演示如何编写一个基于ASP.Net Core的服务器,作为API的后端,并且实现一个 IAuthorizationFilter,对APIC的请求做签名校验。

● API绑定签名密钥后,发给后端的请求中才会添加签名信息。

步骤1 编写一个Controller,提供GET、POST、PUT和DELETE四个接口,且加入 ApigatewaySignatureFilter的Attribute。

// ValuesController.cs

namespace backend_signature.Controllers { [Route("api/[controller]")]

[ApiController]

[ApigatewaySignatureFilter]

public class ValuesController : ControllerBase {

// GET api/values [HttpGet]

public ActionResult<IEnumerable<string>> Get() {

步骤2 实现一个ApigatewaySignatureFilter。将允许的签名key和secret对放入一个 Dictionary中。

// ApigatewaySignatureFilter.cs namespace backend_signature.Filters

{ public class ApigatewaySignatureFilter : Attribute, IAuthorizationFilter

{

private Dictionary<string, string> secrets = new Dictionary<string, string>

{

{"signature_key1", "signature_secret1" }, {"signature_key2", "signature_secret2" }, };

public void OnAuthorization(AuthorizationFilterContext context) { //签名校验代码

...

} } }

步骤3 OnAuthorization函数为签名校验代码。校验流程如下:使用正则表达式解析 Authorization头。得到key和signedHeaders。

private Regex authorizationPattern = new Regex("SDK-HMAC-SHA256\\s+Access=([^,]+),\\s?

SignedHeaders=([^,]+),\\s?Signature=(\\w+)");

...

string authorization = request.Headers["Authorization"];

if (authorization == null)

{ context.Result = new UnauthorizedResult();

return;

}var matches = authorizationPattern.Matches(authorization);

if (matches.Count == 0)

{ context.Result = new UnauthorizedResult();

return;

}var groups = matches[0].Groups;

string key = groups[1].Value;

string[] signedHeaders = groups[2].Value.Split(';');

例如,Authorization头为:

SDK-HMAC-SHA256 Access=signature_key1, SignedHeaders=host;x-sdk-date,

SDK-HMAC-SHA256 Access=signature_key1, SignedHeaders=host;x-sdk-date,