spring mvc 加签验签方案

一、请求格式要求:

1.GET 请求

 URL示例:http://xxxxxxxx/api/test.do?a=1&b=2&c=3&d=4

 
 
content-type:  
签名 key值与a=1&b=2&c=3&d=4 进行一定处理后加密得到

 

 

2.POST请求

URL示例:http://xxxxxxxx/api/test.do

 
 
content-type: application/json
requestBody: {a:1,b:'2',c:'3',d:'4'}
签名 key值与与{a:1,b:'2',c:'3',d:'4'} 进行一定处理后加密得到

 

二、签名参数:

通过第一步中得到签名值后,以参数名 “signature”添加到RequestHead中。

 

四、验签方案:

(1) 增加一个Filter,对POST请求从inputstream中获取json参数,放到ThreadLocal中,并替换掉原来的HttpServlertRequest,因为inputStream只能读一次,如果不替换,会导致spring将参数转换为JSON时没有数据可读。(对GET请求可以不作处理)

(2) 增加一个spring mvc 拦截器,从RequestHead中取出签名,与ThreadLocal中的参数进行验证。(GET请求直接从URL中取参数)

Filter代码如下:

 

    @Override  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
  
    	MyHttpServletRequestWrapperwrap = null;  
       if (request instanceof HttpServletRequest) {  
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;  
            if ("POST".equals(httpServletRequest.getMethod().toUpperCase())  
                    && httpServletRequest.getContentType().contains("application/json")) {  
            	wrap = new MyHttpServletRequestWrapper(httpServletRequest);
            	//request = wrap;
            	TokenBag.setParam(wrap.getJsonPararms());
            }  
        }  
        if(null != wrap){
        	chain.doFilter(wrap, response); 
        }else{
        	chain.doFilter(request, response);
        }
    }

 

MyHttpServletRequestWrapper:用来替换原来的request对象

 

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

	private String jsonPararms;
	
	public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException{
		super(request);
		ServletInputStream  stream = this.getRequest().getInputStream();  
		jsonPararms = IOUtils.toString(stream, "UTF-8"); 
	}
	
	@Override  
    public ServletInputStream getInputStream() {  
		byte[] buffer = null;  
	     try {  
	         buffer = jsonPararms.toString().getBytes("UTF-8");  
	     } catch (UnsupportedEncodingException e) {  
	         e.printStackTrace();  
	     }  
	     final ByteArrayInputStream bais = new ByteArrayInputStream(buffer);  
	     ServletInputStream newStream = new ServletInputStream() {  
	         @Override  
	         public int read() throws IOException {  
	             return bais.read();  
	         }  
	     };  
	     return newStream;  
    }

	public String getJsonPararms() {
		return jsonPararms;
	}

	public void setJsonPararms(String jsonPararms) {
		this.jsonPararms = jsonPararms;
	}

}

 

mvc拦截器:

 

public class SignatureInterceptor extends HandlerInterceptorAdapter{

	private Logger logger = Logger.getLogger(SignatureInterceptor.class);
	
	private TokenService tokenService;
	
	/**
	 * This implementation always returns {@code true}.
	 */
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
		if(logger.isDebugEnabled()){
			logger.debug("check http request signature start...");
		}
		HandlerMethod hm = (HandlerMethod)handler;
                //通过方法上的注解来标识哪些请求需要验签
		Signature s = hm.getMethodAnnotation(Signature.class);
		if(null == s){
			return true;
		}
		String signature = request.getHeader("signature");
		if(StringUtils.isEmpty(signature)){
			logger.error("signature is empty,check signature failed...");
			response.setStatus(401);
			return false;
		}
		if("GET".equals(request.getMethod())){
			String param = request.getQueryString();
			if(checkSignature(signature,param,response)){
				//验签通过
				return super.preHandle(request,response,handler);
			}else{
				response.setStatus(401);
				return false;
			}
		}else if("POST".equals(request.getMethod())){
                        //TokenBag就是一个ThreadLocal
			String param = TokenBag.getParam();
			TokenBag.clean();
			if(StringUtils.isEmpty(param)){
				logger.error("param is empty,check signature failed...");
				response.setStatus(401);
				return false;
			}
			if(checkSignature(signature,param,response)){
				//验签通过
				return super.preHandle(request,response,handler);
			}else{
				response.setStatus(401);
				return false;
			}
		}else{
			logger.error("signature only support POST and GET");
			return false;
		}
	}

  验签方法要与加签方法一致,可以通过app和服务端使用相同的key值加密,最好是使用每个用户惟一的、动态的key值(比如token)来做。

 

 

猜你喜欢

转载自chenqunhui.iteye.com/blog/2307820