All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.taobao.api.internal.sign.SignatureCheckClient Maven / Gradle / Ivy

The newest version!
package com.taobao.api.internal.sign;

import java.util.LinkedHashMap;
import java.util.Map;

import com.taobao.api.ApiException;
import com.taobao.api.FileItem;
import com.taobao.api.TaobaoClient;
import com.taobao.api.internal.util.DESUtil;
import com.taobao.api.internal.util.json.ExceptionErrorListener;
import com.taobao.api.internal.util.json.JSONReader;
import com.taobao.api.internal.util.json.JSONValidatingReader;

/**
 * 签名校验客户端
 * @author xuteng.xt
 *
 * 用于SDK本地校验签名有效性。
 * 本地缓存的SK信息,不自动更新,也不过期。
 * 因为如果保证了连接建立的时候或者是第一次检查的时候,SK的有效的,那么运行时SK信息应当是固定的。
 * 对于客户端重置SK的时候,客户运行的本地代码肯定是还没来得及修改,这个时候,不应该报错签名失败。
 * 当客户端修改了SK,重新建立连接,那么这个时候才能更新本地的SK。
 */
public class SignatureCheckClient {
	
	private TaobaoClient taobaoClient;
	
	private Map secretCacheMap ;
	
	private Map accountCacheMap ;
	
	public SignatureCheckClient(TaobaoClient taobaoClient) {
		
		secretCacheMap = new LinkedHashMap() {
			private static final long serialVersionUID = 1L;
			@Override
			protected boolean removeEldestEntry(Map.Entry eldest) {
				return size() > 1024 * 32;
			}
		};
		
		accountCacheMap = new LinkedHashMap() {
			private static final long serialVersionUID = 1L;
			@Override
			protected boolean removeEldestEntry(Map.Entry eldest) {
				return size() > 1024 * 32;
			}
		};
		
		this.taobaoClient = taobaoClient;
	}
	
	/**
	 * 客户端连接的时候调用此方法
	 * 校验签名是否正确,直接调用远程服务,不走本地缓存的SK信息
	 * 如果远程校验是成功的,那么更新本地SK信息
	 * @param appKey
	 * @param body
	 * @param signature
	 * @param algorithm
	 * @param charset
	 * @return
	 */
	public boolean checkApiRequestSign(String appKey, byte[] body, String signature, String algorithm, String charset) {
		if(!checkParams(appKey,body,signature)){
			return false;
		}

		try{
			SignatureValidateResponse response =  invokeRemote(appKey,body,algorithm,signature,charset);

			if(response.isSuccess() && response.getValid() != null && response.getValid().booleanValue()){
				//更新secret到本地,这里主要是为了更新过期时间
				if(response.getSecret() != null && response.getSecret().length() > 0){
					secretCacheMap.put(appKey, new SignCheckDO(DESUtil.decrypt(response.getSecret(), signature.substring(0,8)),response.getIsvId()));
				}
				return true;
			}
			return false;
		}catch(ApiException e){
			//网络异常的情况
		}
		
		return true;
	}

	/**
	 * 收发消息的时候调用此方法,签名检查全部走本地secret校验
	 * @param appKey
	 * @param body
	 * @param signature
	 * @param algorithm
	 * @param charset
	 * @return
	 */
	public boolean checkApiRequestSignWithCache(String appKey, byte[] body, String signature, String algorithm,String charset) {

		if(!checkParams(appKey,body,signature)){
			return false;
		}
		
		SignCheckDO signCheckDO = secretCacheMap.get(appKey);
		if (signCheckDO != null) {
			
			String topSignature = SignUtils.signAndBase64Encode(body, signCheckDO.getSecret(), algorithm, charset);
			if (topSignature.equals(signature)) {
				return true;
			}
			//如果验证失败,不做额外处理,请求远程
			//远程验证成功,修改本地secret信息;或者不修改内存secret,防止攻击者频繁失效内存Secret
		}
		
		//如果本地签名上下文不存在,调用远程
		return checkApiRequestSign(appKey,body,signature,algorithm,charset);
	}
	
	private boolean checkParams(String appKey, byte[] body, String signature) {
		
		if(appKey == null || appKey.length() == 0){
			return false;
		}
		
		if(signature == null || signature.length() == 0){
			return false;
		}
		
		if(body == null || body.length == 0){
			return false;
		}
		
		return true;
	}
	
	/**
	 * 清除本地的SK缓存信息
	 * @param appKey
	 * @return
	 */
	public SignCheckDO removeCahceSignCheckInfo(String appKey) {
		return secretCacheMap.remove(appKey);
	}
	
	/**
	 * 获取本地缓存的IsvId
	 * @param appKey
	 * @return
	 */
	public Long getIsvId(String appKey) {
		SignCheckDO signCheck = secretCacheMap.get(appKey);
		if(signCheck != null){
			return signCheck.getIsvId();
		}
		return null;
	}
	
	/**
	 * 通过appkey获取aliyun账号Id
	 * @param appKey
	 * @return
	 */
	public String getAliyunUserId(String appKey) {
		SignCheckDO signCheck = secretCacheMap.get(appKey);
		if(signCheck != null){
			return getAliyunUserId(signCheck.getIsvId());
		}
		return null;
	}
	
	/**
	 * 通过淘宝Id获取aliyun账号Id
	 * @param isvId
	 * @return
	 */
	public String getAliyunUserId(Long isvId) {
		
		String account = accountCacheMap.get(isvId);
		
		if(account == null){
			account = invokeRemoteGetAliyunUserId(isvId);
			if(account != null){
				accountCacheMap.put(isvId, account);
			}
		}
		
		return account;
	}
	
	
	/**
	 * 调用top远端获取aliyun用户信息
	 * @param userId
	 * @return
	 * @throws ApiException
	 */
	@SuppressWarnings("unchecked")
	private String invokeRemoteGetAliyunUserId(long userId) {
		try{
			AliyunUserGetRequest request = new AliyunUserGetRequest();
			request.setUserId(userId);
			AliyunUserGetResponse response = taobaoClient.execute(request);

			if(response.isSuccess()){
				String acountJson = response.getAccount();
				if(acountJson != null){
					JSONReader reader = new JSONValidatingReader(new ExceptionErrorListener());
					Map rootObj = (Map)reader.read(acountJson);
					if(rootObj != null){
						return rootObj.get("aliyunPK").toString();
					}
				}
			}
			//一般是用户没有开通阿里云服务,没有对应的阿里云账号
		}catch(ApiException e){
			//网络问题
		}
		return null;
	}
	
	public void removeAllCacheInfo(){
		secretCacheMap.clear();
	}
	
	public SignatureValidateResponse invokeRemote(String appKey, byte[] body, String algorithm, String signature, String charset) throws ApiException {
		SignatureValidateRequest request = new SignatureValidateRequest();
		request.setAlgorithm(algorithm);
		request.setSignature(signature);
		request.setCharset(charset);
		request.setBody(new FileItem("body", body));
		request.setProxyAppKey(appKey);
		return taobaoClient.execute(request);
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy