1.拦截器
package com.sys.interceptor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.authz.annotation.Logical;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.api.base.enums.ChannelTypeEnum;
import com.api.base.vo.BaseVO;
import com.common.utils.AESUtil;
import com.common.utils.SignVerifyUtil;
import com.common.utils.StringUtil;
import com.common.utils.WebConfigUtil;
import com.sys.annotation.VerifySign;
import com.sys.user.entity.User;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
*
* 综合性的拦截器 1.过滤敏感字符 2.解析验签注解 3.解析token注解
*/
public class SystemHandlerInterceptor extends HandlerInterceptorAdapter {
private final Logger log = Logger.getLogger(this.getClass());
@Autowired
private JedisPool jedisPool;
// 排除的urls
private static List<String> list;
// 校验timeStamp的url
private static List<String> timeStampList;
// app类型url
private static List<String> AppSUrlList;
private static List<String> AppYUrlList;
// 平台类型url
private static List<String> platformUrlList;
// 门户类型url
private static List<String> portalSUrlList;
private static List<String> portalYUrlList;
private static List<String> goList = new ArrayList<String>();
// 请求提交方式
private final static String METHOD_GET = "GET";
private final static String METHOD_POST = "POST";
// 前端传过来的的签名数据的参数名
private final static String VERIFY_PARAME = "h5SslSignData";
// 请求提交方式的确定
public final static String REQUEST_METHOD_TYPE = "application/json";
// 请求提交方式的确定
//ajax 方式提交
public final static String REQUEST_HEADER_AJAX = "XMLHttpRequest";
// 过滤敏感字符需要排除的参数名称
public static List<String> PASS_KEYS = new ArrayList<String>();
// 排除带后缀的参数
public final static String PASS_KEYS_SUFFIX = "IMG";
static {
// 初始化静态参数
PASS_KEYS.add("token");
PASS_KEYS.add(VERIFY_PARAME);
PASS_KEYS.add("fileContent");
PASS_KEYS.add("doc");
PASS_KEYS.add("createUrl");
PASS_KEYS.add("imageOR");
PASS_KEYS.add("imgLists");
PASS_KEYS.add("base64");
PASS_KEYS.add("image");// 不拦截图片参数
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 拦截路径被修改后使用这个获取url 不带工程名的 request.getRequestURI() 带工程名不可取
String url = request.getServletPath() +" "+ request.getPathInfo();
String uri = request.getRequestURI();
//1. 放过不需要验证的url放行
for (String path : list) {
if (uri.contains(path)) {
// 如果包含 就放过不验证
log.info(uri + " 放过");
return true;
}
}
log.info(uri + " 开始验证");
request.setCharacterEncoding("UTF-8");
HttpSession session = request.getSession();
// 拦截敏感字符 没有提交类型 不做验证
String contenType = request.getHeader("Content-Type");
String acceptType = request.getHeader("Accept");
String headerAjax = request.getHeader("X-Requested-With");
Map<String, String> dataMap = new HashMap<String, String>();
BaseVO vo = new BaseVO();
// 获取所有的参数和值,过程中过滤敏感字符
boolean b = true;
// 区分ajax的提交类型 解析json数组情况的内容
if (contenType != null && contenType.length() > 0 && contenType.contains(REQUEST_METHOD_TYPE)) {
String requestBody = this.getRequestPayload(request);
// 1验证敏感词
b = setDataMap(requestBody, dataMap);
} else {
//form 或者get 提交类型数据
Map<String, String[]> map = request.getParameterMap();
String queryString = request.getQueryString();
log.info("获取: "+queryString);
//1验证敏感词
b = this.getParameterMap(map, dataMap);
}
// 处理敏感词的过滤结果
if (!b) {
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
// 如果包含,则是ajax提交,否则是form提交
if (acceptType.contains(REQUEST_METHOD_TYPE)) {
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap.put("code", "sensitiveError");
resultMap.put("msg", "操作失败,您输入的内容含有非法字符请检查");
response.getWriter().println(JSON.toJSONString(resultMap));
} else {
response.getWriter().println(this.goBackHtml(request.getContextPath()));
}
log.error("操作失败,您输入的内容含有非法字符请检查......");
return false;
}else {
//2验证是否登录
String channelType = dataMap.get("channelType");
String token = dataMap.get("token");
if(!isLogin(channelType,token, request,uri)) {
//request.getRequestDispatcher("../web/login").forward(request, response);
return false;
}
//3验证是否重复提交
String timeStamp = dataMap.get("timeStamp");
timeStamp = AESUtil.decrypt(timeStamp);
for (String path : timeStampList) {
if (uri.contains(path)) {// 具备验证timeStamp资格
log.info("uri: "+uri +"对比 path: "+path);
if (!checkSubmit(timeStamp, request, uri)) { // 验证
log.info("timeStamp 验证失败(请检查是否重复或为空)");
return false;
}
}
}
}
// 如果没有参数 直接放过
if (dataMap == null || dataMap.size() == 0) {
return true;
}
// 开始解析自定义注解
if (handler instanceof HandlerMethod) {
Method method = ((HandlerMethod) handler).getMethod();
// 验签的注解
log.info("开始验签......");
if(!verifySign(method,dataMap)) {
return false;
}
}
return super.preHandle(request, response, handler);
}
/**
* 验证签名
* @param method
* @param dataMap
* @return
*/
private Boolean verifySign(Method method,Map<String, String> dataMap) {
VerifySign verifySign = method.getAnnotation(VerifySign.class);
if (verifySign != null) {
String[] verifyFields = verifySign.verifyFields();
StringBuffer sb = new StringBuffer();
for (String field : verifyFields) {
sb.append(dataMap.get(field) == null ? "" : dataMap.get(field));
}
// 验签开始
if (!SignVerifyUtil.verifyFields(sb.toString(), dataMap.get(VERIFY_PARAME))) {
log.error("验签失败......");
return false;
}else {
log.info("验签成功....");
}
}
return true;
}
/**
* 重复提交校验
* 1 判断session有没有,没有也是正确 ,设置成前台 ;
* 2 不相等,设置成前台时间.
* 3 如果前台没有,程序终止;
* @param timeStamp
* @param request
* @return
*/
private Boolean checkSubmit(String timeStamp,HttpServletRequest request,String methodName){
Boolean ret = false;
if (timeStamp != null) {
String timeStampSession = (String) request.getSession().getAttribute(methodName);
if (timeStampSession == null) {
ret = true;
} else if (!timeStampSession.equals(timeStamp)) {
ret = true;
}
if ( ret ){
request.getSession().setAttribute(methodName, timeStamp);
}
}else {
log.info("timeStamp 为空");
}
return ret;
}
/**
* 验证用户是否登录,根据channelType区分平台,门户,App
* @param
* @return
* @throws UnsupportedEncodingException
*/
private Boolean isLogin(String channelType, String token, HttpServletRequest request, String uri)
throws UnsupportedEncodingException {
boolean flag = false;
log.info("进入登录验证的uri :"+uri);
if (ChannelTypeEnum.APP.getCode().equals(channelType) || ChannelTypeEnum.APP.getCode().equals(AESUtil.decrypt(channelType)) ) {
// 手机端的交互 验证token有效期
Jedis jedis = jedisPool.getResource();
// 判断token超时时间
String TokenUser = jedis.get("TokenUser" + token);
User userJson = JSONObject.parseObject(TokenUser, User.class);
if (!StringUtil.isBlank(TokenUser)) {
jedis.expire("TokenUser" + token, 1800);
} else {
log.info("根据token获取Redis为空,请登录");
flag = false;
return flag;
}
// 获取用户级别(1用户2.运营商3平台)
String userLevel = userJson.getUserLevel();
if ("1".equals(userLevel)) { // 用户菜单权限
for (String path : AppYUrlList) {
if (uri.contains(path)) {
flag = true;
}
}
} else if ("2".equals(userLevel)) { // 商户菜单权限
for (String path : AppSUrlList) {
if (uri.contains(path)) {
flag = true;
}
}
}
} else {
// 平台 和 门户展示
User user = (User) request.getSession().getAttribute("user");
if (user == null) {
flag = false;
log.info("验证是否登录菜单权限返回值: "+flag);
return flag;
}
// 获取用户级别(1用户2.运营商3平台)
String userLevel = user.getUserLevel();
if ("1".equals(userLevel)) { // 用户菜单权限
for (String path : portalYUrlList) {
if (uri.contains(path)) {
log.info("用户进入 uri: "+uri +"对比 path: "+path);
flag = true;
}
}
} else if ("2".equals(userLevel)) {
for (String path : portalSUrlList) {// 商户菜单权限
if (uri.contains(path)) {
log.info("商户进入 uri: "+uri +"对比 path: "+path);
flag = true;
}
}
} else if ("3".equals(userLevel)) {
for (String path : platformUrlList) {// 平台菜单权限
if (uri.contains(path)) {
log.info("平台进入 uri: "+uri +"对比 path: "+path);
flag = true;
}
}
}
}
log.info("验证是否登录菜单权限返回值: "+flag);
return flag;
}
// 敏感词判断 inputString要验证的字段,str该字段值
private Boolean sensitiveValidate(String inputString) {
if (inputString == null) {
return false;
}
// 从配置文件读取敏感词字符串并转为数组
String sensitive = WebConfigUtil.getValue("sensitive").toLowerCase();
String[] sensitiveArrays = sensitive.split(",");
inputString = inputString.toLowerCase();
// 判断入参字段是否含有敏感词数组中的敏感词
for (int i = 0; i < sensitiveArrays.length; i++) {
if (inputString.trim().contains(sensitiveArrays[i].trim())) {
return true;
}
}
return false;
}
// 直接返回上一页的数据
public String goBackHtml(String path) throws IOException {
StringBuffer sbHtml = new StringBuffer();
sbHtml.append(
"<!doctype html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
sbHtml.append("<title></title><link href=\"" + path + "/static/pop/pop.css\" rel=\"stylesheet\"></head><body>"
+ "<script src=\"" + path + "/static/js/jquery-1.9.1.min.js\"></script>"
+ "<script type=\"text/javascript\" src=\"" + path + "/static/pop/pop.js\"></script>"
+ "<script>pop('操作失败,您输入的内容含有非法字符请检查');</script></body></html>");
return sbHtml.toString();
}
// 获取ajax中post提交时contentType: "application/json; charset=utf-8"的数据
private String getRequestPayload(HttpServletRequest req) throws IOException {
req.setCharacterEncoding("UTF-8");
StringBuilder sb = new StringBuilder();
BufferedReader reader = req.getReader();
char[] buff = new char[1024];
int len;
while ((len = reader.read(buff)) != -1) {
sb.append(buff, 0, len);
}
return sb.toString();
}
// 递归查询的函数 带返回值 终止
public boolean setDataMap(String jsonString, Map<String, String> dataMap) {
if (jsonString == null) {
return true;
}
boolean b = true;
Map<String, Object> map = JSON.parseObject(jsonString);
if(map==null) {
return b;
}
// 循环map
outer: for (Map.Entry<String, Object> entry : map.entrySet()) {
Object obj = entry.getValue();
// 排除不需要过滤的参数名
if (this.passKey(PASS_KEYS, entry.getKey())) {
dataMap.put(entry.getKey(), obj == null ? "" : obj.toString());
continue;
}
if (obj instanceof JSONObject) {
// 处理单一对象
b = setDataMap(obj == null ? "{}" : obj.toString());
if (!b) {
break outer;
}
} else if (obj instanceof JSONArray) {
// 处理list 数组对象
List<Object> list = JSONArray.parseArray(obj == null ? "{}" : obj.toString());
for (Object json : list) {
b = setDataMap(json.toString());
if (!b) {
break outer;
}
}
} else {
log.info("解密参数:"+entry.getKey()+" 值: "+obj);
// 基本数据类型
if (this.sensitiveValidate(this.getValue(obj == null ? "" : obj.toString()))) {
b = false;
break outer;
} else {
dataMap.put(entry.getKey(), obj == null ? "" : obj.toString());
}
}
}
return b;
}
// 递归查询的函数 带返回值 终止
public boolean setDataMap(String jsonString) {
if (jsonString == null) {
return true;
}
boolean b = true;
Map<String, Object> map = JSON.parseObject(jsonString);
// 循环map
outer: for (Map.Entry<String, Object> entry : map.entrySet()) {
// 排除不需要过滤的参数名
if (this.passKey(PASS_KEYS, entry.getKey())) {
continue;
}
Object obj = entry.getValue();
if (obj instanceof JSONObject) {
// 处理单一对象
b = setDataMap(obj == null ? "{}" : obj.toString());
if (!b) {
break outer;
}
} else if (obj instanceof JSONArray) {
// 处理list 数组对象
List<Object> list = JSONArray.parseArray(obj == null ? "{}" : obj.toString());
for (Object json : list) {
b = setDataMap(json.toString());
if (!b) {
break outer;
}
}
} else {
// 基本数据类型
if (this.sensitiveValidate(this.getValue(obj == null ? "" : obj.toString()))) {
b = false;
break outer;
}
}
}
return b;
}
// 获取解密后的信息
private String getValue(String value) {
String temp = null;
try {
if (value != null && value.length() > 31) { // 排除未加密的字段
temp = value;
value = AESUtil.decrypt(value); // 解密字段值
}
} catch (Exception e) {
log.info("解密失败 ,可以忽略的异常", e);
}
// 没有加密的参数重新赋值
return value == null ? temp : value;
}
// 排除不过滤的参数名 key
private boolean passKey(List<String> keys, String key) {
if (keys == null || keys.size() == 0 || key == null || key.length() == 0) {
return true;
}
return keys.contains(key) || key.endsWith(PASS_KEYS_SUFFIX);
}
// 验证是否有敏感词
private boolean getParameterMap(Map<String, String[]> map, Map<String, String> dataMap) {
String[] tmp = null;
for (String key : map.keySet()) {
tmp = map.get(key);
// 排除不需要过滤的参数名
if (this.passKey(PASS_KEYS, key)) {
dataMap.put(key, tmp == null ? "" : tmp[0]);
continue;
}
// 验签排除list类型的key值数据
if (key == null || key.endsWith("]")) {
continue;
}
log.info("解密字段 : "+key +" 值: "+ tmp);
for (String value : tmp) {
value = this.getValue(value);
// 敏感词校验
if (sensitiveValidate(value)) {
return false;
} else {
// 验签排除 数组的数据
if (map.get(key) != null && map.get(key).length == 1) {
dataMap.put(key, tmp[0]);
}
}
}
}
return true;
}
/**
* 验证session存在的token和页面提交的token 值 是否相等
*
*
* @param request
* @return true:验证通过;false:验证不通过;
*/
private boolean isRepeatSubmit(HttpServletRequest request) {
String serverToken = (String) request.getSession(false).getAttribute("token");
String clinetToken = request.getParameter("token");
if (serverToken == null || clinetToken == null) {
return true;
}
if (!serverToken.equals(clinetToken)) {
return true;
}
return false;
}
public static List<String> getList() {
return list;
}
public static void setList(List<String> list) {
SystemHandlerInterceptor.list = list;
}
public static List<String> getTimeStampList() {
return timeStampList;
}
public static void setTimeStampList(List<String> timeStampList) {
SystemHandlerInterceptor.timeStampList = timeStampList;
}
public static List<String> getAppSUrlList() {
return AppSUrlList;
}
public static void setAppSUrlList(List<String> appSUrlList) {
AppSUrlList = appSUrlList;
}
public static List<String> getAppYUrlList() {
return AppYUrlList;
}
public static void setAppYUrlList(List<String> appYUrlList) {
AppYUrlList = appYUrlList;
}
public static List<String> getPlatformUrlList() {
return platformUrlList;
}
public static void setPlatformUrlList(List<String> platformUrlList) {
SystemHandlerInterceptor.platformUrlList = platformUrlList;
}
public static List<String> getPortalSUrlList() {
return portalSUrlList;
}
public static void setPortalSUrlList(List<String> portalSUrlList) {
SystemHandlerInterceptor.portalSUrlList = portalSUrlList;
}
public static List<String> getPortalYUrlList() {
return portalYUrlList;
}
public static void setPortalYUrlList(List<String> portalYUrlList) {
SystemHandlerInterceptor.portalYUrlList = portalYUrlList;
}
}
2. 验签注解
package com.sys.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- 自定义验签注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface VerifySign {
//验签字段集合
String[] verifyFields() default{};
//签名字段集合
String[] signFields() default {“rtnCode”,“sslUuid”};
}