Authored by DengXinFei

安全性校验-暂时不用

... ... @@ -33,5 +33,9 @@
<groupId>com.yoho.dsf.unions</groupId>
<artifactId>yoho-unions-dal</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
</dependency>
</dependencies>
</project>
... ...
package com.yoho.unions.exception;
import com.yoho.error.exception.ServiceException;
/**
* 请求头不正确
* Created by chzhang@yoho.cn on 2015/11/5.
*/
public class RequestHeaderInvalidateException extends ServiceException {
/**
* 异常
*
*/
public RequestHeaderInvalidateException(String headerName) {
super(500, "缺少"+headerName);
}
}
... ...
package com.yoho.unions.exception;
import com.yoho.error.exception.ServiceException;
/**
* client_secutity 不匹配
*
* Created by chzhang@yoho.cn on 2015/11/5.
*/
public class SecurityNotMatchException extends ServiceException {
/**
* 异常
*
*/
public SecurityNotMatchException() {
super(500, "数据验证错误.");
}
}
... ...
package com.yoho.unions.interceptor;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.UnmodifiableListIterator;
import com.yoho.core.common.utils.MD5;
import com.yoho.error.exception.ServiceException;
import com.yoho.unions.exception.RequestHeaderInvalidateException;
import com.yoho.unions.exception.SecurityNotMatchException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
/**
* Created by xinfei on 16/2/20.
*/
public class SecurityInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(SecurityInterceptor.class);
//读取配置文件中的private key 配置
private final Map<String, String> privateKeyMap = new HashMap<>();
// 这些url不会进行client-security校验。 例如 "/notify"
private List<String> excludeUrls;
//这些方法不用校验
private List<String> excludeMethods;
//这些IP下的这些方法不校验
private Map<String /*method*/, String /*ip*/> excludeMethodsBySubnet;
//是否启用
private boolean isDebugEnable = false;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//(1)排除掉exclude和debug模式
if (this.isIgnore(request)) {
return true;
}
//(2)获取请求参数的信息
Map<String, String> params = this.getRequestInfo(request);
//(3)验证请求参数中是否包含必填参数, 如果不包含则请求失败(联盟暂时只做 client_secret 的校验)
this.validateReqParams(params);
//(4)校验安全码是否正确
this.validateSecurity(params);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
/**
* 验证请求参数中的必填参数
*
* @param params 请求参数
* @throws ServiceException
*/
private void validateReqParams(Map<String, String> params) throws RequestHeaderInvalidateException {
ImmutableList must_exist_params = ImmutableList.of("client_secret");
UnmodifiableListIterator<String> it = must_exist_params.listIterator();
while (it.hasNext()) {
String k = it.next();
String headerValue = params.get(k);
if (StringUtils.isEmpty(headerValue)) {
logger.warn("header {} not exist or empty", k);
throw new RequestHeaderInvalidateException(k);
}
}
}
/**
* 验证请求参数的client_secret是否正确
*
* @param reqParams
* @throws SecurityNotMatchException
*/
private void validateSecurity(Map<String, String> reqParams) throws SecurityNotMatchException {
//根据请求参数生成加密码
String caculated_sign = this.getSign(reqParams);
//获取前台传入的client_secret, 比较参数是否一致
String request_sign = reqParams.get("client_secret");
if (!request_sign.equalsIgnoreCase(caculated_sign)) {
logger.warn("client security not match. request_sign:{}, caculate_sign:{}", request_sign, caculated_sign);
throw new SecurityNotMatchException();
}
}
/**
* 获取请求参数信息: requestParam
*
* @param httpServletRequest
* @return Map<String, String> 请求参数的键-值
*/
private Map<String, String> getRequestInfo(HttpServletRequest httpServletRequest) {
Map<String, String> map = new HashMap<>();
Enumeration paramNames = httpServletRequest.getParameterNames();
while (paramNames.hasMoreElements()) {
String key = (String) paramNames.nextElement();
String value = httpServletRequest.getParameter(key);
map.put(key, value);
}
return map;
}
/**
* 根据请求的参数生成client_secret
*
* @param reqParams
* @return
*/
private String getSign(Map<String, String> reqParams) {
//(1)删除不需要生成加密内容的参数
ImmutableList list = ImmutableList.of("/api", "client_secret", "q", "debug_data");
SortedMap<String, String> filtedMap = new TreeMap<>();
for (Map.Entry<String, String> entry : reqParams.entrySet()) {
String k = entry.getKey();
if (!list.contains(k)) {
filtedMap.put(k, entry.getValue());
}
}
//(2)根据客户端类型生成相映的客户端类型的KEY
String clientType = reqParams.get("client_type");
String privateKey = privateKeyMap.get(clientType);
filtedMap.put("private_key", privateKey);
//(3)将参数组装起来, 用=相连, 多个参数直接使用&连接, 如: string: k1=v1&k2=v2
List<String> array = new LinkedList<>();
for (Map.Entry<String, String> entry : filtedMap.entrySet()) {
String pair = entry.getKey() + "=" + entry.getValue();
array.add(pair.trim());
}
String signStr = String.join("&", array);
//sign md5
String sign = MD5.md5(signStr);
return sign.toLowerCase();
}
//排除掉不需要校验的
private boolean isIgnore(HttpServletRequest request) {
//如果请求url包含在过滤的url,则直接返回. 请求url可能是 "/gateway/xxx”这种包含了context的。
if (excludeUrls != null) {
final String requestUri = request.getRequestURI();
for (String excludeUri : excludeUrls) {
if (requestUri.equals(excludeUri) || requestUri.endsWith(excludeUri)) {
logger.info("excludeUri uri: {} for client-security check success.", requestUri);
return true;
}
}
}
//如果请求method包含这些,则不校验
if (excludeMethods != null) {
final String method = request.getParameter("method");
if (StringUtils.isNotEmpty(method) && excludeMethods.contains(method)) {
return true;
}
}
//配置文件配置为 is_debug_enable 为true,并且请求携带参数debug为XYZ,就放行
if (isDebugEnable && "XYZ".equals(request.getParameter("debug"))) {
return true;
}
return false;
}
/**
* spring setter from security-keyyml
*
* @param keyConfigMap security key 的配置
*/
public void setKeyConfigMap(Map<String, Object> keyConfigMap) {
List<Map<String, Object>> keys = (List<Map<String, Object>>) keyConfigMap.get("client_keys");
for (Map<String, Object> one : keys) {
privateKeyMap.put((String) one.get("type"), (String) one.get("key"));
}
}
/**
* 设置不校验的url地址
*/
public void setExcludeUrls(List<String> excludeUrls) {
this.excludeUrls = excludeUrls;
}
public void setExcludeMethods(List<String> excludeMethods) {
this.excludeMethods = excludeMethods;
}
public void setIsDebugEnable(boolean isDebugEnable) {
this.isDebugEnable = isDebugEnable;
}
}
... ...
... ... @@ -53,7 +53,7 @@
</bean>
<mvc:interceptors>
<ref bean="securityInterceptor" />
<!--<ref bean="securityInterceptor" />-->
<ref bean="threadProfileInterceptor"/>
</mvc:interceptors>
</beans>
... ...
... ... @@ -21,6 +21,6 @@ redis.proxy.address=192.168.102.217
redis.proxy.port=6379
redis.proxy.auth=
zkAddress=127.0.0.1:2181
zkAddress=127.0.0.1:11111
# web context
web.context=union
\ No newline at end of file
... ...