Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f97c675
commit 081c261
Showing
6 changed files
with
376 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
...boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package org.jeecg.config.sign.interceptor; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
/** | ||
* online 拦截器配置 | ||
*/ | ||
@Configuration | ||
public class SignAuthConfiguration implements WebMvcConfigurer { | ||
|
||
@Bean | ||
public SignAuthInterceptor signAuthInterceptor() { | ||
return new SignAuthInterceptor(); | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
String[] inculudes = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*", | ||
"/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData", | ||
"/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys", | ||
"/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"}; | ||
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(inculudes); | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
...g-boot-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package org.jeecg.config.sign.interceptor; | ||
|
||
|
||
import java.io.PrintWriter; | ||
import java.util.SortedMap; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
import org.jeecg.common.api.vo.Result; | ||
import org.jeecg.common.util.DateUtils; | ||
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper; | ||
import org.jeecg.config.sign.util.HttpUtils; | ||
import org.jeecg.config.sign.util.SignUtil; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
|
||
import com.alibaba.fastjson.JSON; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
|
||
/** | ||
* 签名拦截器 | ||
* @author qinfeng | ||
*/ | ||
@Slf4j | ||
public class SignAuthInterceptor implements HandlerInterceptor { | ||
/** | ||
* 5分钟有效期 | ||
*/ | ||
private final static long MAX_EXPIRE = 5 * 60; | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | ||
log.debug("request URI = " + request.getRequestURI()); | ||
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request); | ||
//获取全部参数(包括URL和body上的) | ||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper); | ||
//对参数进行签名验证 | ||
String headerSign = request.getHeader("X-Sign"); | ||
String timesTamp = request.getHeader("X-TIMESTAMP"); | ||
|
||
//1.校验时间有消息 | ||
try { | ||
DateUtils.parseDate(timesTamp, "yyyyMMddHHmmss"); | ||
} catch (Exception e) { | ||
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP格式必须为:yyyyMMddHHmmss"); | ||
} | ||
Long clientTimestamp = Long.parseLong(timesTamp); | ||
//判断时间戳 timestamp=201808091113 | ||
if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) { | ||
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期"); | ||
} | ||
|
||
//2.校验签名 | ||
boolean isSigned = SignUtil.verifySign(allParams,headerSign); | ||
|
||
if (isSigned) { | ||
log.debug("Sign 签名通过!Header Sign : {}",headerSign); | ||
return true; | ||
} else { | ||
log.error("request URI = " + request.getRequestURI()); | ||
log.error("Sign 签名校验失败!Header Sign : {}",headerSign); | ||
// //打印日志参数 | ||
// Set<String> keySet = allParams.keySet(); | ||
// Iterator<String> paramIt = keySet.iterator(); | ||
// while(paramIt.hasNext()){ | ||
// String pkey = paramIt.next(); | ||
// String pval = allParams.get(pkey); | ||
// log.error(" ["+pkey+":"+pval+"] "); | ||
// } | ||
|
||
//校验失败返回前端 | ||
response.setCharacterEncoding("UTF-8"); | ||
response.setContentType("application/json; charset=utf-8"); | ||
PrintWriter out = response.getWriter(); | ||
Result<?> result = Result.error("Sign签名校验失败!"); | ||
out.print(JSON.toJSON(result)); | ||
return false; | ||
} | ||
} | ||
|
||
} |
107 changes: 107 additions & 0 deletions
107
...se-core/src/main/java/org/jeecg/config/sign/util/BodyReaderHttpServletRequestWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package org.jeecg.config.sign.util; | ||
|
||
import javax.servlet.ReadListener; | ||
import javax.servlet.ServletInputStream; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletRequestWrapper; | ||
import java.io.*; | ||
import java.nio.charset.Charset; | ||
|
||
/** | ||
* 保存过滤器里面的流 | ||
* | ||
* @author show | ||
* @date 10:03 2019/5/30 | ||
*/ | ||
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { | ||
|
||
private final byte[] body; | ||
|
||
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) { | ||
|
||
super(request); | ||
String sessionStream = getBodyString(request); | ||
body = sessionStream.getBytes(Charset.forName("UTF-8")); | ||
} | ||
|
||
/** | ||
* 获取请求Body | ||
* | ||
* @param request | ||
* @return | ||
*/ | ||
public String getBodyString(final ServletRequest request) { | ||
|
||
StringBuilder sb = new StringBuilder(); | ||
try (InputStream inputStream = cloneInputStream(request.getInputStream()); | ||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")))) { | ||
String line; | ||
while ((line = reader.readLine()) != null) { | ||
sb.append(line); | ||
} | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
return sb.toString(); | ||
} | ||
|
||
/** | ||
* Description: 复制输入流</br> | ||
* | ||
* @param inputStream | ||
* @return</br> | ||
*/ | ||
public InputStream cloneInputStream(ServletInputStream inputStream) { | ||
|
||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
byte[] buffer = new byte[1024]; | ||
int len; | ||
try { | ||
while ((len = inputStream.read(buffer)) > -1) { | ||
byteArrayOutputStream.write(buffer, 0, len); | ||
} | ||
byteArrayOutputStream.flush(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); | ||
} | ||
|
||
@Override | ||
public BufferedReader getReader() { | ||
|
||
return new BufferedReader(new InputStreamReader(getInputStream())); | ||
} | ||
|
||
@Override | ||
public ServletInputStream getInputStream() { | ||
|
||
final ByteArrayInputStream bais = new ByteArrayInputStream(body); | ||
return new ServletInputStream() { | ||
|
||
@Override | ||
public int read() { | ||
|
||
return bais.read(); | ||
} | ||
|
||
@Override | ||
public boolean isFinished() { | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public boolean isReady() { | ||
|
||
return false; | ||
} | ||
|
||
@Override | ||
public void setReadListener(ReadListener readListener) { | ||
|
||
} | ||
}; | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
...cg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/HttpUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package org.jeecg.config.sign.util; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.io.UnsupportedEncodingException; | ||
import java.net.URLDecoder; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.SortedMap; | ||
import java.util.TreeMap; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
|
||
import org.jeecg.common.util.oConvertUtils; | ||
import org.springframework.http.HttpMethod; | ||
|
||
import com.alibaba.fastjson.JSONObject; | ||
|
||
/** | ||
* http 工具类 获取请求中的参数 | ||
* | ||
* @author show | ||
* @date 14:23 2019/5/29 | ||
*/ | ||
public class HttpUtils { | ||
|
||
/** | ||
* 将URL的参数和body参数合并 | ||
* | ||
* @author show | ||
* @date 14:24 2019/5/29 | ||
* @param request | ||
*/ | ||
public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException { | ||
|
||
SortedMap<String, String> result = new TreeMap<>(); | ||
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username | ||
String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/")+1); | ||
if(pathVariable.contains(",")){ | ||
result.put(SignUtil.xPathVariable,pathVariable); | ||
} | ||
// 获取URL上的参数 | ||
Map<String, String> urlParams = getUrlParams(request); | ||
for (Map.Entry entry : urlParams.entrySet()) { | ||
result.put((String)entry.getKey(), (String)entry.getValue()); | ||
} | ||
Map<String, String> allRequestParam = new HashMap<>(16); | ||
// get请求不需要拿body参数 | ||
if (!HttpMethod.GET.name().equals(request.getMethod())) { | ||
allRequestParam = getAllRequestParam(request); | ||
} | ||
// 将URL的参数和body参数进行合并 | ||
if (allRequestParam != null) { | ||
for (Map.Entry entry : allRequestParam.entrySet()) { | ||
result.put((String)entry.getKey(), (String)entry.getValue()); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* 获取 Body 参数 | ||
* | ||
* @author show | ||
* @date 15:04 2019/5/30 | ||
* @param request | ||
*/ | ||
public static Map<String, String> getAllRequestParam(final HttpServletRequest request) throws IOException { | ||
|
||
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); | ||
String str = ""; | ||
StringBuilder wholeStr = new StringBuilder(); | ||
// 一行一行的读取body体里面的内容; | ||
while ((str = reader.readLine()) != null) { | ||
wholeStr.append(str); | ||
} | ||
// 转化成json对象 | ||
return JSONObject.parseObject(wholeStr.toString(), Map.class); | ||
} | ||
|
||
/** | ||
* 将URL请求参数转换成Map | ||
* | ||
* @author show | ||
* @param request | ||
*/ | ||
public static Map<String, String> getUrlParams(HttpServletRequest request) { | ||
Map<String, String> result = new HashMap<>(16); | ||
if(oConvertUtils.isEmpty(request.getQueryString())){ | ||
return result; | ||
} | ||
String param = ""; | ||
try { | ||
param = URLDecoder.decode(request.getQueryString(), "utf-8"); | ||
} catch (UnsupportedEncodingException e) { | ||
e.printStackTrace(); | ||
} | ||
String[] params = param.split("&"); | ||
for (String s : params) { | ||
int index = s.indexOf("="); | ||
result.put(s.substring(0, index), s.substring(index + 1)); | ||
} | ||
return result; | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
...ecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package org.jeecg.config.sign.util; | ||
|
||
import com.alibaba.fastjson.JSONObject; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.util.DigestUtils; | ||
import org.springframework.util.StringUtils; | ||
|
||
import java.util.SortedMap; | ||
|
||
/** | ||
* 签名工具类 | ||
* | ||
* @author show | ||
* @date 10:01 2019/5/30 | ||
*/ | ||
@Slf4j | ||
public class SignUtil { | ||
//签名密钥串(前后端要一致,正式发布请自行修改) | ||
private static final String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a"; | ||
public static final String xPathVariable = "x-path-variable"; | ||
|
||
/** | ||
* @param params | ||
* 所有的请求参数都会在这里进行排序加密 | ||
* @return 验证签名结果 | ||
*/ | ||
public static boolean verifySign(SortedMap<String, String> params,String headerSign) { | ||
if (params == null || StringUtils.isEmpty(headerSign)) { | ||
return false; | ||
} | ||
// 把参数加密 | ||
String paramsSign = getParamsSign(params); | ||
log.info("Param Sign : {}", paramsSign); | ||
return !StringUtils.isEmpty(paramsSign) && headerSign.equals(paramsSign); | ||
} | ||
|
||
/** | ||
* @param params | ||
* 所有的请求参数都会在这里进行排序加密 | ||
* @return 得到签名 | ||
*/ | ||
public static String getParamsSign(SortedMap<String, String> params) { | ||
//去掉 Url 里的时间戳 | ||
params.remove("_t"); | ||
String paramsJsonStr = JSONObject.toJSONString(params); | ||
log.info("Param paramsJsonStr : {}", paramsJsonStr); | ||
return DigestUtils.md5DigestAsHex((paramsJsonStr+signatureSecret).getBytes()).toUpperCase(); | ||
} | ||
} |