1,七牛的客户可以对七牛回调过来的信息做验证。防止其他网站的信息冒充七牛。
2,安全性
*由于回调地址是公网可任意访问的,回调服务如何确认一次回调是合法的呢?
七牛云存储在回调时会对请求数据签名,并将结果包含在请求头Authorization字段中,示例如下:
Authorization:QBox iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV:tDK-3f5xF3SJYEAwsll5g=
其中QBox为固定值,iN7Ngw…dCV为用户的Accesskey,tDK-3f…5g=为签名结果(encoded_data)
回调服务器可以通过以下方法验证其合法性:
获取AUTHORIZATION字段值中的签名结果部分encoded_data
根据Accesskey选取正确的SecretKey
获取明文:data = Request.URL.Path +”\n” +Request.Body
部分语言或框架无法直接获取请求body的原始数据,在自行拼接时应当注意,body中的数据是经过URL编码的
采用HMAC-SHA1签名算法,对明文data签名,秘钥为SecretKey,比较签名结果是否与Authorization中的encoded_data字段相同,如相同则表明这是一个合法的回调请求
以PHP语言为示例,验证代码如下:*
/**
*C('accessKey')取得 AccessKey
*C('secretKey')取得 SecretKey
*callback.php 为回调地址的Path部分
*file_get_contents('php://input')获取RequestBody,其值形如:
*name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2\
*&location=Shanghai&price=1500.00&uid=123
*/
function IsQiniuCallback(){
$authstr = $_SERVER['HTTP_AUTHORIZATION'];
if(strpos($authstr,"QBox ")!=0){
return false;
}
$auth = explode(":",substr($authstr,5));
if(sizeof($auth)!=2||$auth[0]!=C('accessKey')){
return false;
}
$data = "/callback.php\n".file_get_contents('php://input');
return URLSafeBase64Encode(hash_hmac('sha1',$data,C("secretKey"), true)) == $auth[1];
}
注意:如果回调数据包含用户的敏感数据,建议回调地址使用HTTPS协议
3,java源码
package com.qiniu.isValidCallback;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.qiniu.util.Auth;
import com.qiniu.util.StringUtils;
public class IsValidCallback extends HttpServlet {
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String ak = "****";
String sk = "****";
Auth auth = Auth.create(ak,sk);
ServletContext context = getServletContext( );
String charset = request.getCharacterEncoding();
String Authorization = request.getHeader("Authorization");
String contentType = request.getHeader("Content-Type");
String line = "";
BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
String url = "https://14190f2ca55e.b.passageway.io/IsValidCallback/servlet/IsValidCallback";
boolean check = false;
check= auth.isValidCallback(Authorization,url,StringUtils.utf8Bytes(sb.toString()),contentType);
String authorization2 = "QBox " + auth.signRequest(url, StringUtils.utf8Bytes(sb.toString()), contentType);
context.setAttribute("Authorization", Authorization);
context.setAttribute("Authorization", Authorization);
context.setAttribute("authorization2", authorization2);
context.setAttribute("body", sb.toString());
context.setAttribute("contentType", contentType);
if(check){
context.log("签名通过 ok!");
context.setAttribute("check", "签名通过 ok");
}else{
context.log("No ok!");
context.setAttribute("check", "no ok");
}
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
String out = "{\"success\":\"ok\",\"name\":\"test1\"}";
response.getOutputStream().print(out);;
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}