开放API端口SIGN算法详细设计

前言

在app开放接口api的设计中,避免不了的就是安全性问题,因为大多数接口涉及到用户的个人信息以及一些敏感的数据,所以对这些接口需要进行身份的认证,那么这就需要用户提供一些信息,比如用户名密码等,但是为了安全起见让用户暴露的明文密码次数越少越好,我们一般在web项目中,大多数采用保存的session中,然后在存一份到cookie中,来保持用户的回话有效性。但是在app提供的开放接口中,后端服务器在用户登录后如何去验证和维护用户的登陆有效性呢,以下是参考项目中设计的解决方案,其原理和大多数开放接口安全验证一样,如淘宝的开放接口token验证,微信开发平台token验证都是同理。

签名设计

原理:用户登录后向服务器提供用户认证信息(如账户和密码),服务器认证完后给客户端返回一个Token令牌,用户再次获取信息时,带上此令牌,如果令牌正取,则返回数据。对于获取Token信息后,访问用户相关接口,客户端请求的url需要带上如下参数:

时间戳:timestamp

Token令牌:token

然后将所有用户请求的参数按照字母排序(包括timestamp,token),然后全部大写,进行MD5加密,生成sign签名,这就是所说的URL签名算法。然后登陆后每次调用用户信息时,带上sign,timestamp,token参数。

例如:

原请求:    http://dsrrt.xmhcedu.gov.cn/dsideal_yy/html/ypt/index_hc.html?area_id=301053&ticket=ST-1600-Xf2j     V9vIDXMqB6bxyL6h-dsssoserver(post和get都一样,对所有参数排序加密)

加上时间戳和token

http://dsrrt.xmhcedu.gov.cn/dsideal_yy/html/ypt/index_hc.html?area_id=301053&ticket=ST-1600-Xf2j     V9vIDXMqB6bxyL6h-dsssoserver&timestamp      =12445323134&token=wefkfjdskfjewfjkjfdfnc

然后更具url参数生成sign

最终的请求如

http://dsrrt.xmhcedu.gov.cn/dsideal_yy/html/ypt/index_hc.html?area_id=301053&ticket=ST-1600-Xf2j     V9vIDXMqB6bxyL6h-dsssoserver&timestamp      =12445323134&token=wefkfjdskfjewfjkjfdfnc&sign=FDK2434JKJFD334FDF2

最终目的:减小明文的暴露次数;保证数据安全的访问。

Sign算法及原理

1. api请求客户端想服务器端一次发送用用户认证信息(用户名和密码),服务器端请求到改请求后,验证用户信息是否正确。

如果正确:则返回一个唯一不重复的字符串(一般为UUID),然后在Redis(任意缓存服务器)中维护Token----Uid的用户信息关系,以便其他api对token的校验。

如果错误:则返回错误码。

2. 服务器设计一个url请求拦截规则

(1)判断是否包含timestamp,token,sign参数,如果不含有返回错误码。

(2)判断服务器接到请求的时间和参数中的时间戳是否相差很长一段时间(时间自定义如半个小时),如果     超过则说明该 url已经过期(如果url被盗,他改变了时间戳,但是会导致sign签名不相等)。

(3)判断token是否有效,根据请求过来的token,查询redis缓存中的uid,如果获取不到这说明该token     已过期。

(4)根据用户请求的url参数,服务器端按照同样的规则生成sign签名,对比签名看是否相等,相等则放行。

(5)此url拦截只需对获取身份认证的url放行使用API,剩余所有的url都需拦截。

3. Token和Uid关系维护

对于用户登录我们需要创建token--uid的关系,用户退出时需要需删除token--uid的关系。

签名实现

 

1.获取全部请求参数

Public Map<String,String> getParamsMap(HttpServletRequest request) throws                                        ServletException,IOException{

//得到请求的参数Map,注意map的value是String数组类型

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

Map<String, String[]> map = request.getParameterMap();

Set<String> keySet = map.keySet();

for (String key : keySet) {

String[] values = (String[]) map.get(key);

for (String value : values) {

//System.out.println(key+"="+value);

}

}

//System.out.println("--------request.getParameter()--------");

//得到请求头的name集合

Enumeration<String> em = request.getParameterNames();

System.out.println(em);

while (em.hasMoreElements()) {

String name = (String) em.nextElement();

String value = request.getParameter(name);

System.out.println(name+"="+value);

params.put(name,value);

}

Return params;

}

2. 生成签名

代码如下:

import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;

import java.util.Arrays;

import java.util.Map;

import java.util.Set;

public class Sign {

public static String createSign(Map<String, String> params, boolean encode)

throws UnsupportedEncodingException {

Set<String> keysSet = params.keySet();

Object[] keys = keysSet.toArray();

Arrays.sort(keys);

StringBuffer temp = new StringBuffer();

boolean first = true;

for (Object key : keys) {

if (first) {

first = false;

} else {

temp.append("&");

}

temp.append(key).append("=");

Object value = params.get(key);

String valueString = "";

if (null != value) {

valueString = String.valueOf(value);

}

if (encode) {

temp.append(URLEncoder.encode(valueString, "UTF-8"));

} else {

temp.append(valueString);

}

}

return MD5Util.md5(temp.toString()).toUpperCase();

}

}

3.时间戳

//获取系统时间作为 时间戳

long timestamp = System.currentTimeMillis();

 

4.Token令牌:token

对于用户登录我们需要创建token--uid的关系,用户退出时需要需删除token--uid的关系。

生成token--uid关系:

1)    登陆成功

//登陆成功,随机生成uid

UUID uid = UUID.randomUUID();

//在redis中新增token--uid

String token = MD5Util.md5(userID+System.currentTimeMillis());

Jedis jedis = new Jedis("localhost",port);

jedis .set(token ,uid);

//设置过期时间为60*60*60秒-

jedis .expire(token ,60*60*60);

2)    退出时删除

//用户退出时需要需删除token--uid的关系

jedis .del(token );

5.URL拦截请求类

public class InterceptorUID implements HandlerInterceptor {

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object                                            handler)  throws Exception {

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

String encryptionURL = "";

//得到请求头的name集合

Enumeration<String> em = request.getParameterNames();

while (em.hasMoreElements()) {

String name = (String) em.nextElement();

String value = request.getParameter(name);

System.out.println(name+"="+value);

params.put(name,value);

}

if(params !=null){

encryptionURL = Sign.createSign(params, true);

System.out.println("加密后的URL:"+encryptionURL);

}

for(Map.Entry<String, String> entry:params.entrySet()){

String name = entry.getKey();

String value = entry.getValue();

//System.out.println(name+"="+value);

if("timestamp".equals(name) ){

request.setAttribute("timestamp", value);

Long howLong =System.currentTimeMillis()-Long.parseLong(value);

//(2)判断服务器接到请求的时间和参数中的时间戳是否相差超过一个小时,如果超过则说明该url已经过期.

if (howLong>60*60*60){

throw new Exception("URL过期");

}

return true;

}

//判断token是否有效,根据请求过来的token,查询redis缓存中的uid,如果获取不到这说明该token已过期。

if("token".equals(name)){

Jedis jedis = new Jedis("localhost",port);

if(!jedis.exists(value)){

throw new Exception("Token已过期");

}

}

//根据用户请求的url参数,服务器端按照同样的规则生成sign签名,对比签名看是否相等,相等则放行。

if("sign".equals(name)){

if(value.equals(encryptionURL)){

return true;

}else{

return false;

}

}

}

//返回错误

throw new Exception("URL无效");

}

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,

ModelAndView modelAndView) throws Exception {

// TODO Auto-generated method stub

}

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object                                     handler, Exception e)throws Exception {

// TODO Auto-generated method stub

}

}

使用到的工具类

1.自定义MD5

/**

* 加密算法类  MD5

* 64位加密

*

*/

public class MD5Util {

public static String md5(String src){

try {

MessageDigest md = MessageDigest.getInstance("MD5");

byte[] output=md.digest(src.getBytes());

String ret = Base64.encodeBase64String(output);

return ret;

} catch (Exception e) {

throw new Md5Exception("加密失败!",e);

}

}

}

2.查询Redis

连接Redis使用Jedis2.2稳定版,Maven Dependency如下:

<dependency>

<groupId>net.heartsavior</groupId>

<artifactId>jedis</artifactId>

<version>2.2.1.1</version>

</dependency>

厦门理想云:林汉钦

2017-7-4

开放API端口SIGN算法详细设计的更多相关文章

  1. 开放api接口签名验证

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

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

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

  3. 阿里云API网关(6)用户指南(开放 API )

    网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...

  4. 阿里云API网关(4)快速入门(开放 API)

    网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...

  5. springcloud提供开放api接口签名验证

    一.MD5参数签名的方式 我们对api查询产品接口进行优化: 1.给app分配对应的key.secret 2.Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下: a. 按照请求参数 ...

  6. 开放API接口安全处理!

    目录 概念 加密 MD5 Token 开放api参数 重复提交,恶意调用 日志 验证码 开放API接口安全处理! 参考文献: 公钥,私钥和数字签名这样最好理解 (转载) 概念 存在问题: 数据窃取 数 ...

  7. 开放API接口安全处理

    一.开放API接口定义 顾名思义,开放出来给其他人调用的API接口就是开放API接口.例如,短信接口.邮件接口. 二.开放API的弱点 数据窃取 用户的密码等信息被不轨之人窃取,登录账号发布敏感信息, ...

  8. 对飞猪H5端API接口sign签名逆向实验

    免责声明 本文章所提到的技术仅用于学习用途,禁止使用本文章的任何技术进行发起网络攻击.非法利用等网络犯罪行为,一切信息禁止用于任何非法用途.若读者利用文章所提到的技术实施违法犯罪行为,其责任一概由读者 ...

  9. CentOS 防火墙开放特定端口

    iptables是linux下的防火墙,同时也是服务名称.   service  iptables  status        查看防火墙状态 service  iptables  start   ...

随机推荐

  1. MQTT在平台中的应用【本文摘自智车芯官网】

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分.该协议支持所有平台,几乎可以把所有联 ...

  2. js定时器实现图片轮播

    效果展示如下: setInterval(moverleft,3000);定时器设置为3秒,而且实现图片下方的小圆点序号跟图片对应,点击小圆点也能切换图片. 代码如下: <!DOCTYPE htm ...

  3. PHP整数取余返回负数解决办法

    <?php $num1 = 1494313163777; $num2 = 9999; //直接计算取余会出错,出现负数 -8779 //echo $num1 % $num2;exit; //算上 ...

  4. javascript中判断变量是否存在的正确方式

    在Javascript中,我们通常判断一个变量是否存在(即不为null或者undefined),往往是这样判断的 if(tomy){ console.log(obj.name); } 这种写法在大部分 ...

  5. BZOJ3242 [Noi2013]快餐店 【环套树 + 单调队列dp】

    题目链接 BZOJ3242 题解 题意很清楚,找一点使得最远点最近 如果是一棵树,就是直径中点 现在套上了一个环,我们把环单独拿出来 先求出环上每个点外向树直径更新答案,并同时求出环上每个点外向的最远 ...

  6. debounce 与 throttle 区别

    原文地址:http://undefinedblog.com/debounce-and-throttle/ 二.什么是debounce    1. 定义 如果用手指一直按住一个弹簧,它将不会弹起直到你松 ...

  7. mysql修改表中某个字段的默认值

    Mysql中用SQL增加.删除字段,修改字段名.字段类型.注释,调整字段顺序总结   在网站重构中,通常会进行数据结构的修改,所以添加,删除,增加mysql表的字段是难免的,有时为了方便,还会增加修改 ...

  8. oracleLinux7上安装oracle11g r2(脚本简单配置环境)

    一 环境脚本简单配置 #!/bin/bashmv /etc/yum.repos.d/* /tmpmv iso.repo /etc/yum.repos.d/tar zxvf a.tar.gzmv 7Se ...

  9. Maven 标准目录结构

    Maven 标准目录结构 好的目录结构可以使开发人员更容易理解项目,为以后的维护工作也打下良好的基础.Maven2根据业界公认的最佳目录结构,为开发者提供了缺省的标准目录模板.Maven2的标准目录结 ...

  10. eclipse web(Spring+SpringMVC+Hibernate)项目迁移至intellij idea

    1.导入Eclipseweb项目 跟着导航一直下一步 出现警告不要担心,先点击确认,到后面再进行设置jdk 成功导入项目后如下图 2.对导入的项目进行配置按Ctrl+shift+alt+s(或下图中的 ...