Spring设计安全的Restful接口-无用户状态的安全

设计安全的Restful接口:无用户交互状态的接口安全设计。我们需要实现UR拦截请求拦截,接口接入授权验证和请求验重。本文中不涉及任何用户校验。

设计原理

1.UR拦截请求拦截:通过URL进行拦截过滤;

2.接入授权验证:验证请求头Token;

3.请求验重:验证请求序列Sequence;

接口设计文档请参考:WMS系统对外开放接口设计文档-V.1.0.docx

注:文档禁用于商业用途!

安全过滤器

package com.wlyd.fmcgwms.util.security;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONObject;
import com.wlyd.fmcgwms.util.Log;
import com.wlyd.fmcgwms.util.SAASTokenManager;
import com.wlyd.fmcgwms.util.StaticProperty;
import com.wlyd.fmcgwms.util.api.RSAUtils;
import com.wlyd.fmcgwms.util.ehcache.EhcacheUtil;
import com.wlyd.fmcgwms.util.sysinit.InitSysProperties;
/**
 * 接口安全过滤器
 * 
 * @package com.wlyd.fmcgwms.util.security.SecurityFilter
 * @date   2017年3月15日  下午1:57:18
 * @author pengjunlin
 * @comment   
 * @update
 */
public class SecurityFilter implements Filter {
	
	private static String API_ISENABLE ;// WMS是否开通对外接口
	
	private static String API_PALTFORM;// WMS开放的平台编码
	
	private static String API_MEMBERCODE;//WMS开放的服务商编码
	
	private static String publicKey;// 公钥
	
	private static String privateKey;// 私钥
	

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}
	
	/**
	 * 验证配置是否规范
	 * 
	 * @MethodName: isConfiged 
	 * @Description: 
	 * @param platformCode
	 * @param memberCode
	 * @return
	 * @throws
	 */
	private boolean isConfiged(String platformCode,String memberCode){
        if(API_ISENABLE==null||!API_ISENABLE.equals("true")){
			return false;
		}
        if(API_PALTFORM==null||platformCode==null||!platformCode.equals(API_PALTFORM)){
        	return false;
        }
		if (API_MEMBERCODE==null||memberCode == null||! memberCode.equals(API_MEMBERCODE)) {
			return false;
		}
		return true;
	}
	
	/**
	 * 验证token是否有效
	 * 
	 * @MethodName: validateToken 
	 * @Description: 
	 * @param token
	 * @return
	 * @throws
	 */
	private boolean validateToken(String token){
		if(token==null||token.equals("")){
			return false;
		}
		String params[]=SAASTokenManager.decryptToken(privateKey, token, "&");
		if(params==null||params.length<3){
			return false;
		}
		long now=System.currentTimeMillis();
		// Token超时验证20s
		long seconds=(now-Long.valueOf(params[2]))/1000;
		if(!API_PALTFORM.equals(params[0])||!API_MEMBERCODE.equals(params[1])||seconds>20){
			return false;
		}
		return true;
	}
	
	/**
	 * 验证请求是否重复
	 * 
	 * @MethodName: validateSequece 
	 * @Description: 
	 * @param sequence
	 * @return
	 * @throws
	 */
	public boolean validateSequece(String sequence){
		if(sequence==null||sequence.equals("")){
			return false;
		}
		String requestSequence=(String) EhcacheUtil.get(StaticProperty.REQUESTCACHE, sequence);
		// 请求序列相同验证失败
		if(requestSequence!=null&&sequence.equals(requestSequence)){
			return false;
		}
		return true;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		PrintWriter out = null;
		HttpServletRequest req = (HttpServletRequest) request;
		HttpServletResponse res = (HttpServletResponse) response;
		String platformCode = req.getHeader("PlatformCode");
		String memberCode = req.getHeader("MemberCode");
		String token = req.getHeader("Token");
		String sequence = req.getHeader("Sequence");
		//String path = req.getServletPath();
		try {
			byte [] bytes=SAASTokenManager.generateBytesToken(publicKey, platformCode, memberCode,"&");
			token = RSAUtils.bytesToString(bytes);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		
		// 验证接口是否配置正确 
		if(!isConfiged(platformCode, memberCode)){
			JSONObject json = new JSONObject();
			json.put("IsSuccess", "false");
			json.put("OperationDesc", "API parameters are not configed right! ");
			json.put("ResultCode", ResultCode.OPEN_API_CONFIG_ERROR);
			try {
				out = res.getWriter();
				out.write(json.toJSONString());
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}
		
		// 验证Token是否合法
		if(!validateToken(token)){
			JSONObject json = new JSONObject();
			json.put("IsSuccess", "false");
			json.put("OperationDesc", "Unauthorized:Token is invalid!");
			json.put("ResultCode", ResultCode.OPEN_API_TOKEN_INVALID);
			try {
				out = res.getWriter();
				out.write(json.toJSONString());
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}
		
		// 验证Sequence是否合法
		if(!validateSequece(sequence)){
			JSONObject json = new JSONObject();
			json.put("IsSuccess", "false");
			json.put("OperationDesc", "Refused:request API too frequently!");
			json.put("ResultCode", ResultCode.OPEN_API_REQUEST_REQUENTLY);
			try {
				out = res.getWriter();
				out.write(json.toJSONString());
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}

		chain.doFilter(request, response);

	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		Log.getLogger(getClass()).info(">>>SecurityFilter invoke init method。。。。。。。。。START!"); 
		API_ISENABLE = InitSysProperties.getLowerCaseFromEhcache(StaticProperty.WMS_OPEN_API_ISENABLE);
		API_PALTFORM = InitSysProperties.getUpperCaseFromEhcache(StaticProperty.WMS_OPEN_API_PLATFORM);
		API_MEMBERCODE = InitSysProperties.getUpperCaseFromEhcache(StaticProperty.WMS_OPEN_API_MEMBERCODE);
		publicKey=EhcacheUtil.get(StaticProperty.WMS_OPEN_API_RSA_PUBLIC_KEY).toString();
		privateKey=EhcacheUtil.get(StaticProperty.WMS_OPEN_API_RSA_PRIVATE_KEY).toString();
		Log.getLogger(getClass()).info(">>>SecurityFilter invoke init method。。。。。。。。。SUCCESS!"); 
	}

}

注:需要通过RSA工具生成密钥对(公钥&私钥)。

web.xml配置过滤器

<filter>
		<filter-name>SecurityFilter</filter-name>
		<filter-class>com.wlyd.fmcgwms.util.security.SecurityFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>SecurityFilter</filter-name>
		<url-pattern>/openapi/*</url-pattern>
	</filter-mapping>

控制层openapi接口

package com.wlyd.fmcgwms.controller.security;

import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.wlyd.fmcgwms.controller.BaseController;
import com.wlyd.fmcgwms.util.Tools;
/**
 * 开放API控制层
 * 
 * @package com.wlyd.fmcgwms.controller.platform.OpenAPIController
 * @date   2017年3月14日  下午4:52:12
 * @author pengjunlin
 * @comment   
 * @update
 */
@Controller
@RequestMapping("/openapi")
public class OpenAPIController extends BaseController{
	
	/**
	 * 未授权
	 * 
	 * @MethodName: unauthenticated 
	 * @Description: 
	 * @return
	 * @throws
	 */
	@RequestMapping("/unauthenticated")
	@ResponseBody
	public String unauthenticated(){
		Map<String,Object> map=new HashMap<String, Object>();
		map.put("IsSuccess", "false");
		map.put("OperationDesc", "Unauthenticated:Please contact to WMS developers!");
		return Tools.toJson(map);
	}
	
	/**
	 * 授权成功
	 * 
	 * @MethodName: success 
	 * @Description: 
	 * @return
	 * @throws
	 */
	@RequestMapping("/success")
	@ResponseBody
	public String success(){
		Map<String,Object> map=new HashMap<String, Object>();
		map.put("IsSuccess", "true");
		map.put("OperationDesc", "Authenticated!");
		return Tools.toJson(map);
	}

}

没有其它的方法。

RSA加密工具

package com.wlyd.fmcgwms.util.api;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * <p>
 * RSA公钥/私钥/签名工具包
 * </p>
 * <p>
 * 罗纳德·李维斯特(Ron [R]ivest)、阿迪·萨莫尔(Adi [S]hamir)和伦纳德·阿德曼(Leonard [A]dleman)
 * </p>
 * <p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢,一般文件不使用它来加密而是使用对称加密,<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密,这样保证密钥的安全也就保证了数据的安全
 * </p>
 * 
 * @author IceWee
 * @date 2012-4-26
 * @version 1.0
 */
public class RSAUtils {

	/**
	 * 加密算法RSA
	 */
	public static final String KEY_ALGORITHM = "RSA";

	/**
	 * 签名算法
	 */
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

	/**
	 * 获取公钥的key
	 */
	private static final String PUBLIC_KEY = "RSAPublicKey";

	/**
	 * 获取私钥的key
	 */
	private static final String PRIVATE_KEY = "RSAPrivateKey";

	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * <p>
	 * 生成密钥对(公钥和私钥)
	 * </p>
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> genKeyPair() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator
				.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}

	/**
	 * <p>
	 * 用私钥对信息生成数字签名
	 * </p>
	 * 
	 * @param data
	 *            已加密数据
	 * @param privateKey
	 *            私钥(BASE64编码)
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateK);
		signature.update(data);
		return Base64Utils.encode(signature.sign());
	}

	/**
	 * <p>
	 * 校验数字签名
	 * </p>
	 * 
	 * @param data
	 *            已加密数据
	 * @param publicKey
	 *            公钥(BASE64编码)
	 * @param sign
	 *            数字签名
	 * 
	 * @return
	 * @throws Exception
	 * 
	 */
	public static boolean verify(byte[] data, String publicKey, String sign)
			throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicK = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(publicK);
		signature.update(data);
		return signature.verify(Base64Utils.decode(sign));
	}

	/**
	 * <P>
	 * 私钥解密
	 * </p>
	 * 
	 * @param encryptedData
	 *            已加密数据
	 * @param privateKey
	 *            私钥(BASE64编码)
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] encryptedData,
			String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher
						.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher
						.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * <p>
	 * 公钥解密
	 * </p>
	 * 
	 * @param encryptedData
	 *            已加密数据
	 * @param publicKey
	 *            公钥(BASE64编码)
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] encryptedData,
			String publicKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher
						.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher
						.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * <p>
	 * 公钥加密
	 * </p>
	 * 
	 * @param data
	 *            源数据
	 * @param publicKey
	 *            公钥(BASE64编码)
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey)
			throws Exception {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		// 对数据加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * <p>
	 * 私钥加密
	 * </p>
	 * 
	 * @param data
	 *            源数据
	 * @param privateKey
	 *            私钥(BASE64编码)
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
			throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * <p>
	 * 获取私钥
	 * </p>
	 * 
	 * @param keyMap
	 *            密钥对
	 * @return
	 * @throws Exception
	 */
	public static String getPrivateKey(Map<String, Object> keyMap)
			throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return Base64Utils.encode(key.getEncoded());
	}

	/**
	 * <p>
	 * 获取公钥
	 * </p>
	 * 
	 * @param keyMap
	 *            密钥对
	 * @return
	 * @throws Exception
	 */
	public static String getPublicKey(Map<String, Object> keyMap)
			throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return Base64Utils.encode(key.getEncoded());
	}
	
	/**
	 * 将加密后的字节数组转换为对象
	 * 
	 * @MethodName: bytesToString 
	 * @Description: 
	 * @param encrytpByte
	 * @return
	 * @throws
	 */
	public static String bytesToString(byte[] encrytpByte) {
	     String result = "";
	     for (Byte bytes : encrytpByte) {
	         result += bytes.toString() + " ";
	     }
	     return result;
	 }
	
	/**
	 * 公钥加密
	 * 
	 * @MethodName: encrypt 
	 * @Description: 
	 * @param publicKey
	 * @param obj
	 * @return
	 * @throws
	 */
	public static byte[] encrypt(RSAPublicKey publicKey, byte[] obj) {
	     if (publicKey != null) {
	         try {
	             Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
	             cipher.init(Cipher.ENCRYPT_MODE, publicKey);
	             return cipher.doFinal(obj);
	         } catch (Exception e) {
	             e.printStackTrace();
	         }
	     }
	     return null;
	 }
	
	/**
	 * 私钥加密
	 * 
	 * @MethodName: decrypt 
	 * @Description: 
	 * @param privateKey
	 * @param obj
	 * @return
	 * @throws
	 */
	public static byte[] decrypt(RSAPrivateKey privateKey, byte[] obj) {
	     if (privateKey != null) {
	         try {
	             Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
	             cipher.init(Cipher.DECRYPT_MODE, privateKey);
	             return cipher.doFinal(obj);
	         } catch (Exception e) {
	             e.printStackTrace();
	         }
	     }
	     return null;
	 }

}

Token管理工具

package com.wlyd.fmcgwms.util;

import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.wlyd.fmcgwms.persistence.beans.api.AccessToken;
import com.wlyd.fmcgwms.util.api.Base64Utils;
import com.wlyd.fmcgwms.util.api.RSAUtils;
import com.wlyd.fmcgwms.util.ehcache.EhcacheUtil;
import com.wlyd.fmcgwms.util.sysinit.InitSysProperties;

/**
 * SAAS Token管理工具
 * 
 * @packge com.wlyd.wmscloud.util.SAASTokenManager
 * @date 2016年5月6日 上午10:20:01
 * @author pengjunlin
 * @comment
 * @update
 */
public class SAASTokenManager {
	

	/**
	 * Token存储对象,保持100000个并发容量(K-useraccount@corCode,V-token)
	 */
	private static final Map<String, Object> map = new ConcurrentHashMap<String, Object>(
			100000);

	/**
	 * 获取用户Token
	 * 
	 * @MethodName: getToken
	 * @Description:
	 * @param key
	 * @return
	 * @throws
	 */
	public static AccessToken getToken(String key) {
		if (map.containsKey(key)) {
			return (AccessToken) map.get(key);
		}
		return null;
	}

	/**
	 * 添加用户token
	 * 
	 * @MethodName: putToken
	 * @Description:
	 * @param key
	 *            useraccount@corCode
	 * @param accessToken
	 * @throws
	 */
	public static void putToken(String key, AccessToken accessToken) {
		map.put(key, accessToken);
	}

	/**
	 * 移除token
	 * 
	 * @MethodName: removeToken
	 * @Description:
	 * @param key
	 *            useraccount@corCode
	 * @throws
	 */
	public static void removeToken(String key) {
		if (map.containsKey(key)) {
			map.remove(key);
		}
	}

	/**
	 * 验证Token是否过期
	 * 
	 * @MethodName: isVlidateToken
	 * @Description:
	 * @param key
	 *            useraccount@corCode
	 * @return
	 * @throws
	 */
	public static boolean isVlidateToken(String key) {
		if (map.containsKey(key)) {
			AccessToken accessToken = (AccessToken) map.get(key);
			long currentTimestamp = new Date().getTime();
			// 有效时间两小时
			if (accessToken.getLongTime() - currentTimestamp > 2 * 3600 * 1000) {
				return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * 更新Token
	 * 
	 * @MethodName: reputToken
	 * @Description:
	 * @param key
	 *            useraccount@corCode
	 * @param accessToken
	 * @return
	 * @throws
	 */
	public static void reputToken(String key, AccessToken accessToken) {
		if (map.containsKey(key)) {
			putToken(key, accessToken);
		}
	}

	/**
	 * 更新Token
	 * 
	 * @MethodName: reputToken
	 * @Description:
	 * @param key
	 *            useraccount@corCode
	 * @param tokenStr
	 * @return
	 * @throws
	 */
	public static void reputToken(String key, String tokenStr) {
		if (map.containsKey(key)) {
			AccessToken accessToken = new AccessToken();
			accessToken.setToken(tokenStr);
			accessToken.setTimestamp(new Timestamp(new Date().getTime()));
			putToken(key, accessToken);
		}
	}
	
	/**
	 * 是否包含用户token
	 * @MethodName: iscontainKey 
	 * @Description: 
	 * @param key
	 *          useraccount@corCode
	 * @return
	 * @throws
	 */
	public static boolean iscontainKey(String key){
		return map.containsKey(key);
	}
	
	/**
	 * 生成RSA加密 Token
	 * 
	 * @MethodName: generateToken 
	 * @Description: 
	 * @param platformCode
	 * @param tenantCode
	 * @return
	 * @throws
	 */
	public static String generateToken(String publicKey,String platformCode,String tenantCode){
		String str=platformCode+"$"+tenantCode+"$"+new Date().getTime();
		try {
			byte [] bytes= RSAUtils.encryptByPublicKey(str.getBytes(),publicKey);
		//	return new String( bytes ,"UTF-8");
			return Base64Utils.encode(bytes);
		} catch (Exception e) {
			e.printStackTrace(); 
		}
		return null;
	}
	
	/**
	 * 生成RSA加密 Token
	 * 
	 * @MethodName: generateBytesToken 
	 * @Description: 
	 * @param platformCode
	 * @param tenantCode
	 * @return
	 * @throws
	 */
	public static byte [] generateBytesToken(String publicKey,String platformCode,String tenantCode){
		byte [] bytes=new byte[0];
		String str=platformCode+"$"+tenantCode+"$"+new Date().getTime();
		try {
			bytes= RSAUtils.encryptByPublicKey(str.getBytes(),publicKey);
			return bytes;
		} catch (Exception e) {
			e.printStackTrace(); 
		}
		return null;
	}
	
	/**
	 * 生成RSA加密 Token
	 * 
	 * @MethodName: generateBytesToken 
	 * @Description: 
	 * @param platformCode
	 * @param tenantCode
	 * @param regex
	 * @return
	 * @throws
	 */
	public static byte [] generateBytesToken(String publicKey,String platformCode,String tenantCode,String regex){
		byte [] bytes=new byte[0];
		String str=platformCode+regex+tenantCode+regex+new Date().getTime();
		try {
			bytes= RSAUtils.encryptByPublicKey(str.getBytes(),publicKey);
			return bytes;
		} catch (Exception e) {
			e.printStackTrace(); 
		}
		return null;
	}
	
	/**
	 * 模拟自动生成Token
	 * 
	 * @MethodName: getToken 
	 * @Description: 
	 * @return
	 * @throws
	 */
	public  static String getToken(){
		// 开启线程同步库存数据到OMS
		/*Map<String, Object> keyMap=null;
		try {
			keyMap = RSAUtils.genKeyPair();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String publicKey=null;
		try {
			publicKey = RSAUtils.getPublicKey(keyMap);
		} catch (Exception e) {
			e.printStackTrace();
		}
		String privateKey=null;
		try {
			privateKey = RSAUtils.getPrivateKey(keyMap);
		} catch (Exception e) {
			e.printStackTrace();
		}*/
		String token=null;
		try {
			String publicKey=EhcacheUtil.get("OMS_RSA_PUBLIC_KEY").toString();
			//String privateKey=EhcacheUtil.get("OMS_RSA_PRIVATE_KEY").toString();
			String platformCode = (String) EhcacheUtil.get("WAAS_PLATFORMCODE");
			String memberCode =  InitSysProperties.getUpperCaseFromEhcache(StaticProperty.WAAS_MEMBERCODE);
			token = SAASTokenManager.generateToken(publicKey, platformCode, memberCode);
			if(token!=null){
				token.replaceAll("\\n", "");
				token.replaceAll("\\r", "");
			}
		} catch (Exception e) { 
			Log.getLogger(SAASTokenManager.class).error("Token Error:"+e.getMessage());
			e.printStackTrace();
		}
		return token;
	}
	
	/**
	 * 解密Token
	 * 
	 * @MethodName: decryptToken 
	 * @Description: 
	 * @param privateKey
	 * @param token
	 * @param regex
	 * @return
	 * @throws
	 */
	public static String [] decryptToken(String privateKey,String token,String regex){
		String params[]=new String[0];
		try {
			String[] strArr = token.split(" ");
	        
	        int len = strArr.length;
	        
	        // 转回bytes
	        byte[] clone = new byte[len];
	         
	        for (int i = 0; i < len; i++) {
	             clone[i] = Byte.parseByte(strArr[i]);
	        }
	        String decryptedToken=new String(RSAUtils.decryptByPrivateKey(clone, privateKey));
			params=decryptedToken.split(regex);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return params;
	}

	/**
	 * @throws Exception 
	 * 测试函数入口
	 * 
	 * @MethodName: main
	 * @Description:
	 * @param args
	 * @throws
	 */
	public static void main(String[] args) throws Exception {
		System.out.println(Md5.getMD5Str("123456"));
		String key = "wmsadmin@10000"; 
		AccessToken accessToken = new AccessToken();
		accessToken.setToken("token==xxjisifdihfifdds");
		accessToken.setTimestamp(new Timestamp(new Date().getTime()));
		putToken(key, accessToken);
		AccessToken accessToken2 = getToken(key);
		System.out.println("token:" + accessToken2.getToken());
		System.out.println("isValidate:" + isVlidateToken(key));
		
		Map<String, Object> keyMap=RSAUtils.genKeyPair();
		String publicKey=RSAUtils.getPublicKey(keyMap);
		String privateKey=RSAUtils.getPrivateKey(keyMap);

		System.out.println("publicKey:\n"+publicKey);
		System.out.println("privateKey:\n"+privateKey);

		String token=generateToken(publicKey,"1234", "10000");
		
		byte [] bitesToken=RSAUtils.encryptByPublicKey(("1234$10000$"+new Date().getTime()).getBytes(), publicKey);
		
		System.out.println("RSA Token:"+token);
		
		System.out.println("RSA bites Token加密:"+new String(bitesToken,"UTF-8")); 
		
		System.out.println("RSA bites Token解密:"+new String(RSAUtils.decryptByPrivateKey(bitesToken, privateKey))); 
		
		System.out.println("加密:"+new String(RSAUtils.encryptByPublicKey("this is data".getBytes(), publicKey),"UTF-8"));
		System.out.println("明文:"+new String(RSAUtils.decryptByPrivateKey(RSAUtils.encryptByPublicKey("this is data".getBytes(), publicKey), privateKey)));
	}

}

本文中所述的token我并没有保存下来,而是客户端每次来了都进行验证。

Ehcache缓存请求30s验重

缓存方案你也可以用Redis来做,这里因为项目使用的是Ehcache故将就之。
ehcahe配置:ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- 分布式缓存对象同步  采用RMI方式  官方还提供其他同步方式  此处略 -->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">

 	<!-- <diskStore path="java.io.tmpdir" /> -->
	<!--1. 指定除自身之外的网络群体中其他提供同步的主机列表,用“|”分开不同的主机 -->
	<!-- <cacheManagerPeerProviderFactory
		class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
		properties="peerDiscovery=manual,rmiUrls=//10.100.0.60:40004/metaCache" /> -->

	<!--2. 配宿主主机配置监听程序,来发现其他主机发来的同步请求 -->
	<!-- <cacheManagerPeerListenerFactory
		class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
		properties="port=40004,socketTimeoutMillis=120000" />此处默认8000端口 -->
	
	<!-- 默认缓存 -->
	<defaultCache 
		maxElementsInMemory="10000" 
		eternal="false"
		timeToIdleSeconds="1800" 
		overflowToDisk="true"
		diskSpoolBufferSizeMB="30" 
		maxElementsOnDisk="10000000"
		diskPersistent="false" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
	
	<!-- 缓存 -->
	<cache name="metaCache" 
		maxElementsInMemory="1000" 
		eternal="false"
		timeToIdleSeconds="0" 
		overflowToDisk="false"
		diskSpoolBufferSizeMB="30" 
		maxElementsOnDisk="10000000"
		diskPersistent="false" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<!-- <cacheEventListenerFactory
    			class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
 			properties="replicateAsynchronously=true, 
 						replicatePuts=false, 
 						replicateUpdates=true,
 						replicateUpdatesViaCopy=true, 
 						replicateRemovals=true "/>
		<bootstrapCacheLoaderFactory
			class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
			properties="bootstrapAsynchronously=false" /> -->
	</cache>

	<cache name="REQUESTCACHE" 
		maxElementsInMemory="100000" 
		eternal="false"
		timeToIdleSeconds="30" 
		timeToLiveSeconds="30" 
		overflowToDisk="false"
		maxElementsOnDisk="0" 
		diskPersistent="false"
		diskExpiryThreadIntervalSeconds="0" 
		memoryStoreEvictionPolicy="LRU">

	</cache>
</ehcache>

Ehcache工具类:

package com.wlyd.fmcgwms.util.ehcache;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

/**
 * ehcache 缓存工具类
 * 
 * @packge com.wlyd.fmcgwms.util.ehcache.EhcacheUtil
 * @date 2016年4月28日 下午4:11:27
 * @author pengjunlin
 * @comment cacheName在ehcache.xml中配置
 * @update 添加注释,代码重构删除原有类com。wlyd.fmcgwms.util.ehcache.EhcacheUtilOverWrite
 */
public class EhcacheUtil {

	public static CacheManager manager = CacheManager.create();// 缓存管理

	public static String cacheName = "metaCache";// 缓存名称

	/**
	 * 获取缓存对象
	 * 
	 * @MethodName: get
	 * @Description:
	 * @param key
	 * @return
	 * @throws
	 */
	public static Object get(Object key) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			Element element = cache.get(key);
			if (element != null) {
				return element.getObjectValue();
			}
		}
		return null;
	}

	/**
	 * 获取缓存对象
	 * 
	 * @MethodName: get
	 * @Description:
	 * @param cacheName
	 * @param key
	 * @return
	 * @throws
	 */
	public static Object get(String cacheName, Object key) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			Element element = cache.get(key);
			if (element != null) {
				return element.getObjectValue();
			}
		}
		return null;
	}

	/**
	 * 添加缓存对象
	 * 
	 * @MethodName: put
	 * @Description:
	 * @param key
	 * @param value
	 * @throws
	 */
	public static void put(Object key, Object value) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			cache.put(new Element(key, value));
		}
	}

	/**
	 * 添加缓存对象
	 * 
	 * @MethodName: put
	 * @Description:
	 * @param cacheName
	 * @param key
	 * @param value
	 * @throws
	 */
	public static void put(String cacheName, Object key, Object value) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			cache.put(new Element(key, value));
		}
	}

	/**
	 * 移出缓存对象
	 * 
	 * @MethodName: remove
	 * @Description:
	 * @param key
	 * @return
	 * @throws
	 */
	public static boolean remove(Object key) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			return cache.remove(key);
		}
		return false;
	}

	/**
	 * 移除缓存对象
	 * 
	 * @MethodName: remove
	 * @Description:
	 * @param cacheName
	 * @param key
	 * @return
	 * @throws
	 */
	public static boolean remove(String cacheName, Object key) {
		Cache cache = manager.getCache(cacheName);
		if (cache != null) {
			return cache.remove(key);
		}
		return false;
	}
}



测试方法:

package fmcgwms;


import org.apache.http.client.methods.HttpGet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.alibaba.fastjson.JSONObject;
import com.wlyd.fmcgwms.util.Md5;
import com.wlyd.fmcgwms.util.SAASTokenManager;
import com.wlyd.fmcgwms.util.StaticProperty;
import com.wlyd.fmcgwms.util.api.APIHttpClient;
import com.wlyd.fmcgwms.util.api.RSAUtils;
import com.wlyd.fmcgwms.util.ehcache.EhcacheUtil;
/**
 * 开放接口对外模拟测试
 * @package fmcgwms.OpenApiTest
 * @date   2017年3月15日  下午5:51:03
 * @author pengjunlin
 * @comment   
 * @update
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OpenApiTest {
        @Test
	public void testEchcacheToTimeout(){
		
		
		String requestSequence=Md5.getMD5Str("localhost&interface&opt_type&uniquecode");
		
		System.out.println(">>>>>原始MD5串:"+requestSequence); 
		
		EhcacheUtil.put(StaticProperty.REQUESTCACHE, requestSequence, requestSequence);
		
		System.out.println(">>>>>缓存:"+EhcacheUtil.get(StaticProperty.REQUESTCACHE, requestSequence)); 
		
		try {
			Thread.sleep(31000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(">>>>>30s后缓存:"+EhcacheUtil.get(StaticProperty.REQUESTCACHE, requestSequence)); 
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		
	}


Ehcache的输出:

>>>>>原始MD5串:cae76d65fc5c47875730ed35a262abb4
>>>>>缓存:cae76d65fc5c47875730ed35a262abb4
2017-03-16 15:13:49,647 [net.sf.ehcache.CacheManager@2f1a74d1] INFO  [net.sf.ehcache.util.UpdateChecker] - This is the latest GA release.
2017-03-16 15:14:00,016 [SpringJobSchedulerFactoryBean_Worker-1] ERROR [com.wlyd.fmcgwms.persistence.beans.platform.job.CycleFetchInterfaceInfoTaskJob] - >>>WMS-WAAS接口定时任务<<启动失败---fetch----:请设置WAAS_API_ISENABLE=true
2017-03-16 15:14:00,016 [SpringJobSchedulerFactoryBean_Worker-2] ERROR [com.wlyd.fmcgwms.persistence.beans.platform.job.CycleSendInterfaceInfoTaskJob] - >>>WMS-WAAS定时任务<<启动失败----send---:请设置WAAS_API_ISENABLE=true
>>>>>30s后缓存:null


  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值