开放API端口SIGN算法详细设计
开放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×tamp =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×tamp =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算法详细设计的更多相关文章
- 开放api接口签名验证
不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...
- 【转】开放api接口签名验证
不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...
- 阿里云API网关(6)用户指南(开放 API )
网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...
- 阿里云API网关(4)快速入门(开放 API)
网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...
- springcloud提供开放api接口签名验证
一.MD5参数签名的方式 我们对api查询产品接口进行优化: 1.给app分配对应的key.secret 2.Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下: a. 按照请求参数 ...
- 开放API接口安全处理!
目录 概念 加密 MD5 Token 开放api参数 重复提交,恶意调用 日志 验证码 开放API接口安全处理! 参考文献: 公钥,私钥和数字签名这样最好理解 (转载) 概念 存在问题: 数据窃取 数 ...
- 开放API接口安全处理
一.开放API接口定义 顾名思义,开放出来给其他人调用的API接口就是开放API接口.例如,短信接口.邮件接口. 二.开放API的弱点 数据窃取 用户的密码等信息被不轨之人窃取,登录账号发布敏感信息, ...
- 对飞猪H5端API接口sign签名逆向实验
免责声明 本文章所提到的技术仅用于学习用途,禁止使用本文章的任何技术进行发起网络攻击.非法利用等网络犯罪行为,一切信息禁止用于任何非法用途.若读者利用文章所提到的技术实施违法犯罪行为,其责任一概由读者 ...
- CentOS 防火墙开放特定端口
iptables是linux下的防火墙,同时也是服务名称. service iptables status 查看防火墙状态 service iptables start ...
随机推荐
- 从微软msdn阅读事件的使用
微软文章:如何:在 Windows 窗体应用程序中使用事件 地址:https://msdn.microsoft.com/zh-cn/library/0y0987sc.aspx 文章:C#事件的订阅与触 ...
- 【Android入门】——模拟器的创建及常见问题汇总
[前言] 刚刚接触Android,第一门课我们就来创建一个模拟器.安卓模拟器,简称AVD(Android Virtual Device),是安卓运行的虚拟设备.有了他以后,我们就不需要在连着安卓手机进 ...
- Chromium学习笔记
1. How to build chromium Follow the steps on:http://www.chromium.org/Home 需要安装Win7 x64的OS,PC的配置尽可能高端 ...
- 批处理之SET命令
除了 下面分别介绍: 表示第二个字符到倒数第三个字符的值
- 【bzoj2049】[Sdoi2008]Cave 洞穴勘测 LCT
题目描述 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如 ...
- 你对javascript的原生操作或者工具了解多少呢?
1.有一个长度未知的数组a,如果它的长度为0就把数字1添加到数组里面,否则按照先进先出的队列规则让第一个元素出队. 分析:这道题主要是考核了数组的队列方法和栈方法.另外,原题还有字数限制的,只有在字数 ...
- Java的Properties使用及格式定义
java.util.Properties extends Hashtable<Object,Object> 方便读取 键值对 格式的文本资源工具 常用方法一览 初始化对象 new Prop ...
- git使用笔记(九)操作原理
By francis_hao Nov 27,2016 参考[1]的一张图已经把git的基本原理描述的很清楚了,如下: 下面以实例演示其过程,需要用到两个命令cat-file和ls-fil ...
- 用实例工厂的方法实例化bean
在实例化bean时,除了setter,constructor方法外,还有实例工厂方法,和静态工厂方法. 看代码: People类的代码如下: package com.timo.domain; publ ...
- Kafka自我学习-报错篇
1. kafka启动出现:Unsupported major.minor version 52.0 错误, 具体的错误输出: Exception in thread "main" ...