一、签名机制简介

1、如何保证数据在通信时的安全性

如果外部用户需要访问开放的 API接口,我们通过 HTTP Post或Get方式请求服务器,那么在写对外开放的 API接口如何保证数据的安全性的?
在开发中,为了保证数据在通信时的安全性,我们可以采用参数签名的方式来进行相关验证。所以后端在开发对外开放的 API接口时,一般会对参数进行签名来保证接口的安全性。

在设计签名算法时,主要考虑这几个问题:

  1. 请求的来源/身份是否合法?
  2. 请求参数是否被篡改?
  3. 请求是否唯一?

基于这几个问题,我们通过以下步骤来保证数据在通信时的安全性:

1.1 请求身份

通过给第三方开发者分配 AccessKey和 AccessKey Secret来验证请求者身份。

  • AccessKey ID:用于标识访问者的身份,确保唯一。也可理解为用户名
  • AccessKey Secret:用于接口加密,确保不易被穷举,生成算法不易被猜测。也可理解为用户密码

AccessKey ID和AccessKey Secret由 API服务方分配给访问者,必须严格保密。

1.2 防止篡改 - 参数签名

通过将请求的所有参数按照字母先后顺序排序后拼接再根据签名算法(比如MD5)加密得到新的字符串来保证请求参数不被篡改。

主要就是两点:

  • 构造用于签名的规范字符串
  • 将构造用于签名的规范字符串通过签名算法生成签名值

1.3 重放攻击

上面虽然解决了请求参数被篡改的隐患,但是还存在着重复使用请求参数伪造二次请求的隐患。
我们可以在请求里携带时间戳等参数来保证请求的唯一和过期或者重复的请求在指定时间内有效(可配置)。

1.3.1 timestamp+nonce方案

nonce指唯一的随机字符串,用来标识每个被签名的请求。

通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的 nonce以阻止它们被二次使用)。然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用 timestamp来优化 nonce的存储。

假设允许客户端和服务端最多能存在15分钟的时间差,同时追踪记录在服务端的 nonce集合。
当有新的请求进入时,

  • 首先检查携带的 timestamp是否在15分钟内,如超出时间范围,则拒绝,
  • 然后查询携带的 nonce,如存在已有集合,则拒绝。
  • 否则,记录该 nonce,并删除集合内时间戳大于15分钟的nonce(可以使用 redis的 expire,新增 nonce的同时设置它的超时失效时间为15分钟)。

1.3.2 Token&AppKey(APP)

在 APP开放API接口的设计中,由于大多数接口涉及到用户的个人信息以及产品的敏感数据,所以要对这些接口进行身份验证,为了安全起见让用户暴露的明文密码次数越少越好,然而客户端与服务器的交互在请求之间是无状态的,也就是说,当涉及到用户状态时,每次请求都要带上身份验证信息。

Token身份验证:

  • 用户登录向服务器提供认证信息(如账号和密码),服务器验证成功后返回 Token给客户端;
  • 客户端将 Token保存在本地,后续发起请求时,携带此 Token;
  • 服务器检查 Token的有效性,有效则放行,无效(Token错误或过期)则拒绝。

安全隐患:Token被劫持,伪造请求和篡改参数。《设计一个安全的对外接口》这篇也推荐阅读一下。

1.3.3 Token+AppKey签名验证

与上面开发平台的验证方式类似,为客户端分配 AppKey(密钥,用于接口加密,不参与传输),将AppKey和所有请求参数组合成源串,根据签名算法生成签名值,发送请求时将签名值一起发送给服务器验证。

这样,即使 Token被劫持,对方不知道 AppKey和签名算法,就无法伪造请求和篡改参数。
再结合上述的重发攻击解决方案,即使请求参数被劫持也无法伪造二次重复请求。

二、开放 API接口签名验证定义

通过对签名机制的了解,我们自己实现一个 开放 API接口签名验证。

我们API接口采用 TOKEN授权机制 + AppKey签名验证来实现进行交互。

  • 第三方在进行所有业务接口请求之前,必须先通过 API接口获取到正确的授权码(TOKEN)。
  • 上面AccessKey ID和AccessKey Secret可以理解为 token授权机制的用户名和密码,变量名自定义(AppKey和Code等)。
  • 签名算法中 构造用于签名的规范字符串的方式后端自定义。

1、请求

第三方在进行所有业务接口请求之前,必须先通过 API接口获取到正确的授权码(TOKEN)。
每个接口都有请求方式说明,主要使用 get、post进行数据交互。所有接口采取 utf-8字符集 发送。

  • get请求时,系统级参数和应用参数都以 get参数方式签名并发送。
  • post请求时,系统级参数以 get参数方式,应用参数都以 post参数方式签名并发送。
    具体请求参数请参见各接口说明。

2、请求参数

系统级参数:

appKey:用于标识访问者的身份,即用户名
format:响应数据格式
signMethod:签名算法
signVersion:签名版本
timestamp:请求时间
nonce:指唯一的随机字符串(比如uuid),用来标识每个被签名的请求
version:接口版本
sign:API 签名值
token:授权码(部分接口不需要,比如获取授权码。详见各接口定义)

应用级参数:见各接口规定的参数。

3、签名验签算法设计

3.1 签名生成算法

签名生成算法步骤如下:

  1. 生成构造用于签名的规范字符串(StringToSign)。
  2. 将 StringToSign字符串通过签名算法(这里使用 MD5)生成签名值,并将签名值转成为大写,然后再进行Base64编码。即得到最终 API 的签名值。
  3. 将 API 的签名值作为 sign参数的值添加到请求参数中,即完成对请求签名的过程。

HTTP请求的构造用于签名的规范字符串(StringToSign)伪代码如下:

StringToSign =HTTPRequestMethod + '\n' +CanonicalURI + '\n' +CanonicalQueryString + '\n' +Token + '\n' +HexEncode(Hash(RequestPayload))

参数说明:
(1)HTTPRequestMethod的值
HTTP请求方法的值,如GET、PUT、POST等。以换行符结束。

(2)CanonicalURI的值
规范URI参数(CanonicalURI)的值,以换行符结束。

规范URI,即请求资源路径,是 URI的绝对路径部分的URI编码。

格式:
根据 RFC 3986标准化URI路径,移除冗余和相对路径部分,路径中每个部分必须为URI编码。如果URI路径不以“/”结尾,则在尾部添加“/”。

(3)CanonicalQueryString的值
将 GET参数通过西面要求拼接生成规范查询字符串(CanonicalQueryString)的值,以换行符结束。
查询字符串,即查询参数或者 GET参数。如果没有查询参数,则为空字符串。
格式:
规范查询字符串需要满足以下要求:

  • 根据以下规则对每个参数名和值进行 URI编码:

    • 请勿对RFC 3986定义的任何非预留字符进行URI编码,这些字符包括:A-Z、a-z、0-9、-、_、.和~。
    • 使用%XY对所有非预留字符进行百分比编码,其中X和Y为十六进制字符(0-9和A-F)。例如,空格字符必须编码为%20,扩展UTF-8字符必须采用“%XY%ZA%BC”格式。
  • 对于每个参数,追加“URI编码的参数名称=URI编码的参数值”。如果没有参数值,则以空字符串代替,但不能省略“=”。

注意:这里我们定义了系统级参数与应用级参数,根据各接口规定的参数和 GET请求方式,合理的将系统级参数与应用级参数合并,来拼接查询字符串。

(4)Token的值
通过 API接口获取到正确的授权码(TOKEN)的值,以换行符结束。

(5)HexEncode(Hash(RequestPayload))的值
使用 SHA 256哈希函数请求正文中的 body体(RequestPayload),生成的小写哈希值。如果 RequestPayload为空或者 NULL时,默认空字符串来处理。
释义:
请求消息体。消息体需要做两层转换:HexEncode(Hash(RequestPayload)),其中:

  • Hash表示生成消息摘要的函数,当前支持SHA-256算法。
  • HexEncode表示以小写字母形式返回摘要的 Base-16编码的函数。

例如,HexEncode(“m”) 返回值为“6d”而不是“6D”。输入的每一个字节都表示为两个十六进制字符。

注意:

  • 各个参数值之间使用 换行符连接,或者你可以使用其他符合都可以。
  • 默认最后一行参数不需要添加换行符’\n’。

上面生成构造用于签名的规范字符串 参考了华为云,你也可以自定义生成规则。
示例:

stringToSign=GET
/sign-web-api/sign/getById.json/
appKey=zhaoyun&format=json&nonce=ae69c7a6-feaa-4b3d-b0a8-718d5c4d2a08&signMethod=MD5&signVersion=1.0&timestamp=1639405259585&token=3ea308fa-14c8-4d35-9dad-ac1434f4b75f&userId=1001&version=1.0
3ea308fa-14c8-4d35-9dad-ac1434f4b75f
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

3.2 签名验证算法(后端)

接口提供方验证接口请求是否可信,主要算法跟生成API签名的算法是一样的。

签名验证算法步骤如下:

  1. 得到客户端请求携带的 API签名值(API 的签名值),非空判断。
  2. 检查 Token授权码的有效性
  3. 检查携带的 timestamp的有效性
  4. 检查携带的 nonce的唯一性
  5. 服务器端根据请求方携带的参数(注意:不包括sign参数)通过签名生成算法生成 API签名值。
  6. 开始签名验证,如果服务器端生成的 API签名值与客户端请求的API签名是否一致的。如果一致,则请求是可信的,放行通过;否则就是不可信的,拒绝访问。

4、响应

接口以 JSON数据格式响应,响应的固定参数格式为:

{"success": true|false,"errorMessage": "失败时错误信息","resultData": "返回结果集"
}

具体响应结果集请参见各接口说明。

5、其他

注意:

  • 授权码(TOKEN),授权时长为一天。
  • API接口中的地址、appKey、appKeyCode 为接口方提供,对接方请勿泄露,否则后果自付。

6、API接口参数说明

这类列举一下 获取授权码(TOKEN)接口参数说明,其他接口根据业务自己定义。

6.1 获取授权码(TOKEN)接口

获取接口授权码,在调用其他业务接口前,必须通过该接口获取授权码。

  • 请求地址:xxxx
  • 请求方式:GET
  • 请求参数:?appKey=zhaoyun&appKeySecret=zhaoyun123456
  • 响应结果:
{"success": true,"errorMessage": null,"resultData": {"token": "f834480f-2e91-405e-95e1-983d4c128e08","tokenExpireTime": 1639477778147}
}

注意:

  • 授权码有效期为:1天。
  • 每调用一次或刷新后,旧的授权码(TOKEN)将失效。

三、Java代码实现

创建 maven 项目,下面贴一些主要代码。

1、自定义BodyReaderFilter

解决 springboot 对请求消息体中流不可重复读取问题。

@WebFilter(filterName = "bodyReaderFilter", urlPatterns = "/*")
public class BodyReaderFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// do nothing}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (request instanceof HttpServletRequest) {// 将请求对象包装为 可重复读取流的请求对象。注意:构造好了,但是需要在拦截器中获取requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);}if (requestWrapper == null) {chain.doFilter(request, response);} else {chain.doFilter(requestWrapper, response);}}@Overridepublic void destroy() {// do nothing}
}public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {private byte[] requestBody = null;// 用于将流保存下来public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);requestBody = StreamUtils.copyToByteArray(request.getInputStream());}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}}

2、自定义拦截器

实现 签名认证拦截。这里没有进行方法封装,步骤写的很清晰。

public class SignInterceptor implements HandlerInterceptor {Logger logger = LoggerFactory.getLogger(SignInterceptor.class);@Autowiredprivate AppTokenService appTokenService;@Autowiredprivate NonceService nonceService;/*** 15分钟*/private static final Long FIFTEEN_MINUTES = 1000 * 60 * 15L;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {BaseResult baseResult = new BaseResult();request.setCharacterEncoding("UTF-8");String method = request.getMethod();StringBuffer requestURL = request.getRequestURL();String canonicalURI = requestURL.substring(requestURL.indexOf(request.getContextPath()));if (!canonicalURI.endsWith("/")) {canonicalURI = canonicalURI + "/";}/*** 签名验证算法步骤如下:  <br/>* 1. 得到客户端请求携带的 API签名值(API 的签名值),非空判断。 <br/>* 2. 检查 Token授权码的有效性  <br/>* 3. 检查携带的 timestamp的有效性  <br/>* 4. 检查携带的 nonce的唯一性  <br/>* 5. 服务器端根据请求方携带的参数(注意:不包括sign参数)通过签名生成算法生成 API签名值。  <br/>* 6. 开始签名验证,如果服务器端生成的 API签名值与客户端请求的API签名是否一致的。如果一致,则请求是可信的,放行通过;否则就是不可信的,拒绝访问。 <br/>*/// 获取请求参数Map<String, String[]> parameterMap = request.getParameterMap();// 1. 得到客户端请求携带的 API签名值(API 的签名值),非空判断。if (!parameterMap.containsKey("sign")) {baseResult.setSuccess(false);baseResult.setErrorMessage("签名(sign)请求参数缺失");responseOutByJson(response, baseResult);return false;}String[] signArr = parameterMap.get("sign");String sign = null;if (signArr == null || StringUtils.isBlank(sign = signArr[0])) {baseResult.setSuccess(false);baseResult.setErrorMessage("签名(sign)请求参数值为空");responseOutByJson(response, baseResult);return false;}// 2. 检查 Token授权码的有效性(获取授权码接口不判断)String token = "";if(!canonicalURI.startsWith(request.getContextPath() + "/sign/getToken.json")){// 如果header中不存在token,则从参数中获取tokentoken = request.getHeader("token");if (StringUtils.isBlank(token)) {token = request.getParameter("token");}if (StringUtils.isBlank(token)) {baseResult.setSuccess(false);baseResult.setErrorMessage("token请求参数缺失,或者值为空");responseOutByJson(response, baseResult);}// 查询token信息AppToken appToken = appTokenService.queryByToken(token);if (appToken == null || appToken.getTokenExpireTime() < System.currentTimeMillis()) {baseResult.setSuccess(false);baseResult.setErrorMessage("token已过期,请重新登录");responseOutByJson(response, baseResult);}}// 3. 检查携带的 timestamp的有效性// 当前请求时间戳String timestamp = parameterMap.get("timestamp")[0];if (timestamp == null) {baseResult.setSuccess(false);baseResult.setErrorMessage("请求参数(timestamp)缺失,请检查后再试");responseOutByJson(response, baseResult);}long now = System.currentTimeMillis();// 判断 timestamp是否在规定时间范围内 5分钟 如超出时间范围,则拒绝if (now - Long.parseLong(timestamp) >= FIFTEEN_MINUTES) {baseResult.setSuccess(false);baseResult.setErrorMessage("请求超时");responseOutByJson(response, baseResult);}// 4. 检查携带的 nonce的唯一性// 查询携带的随机字符串nonceString nonce = parameterMap.get("nonce")[0];if (nonce == null) {baseResult.setSuccess(false);baseResult.setErrorMessage("请求参数(nonce)缺失,请检查后再试");responseOutByJson(response, baseResult);}// 从缓存中查找是否有相同请求,,如存在已有集合,则拒绝if (nonceService.isExist(nonce)) {baseResult.setSuccess(false);baseResult.setErrorMessage("nonce已存在,请求错误,请检查后再试");responseOutByJson(response, baseResult);} else {// 否则,记录该nonce,并删除集合内时间戳大于5分钟的nonce,新增nonce的同时设置它的超时失效时间为5分钟nonceService.saveNonceAndDeleteExpireTime(nonce);}// 5. 服务器端根据请求方携带的参数(注意:不包括sign参数)通过签名生成算法生成 API签名值。// 获取请求消息体String requestBody = getRequestBody(request);Map<String, String> getParameterMap = parameterMap.entrySet().stream().filter(m -> !"sign".equals(m.getKey())).collect(Collectors.toMap(m -> m.getKey(), m -> m.getValue()[0], (o, n) -> n));getParameterMap.put("token", token);// 生成sign,进行签名认证String genSign = SignUtils.generateSign(method, canonicalURI, getParameterMap, token, requestBody);logger.error("----preHandle---- -> sign={},genSign={}", sign, genSign);if (!sign.equals(genSign)) {baseResult.setSuccess(false);baseResult.setErrorMessage("签名(sign)不匹配, 签名验证失败");responseOutByJson(response, baseResult);return false;}return true;}/*** 获取请求消息体** @param request* @return*/private String getRequestBody(HttpServletRequest request) {StringBuilder sb = new StringBuilder("");try (BufferedReader br = request.getReader()) {String str;while ((str = br.readLine()) != null) {sb.append(str);}} catch (IOException e) {logger.error("系统异常 -> 获取请求消息体参数异常。e={}", e.getMessage());}return sb.toString();}/*** 响应输出json* * @param response* @param baseResult*/private void responseOutByJson(HttpServletResponse response, BaseResult baseResult) {response.setCharacterEncoding("UTF-8");response.setContentType("application/json");try (PrintWriter out = response.getWriter()) {out.print(JSON.toJSONString(baseResult, SerializerFeature.WriteNonStringValueAsString, SerializerFeature.WriteMapNullValue));} catch (IOException e) {logger.error("系统异常 -> 响应异常。e={}", e.getMessage());}}
}

3、签名算法工具类

public class SignUtils {private static final Logger logger = LoggerFactory.getLogger(SignUtils.class);private SignUtils() {}/*** 生成签名* * @param httpRequestMethod* @param canonicalURI* @param getParamterMap* @param token* @param requestBodyStr* @return*/public static String generateSign(String httpRequestMethod, String canonicalURI, Map<String, String> getParamterMap, String token, String requestBodyStr) {/*** 签名生成算法步骤如下:  <br/>* 1. 生成构造用于签名的规范字符串(StringToSign)。  <br/>* 2. 将 StringToSign字符串通过签名算法(这里使用 MD5)生成签名值,并将签名值转成为大写,然后再进行Base64编码。即得到最终 API的签名值。 <br/>**/String stringToSign = structureStringToSign(httpRequestMethod, canonicalURI, getParamterMap, token, requestBodyStr);// MD5后转成大写即为最终签名结果。String sign = Md5Utils.MD5Upper(stringToSign).toUpperCase();logger.info("签名算法,生成 API的签名值 -> sign={}", sign);return sign;}/*** 构造用于签名的规范字符串** @param httpRequestMethod* @param canonicalURI* @param getParamterMap* @param token* @param requestBodyStr* @return*/private static String structureStringToSign(String httpRequestMethod, String canonicalURI, Map<String, String> getParamterMap, String token, String requestBodyStr) {/*** StringToSign = <br/>*       HTTPRequestMethod + '\n' +  <br/>*       CanonicalURI + '\n' +  <br/>*       CanonicalQueryString + '\n' +  <br/>*       Token + '\n' +  <br/>*       HexEncode(Hash(RequestPayload))  <br/>*/String canonicalQueryString = spliceCanonicalQueryString(getParamterMap);// 根据RFC 3986标准化URI路径,移除冗余和相对路径部分,路径中每个部分必须为URI编码。如果URI路径不以“/”结尾,则在尾部添加“/”。if (!canonicalURI.endsWith("/")) {canonicalURI = canonicalURI + "/";}// 如果请求消息体为null,直接使用空字符串""。SHA256 哈希,并小写String sha256RequestBody = sha256RequestBody = Sha256Utils.getSHA256(StringUtils.isBlank(requestBodyStr) ? "" : requestBodyStr).toLowerCase();;// 构建规范字符串StringBuffer stringToSign = new StringBuffer("");stringToSign.append(httpRequestMethod.toUpperCase()).append("\n").append(canonicalURI).append("\n").append(canonicalQueryString).append("\n").append(token).append("\n").append(sha256RequestBody);logger.info("生成构造用于签名的规范字符串 -> canonicalQueryString={}, stringToSign={}", canonicalQueryString, stringToSign);return stringToSign.toString();}/*** 获取拼接生成规范查询字符串,不带sign** @param getParamterMap*            - 系统级参数与应用级参数合并之后的集合* @return*/public static String spliceCanonicalQueryString(Map<String, String> getParamterMap) {if (null == getParamterMap) {return null;}// 字典排序TreeMap<String, String> sortMap = new TreeMap<>(getParamterMap);return spliceParams(sortMap);}/*** 拼接参数** @param treeMap* @return*/private static String spliceParams(TreeMap<String, String> treeMap) {if (null == treeMap) {return null;}StringBuilder paramStr = new StringBuilder();/*** 去除首尾空格,符合URL编码的编码规则*/treeMap.forEach((key, value) -> {key = key.trim();key = URLEncoder.encode(key, StandardCharsets.UTF_8).replace("*", "%2A").replace("+", "%20").replace("%7E", "~");value = StringUtils.isBlank(value) ? "" : value.trim();value = URLEncoder.encode(value, StandardCharsets.UTF_8).replace("*", "%2A").replace("+", "%20").replace("%7E", "~");paramStr.append("&").append(key).append("=").append(value);});// 去掉第一个&return paramStr.substring(1);}}

4、配置类

@Configuration
public class WebConfig implements WebMvcConfigurer {@Beanpublic SignInterceptor signInterceptor() {return new SignInterceptor();}/*** 添加拦截器 https://blog.csdn.net/qq_42240485/article/details/104900009* * @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(signInterceptor()).addPathPatterns("/sign/**").excludePathPatterns("/login", "/", "/index");}/*** 添加过滤器* * @return*/@Beanpublic FilterRegistrationBean<BodyReaderFilter> Filters() {FilterRegistrationBean<BodyReaderFilter> registrationBean = new FilterRegistrationBean<BodyReaderFilter>();registrationBean.setFilter(new BodyReaderFilter());registrationBean.addUrlPatterns("/*");registrationBean.setName("bodyReaderFilter");return registrationBean;}}

参考文章:

  • 阿里云-签名机制:https://help.aliyun.com/document_detail/44396.html
  • 华为云-AK/SK签名认证流程:https://support.huaweicloud.com/devg-apisign/api-sign-algorithm.html
  • 开放 API接口签名验证:https://blog.csdn.net/yonhu123java/article/details/108483494

– 求知若饥,虚心若愚。

基于Token实现开放API接口签名验证相关推荐

  1. 开放API接口签名验证,让你的接口从此不再裸奔

    点击上方蓝色"终端研发部",选择"设为星标" 学最好的别人,做最好的我们 接口安全问题 请求身份是否合法? 请求参数是否被篡改? 请求是否唯一? AccessK ...

  2. 拒绝接口裸奔!开放API接口签名验证!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/kChH 接口安全问题 请求身份是否合 ...

  3. 【转】开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  4. 开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  5. 公司接口裸奔10年了,有必要用API接口签名验证吗?

    点击上方"阿拉奇学Java",选择"置顶或者星标" 每天早晨00点00分,与你相约! 往日回顾:微信支付的架构到底有多牛? 接口安全问题 请求身份是否合法? 请 ...

  6. 开放 API 接口安全设计思路

    开放API接口安全校验的背景: 在未进行安全处理的开放 API 接口存在诸多的风险问题,如以下三种常见场景: 1.场景一 A 公司开发的开放 API 未对接口进行安全控制,有黑客通过爬虫程序调用开放 ...

  7. 腾讯会议开放API接口,为企业打造专属的“腾讯会议”

    远程办公需求在疫情期间爆发,推动各行各业加速企业内外部协同效率的数字化改造.基于这样的背景,腾讯会议宣布开放API接口,无论是企业IT.系统集成商.SaaS服务商,均可轻松适配多种会议场景需求,同时还 ...

  8. 开放API接口整合多元办公能力,企业微信助IT企业打造高效办公平台

    5月11日,企业微信线下行业交流沙龙--IT行业专场来到北京,产品团队在现场深入解读了企业微信如何更好地帮助IT企业兼顾效率与成本,满足IT企业办公的高效.轻便和移动化. 此次活动,艺龙网及众安保险作 ...

  9. 开放api接口平台鉴权怎么做?

    我们的接口需要提供给外部第三方系统去调用,那么在做开放接口安全管理的时候先要想明白几点,为什么要做安全,有哪些地方要做安全? 解决方式: (1)优化方式一:数据加密 调用方将调用方身份信息和密码通过明 ...

  10. 基于http协议的api接口对于客户端的身份认证方式以及安全措施[转]

    基于http协议的api接口对于客户端的身份认证方式以及安全措施 由于http是无状态的,所以正常情况下在浏览器浏览网页,服务器都是通过访问者的cookie(cookie中存储的jsessionid) ...

最新文章

  1. 从Atlas到Microsoft ASP.NET AJAX(4) - Browser Compatibility
  2. 面试让你手写SQL的时候,你慌了没?
  3. Python 浮点数运算
  4. dmtracedump
  5. eclipse 搜索 正则表达式
  6. 谈论Java原子变量和同步的效率 -- 颠覆你的生活
  7. 【概率DP】$P2059$ 卡牌游戏
  8. 多重线性回归 多元线性回归_了解多元线性回归
  9. Redis:分布式锁setnx(只 实现了 互斥性和容错性)
  10. genymotion-安卓模拟器-IMEI修改方法
  11. 使用JSONP解决跨域
  12. 卡特兰数 Catalan number
  13. pr控制C语言程序,PR控制(含代码)
  14. 解决stm32下载错误 “Could not stop Cortex-M device.Please check the JTAG cable.“
  15. 无Internet访问权限-已解决
  16. 微信公众号【黄小斜】和【Java技术江湖】
  17. 【计算机网络】网页相关基础知识(万维网,HTTP,动态网页)
  18. 面试官问的最后一个问题应该怎么回答?
  19. 免费分享一个SpringBoot鲜花商城管理系统,很漂亮的
  20. Lifecycle是什么?

热门文章

  1. sqlserver 修改表字段长度(好记性不如烂笔头)
  2. 浅谈UG学习的方法(适合初学者)
  3. KITTI 3D目标检测数据集解析(完整版)
  4. php zip扩展文件,php使用ZipArchive扩展实现文件的zip压缩与zip解压
  5. 日志收集之nxlog
  6. ELK-使用nxlog+filebeat采集不同类型的日志输出到logstash
  7. Qt开发之路——基于RedfishAPI的服务器管理小应用
  8. 工业交换机芯片选择需要注意什么事项呢?
  9. 集成显卡连接显示器的线跟独立显卡的不同么,分别叫什么
  10. python实现可视化数独求解器(附代码链接及点点讲解)