|
@@ -0,0 +1,399 @@
|
|
|
|
+package com.medipath.project.filter;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
+import com.medipath.cif.dto.UserDTO;
|
|
|
|
+import com.medipath.operating.dto.InterfacesDTO;
|
|
|
|
+import com.medipath.operating.result.Result;
|
|
|
|
+import com.medipath.project.dubbo.client.OperatingInterfacesDubboServiceClient;
|
|
|
|
+import com.medipath.project.dubbo.client.UserDubboServiceClient;
|
|
|
|
+import com.netflix.zuul.ZuulFilter;
|
|
|
|
+import com.netflix.zuul.context.RequestContext;
|
|
|
|
+import io.jsonwebtoken.Claims;
|
|
|
|
+import io.jsonwebtoken.ExpiredJwtException;
|
|
|
|
+import io.jsonwebtoken.Jwts;
|
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.apache.commons.lang3.tuple.ImmutableTriple;
|
|
|
|
+import org.apache.commons.lang3.tuple.Triple;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
|
|
+@Component
|
|
|
|
+public class AccessFilter extends ZuulFilter {
|
|
|
|
+
|
|
|
|
+ private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
|
|
|
|
+
|
|
|
|
+ private static final String X_CLIENT_TOKEN_USERID = "x-client-token-userId";
|
|
|
|
+ private static final String X_CLIENT_TOKEN_OPERATORID = "x-client-token-operatorId";
|
|
|
|
+
|
|
|
|
+ private static final String BEARER = "Bearer ";
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * jwt token 密钥,主要用于token解析,签名验证
|
|
|
|
+ */
|
|
|
|
+ @Value("${spring.security.oauth2.jwt.signingKey}")
|
|
|
|
+ private static String signingKey = "123456";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ @Value("${signature}")
|
|
|
|
+ private String signature; // 是否启用签名
|
|
|
|
+
|
|
|
|
+ @Value("${pub.secret}")
|
|
|
|
+ private String pubSecret;
|
|
|
|
+
|
|
|
|
+ @Value("${player.switch}")
|
|
|
|
+ private String playerSwitch;
|
|
|
|
+
|
|
|
|
+ @Resource
|
|
|
|
+ private UserDubboServiceClient userDubboServiceClient;
|
|
|
|
+
|
|
|
|
+ @Resource
|
|
|
|
+ private OperatingInterfacesDubboServiceClient operatingInterfacesDubboServiceClient;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String filterType() {
|
|
|
|
+ return "pre";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int filterOrder() {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean shouldFilter() {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
|
|
|
+// HttpServletRequest req = (HttpServletRequest) request;
|
|
|
|
+// HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req); //获得请求参数中的token值
|
|
|
|
+// String token = request.getParamter("token");
|
|
|
|
+// if(!StringUtils.isEntry(token)){ //如果请求中带有这个参数,则进行过滤加一个header头
|
|
|
|
+// requestWrapper.addHeader("tokenr", token);
|
|
|
|
+// chain.doFilter(requestWrapper, response); // Goes to default servlet.
|
|
|
|
+// }
|
|
|
|
+// chain.doFilter(request, response); // Goes to default servlet.
|
|
|
|
+// }
|
|
|
|
+ @Override
|
|
|
|
+ public Object run() {
|
|
|
|
+ RequestContext ctx = RequestContext.getCurrentContext();
|
|
|
|
+ HttpServletRequest request = ctx.getRequest();
|
|
|
|
+ String token = request.getHeader("token");
|
|
|
|
+ String servicePath = request.getRequestURI();
|
|
|
|
+ try {
|
|
|
|
+ if (StrUtil.endWith(servicePath,"/v2/api-docs")){
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+ Result<InterfacesDTO> interfacesDTOResult = operatingInterfacesDubboServiceClient.detailForGateway(servicePath);
|
|
|
|
+ if (!StringUtils.equals("200", interfacesDTOResult.getCode())) {
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"512\",\"message\":\"zuul Access Filter - unknown interfaces :"+servicePath+"\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ InterfacesDTO interfacesDTO = interfacesDTOResult.getData();
|
|
|
|
+ if (interfacesDTO.getStatus() != 1) {
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"513\",\"message\":\"zuul Access Filter - interfaces status is error\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (interfacesDTO.getAuthorizeType() == 1) {
|
|
|
|
+ // 登录拦截
|
|
|
|
+ Triple<Boolean, String, String> tripleUser = getUserIdFromToken(token,request);
|
|
|
|
+ if(tripleUser.getLeft() == false) {
|
|
|
|
+ log.warn("登录认证失败,请求接口:{},请求IP:{},请求token:{},请求参数:{}", request.getRequestURI(),
|
|
|
|
+ getIpAddress(request), request.getHeader("token"), JSON.toJSONString(request.getParameterMap()));
|
|
|
|
+ // responseResult(response, result);
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"598\",\"message\":\"zuul Access Filter - login validate is false\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ UserDTO user = userDubboServiceClient.getUser(Long.parseLong(tripleUser.getMiddle()));
|
|
|
|
+ if (user == null){
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"597\",\"message\":\"zuul Access Filter - user is null\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ if (user.getStatus() != 1){
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"596\",\"message\":\"zuul Access Filter - user status is error\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ ctx.addZuulRequestHeader(X_CLIENT_TOKEN_USERID, tripleUser.getMiddle());
|
|
|
|
+ ctx.addZuulRequestHeader(X_CLIENT_TOKEN_OPERATORID, tripleUser.getRight());
|
|
|
|
+ try{
|
|
|
|
+ userDubboServiceClient.updateLastTime(Long.parseLong(tripleUser.getMiddle()));
|
|
|
|
+ }catch (Exception e){
|
|
|
|
+ log.error("更新最后请求时间异常",e);
|
|
|
|
+ }
|
|
|
|
+// boolean pass = validateLogin(request);
|
|
|
|
+// if (!pass) {
|
|
|
|
+// log.warn("登录认证失败,请求接口:{},请求IP:{},请求token:{},请求参数:{}", request.getRequestURI(),
|
|
|
|
+// getIpAddress(request), request.getHeader("token"), JSON.toJSONString(request.getParameterMap()));
|
|
|
|
+// // responseResult(response, result);
|
|
|
|
+// ctx.setSendZuulResponse(false);
|
|
|
|
+// ctx.setResponseStatusCode(200);
|
|
|
|
+// ctx.setResponseBody("{\"code\":\"598\",\"message\":\"zuul Access Filter - login validate is false\"}");
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+// if (StringUtils.equals("true", signature)) {
|
|
|
|
+// // 验证签名
|
|
|
|
+// boolean pass = validateSign(request, (String) merchant.get("secret"));
|
|
|
|
+// if (!pass) {
|
|
|
|
+// log.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
|
|
|
|
+// getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
|
|
|
|
+//
|
|
|
|
+// Result result = new Result();
|
|
|
|
+// result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
|
|
|
|
+// // responseResult(response, result);
|
|
|
|
+// ctx.setSendZuulResponse(false);
|
|
|
|
+// ctx.setResponseStatusCode(200);
|
|
|
|
+// ctx.setResponseBody("{\"code\":\"597\",\"message\":\"zuul Access Filter - sign validate is false\"}");
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+// if (StringUtils.equals("1", i.getMerchantAuthorizeType())) {
|
|
|
|
+// // 商户授权
|
|
|
|
+// boolean pass = validateMerchantAuthorize(i.getId(),merchantCode);
|
|
|
|
+// if (!pass) {
|
|
|
|
+// log.warn("商户认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
|
|
|
|
+// getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
|
|
|
|
+//
|
|
|
|
+// Result result = new Result();
|
|
|
|
+// result.setCode(ResultCode.UNAUTHORIZED).setMessage("商户认证失败");
|
|
|
|
+// // responseResult(response, result);
|
|
|
|
+// ctx.setSendZuulResponse(false);
|
|
|
|
+// ctx.setResponseStatusCode(200);
|
|
|
|
+// ctx.setResponseBody("{\"code\":\"596\",\"message\":\"zuul Access Filter - merchant validate is false\"}");
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+ if (interfacesDTO.getReturnCodeTranslate() != null) {
|
|
|
|
+ if (interfacesDTO.getReturnCodeTranslate() == 1) {
|
|
|
|
+ request.setAttribute("returnCodeTranslate", 1);
|
|
|
|
+ } else {
|
|
|
|
+ request.setAttribute("returnCodeTranslate", 0);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ request.setAttribute("returnCodeTranslate", 0);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("异常e:{}", e);
|
|
|
|
+ ctx.setSendZuulResponse(false);
|
|
|
|
+ ctx.setResponseStatusCode(200);
|
|
|
|
+ ctx.setResponseBody("{\"code\":\"599\",\"message\":\"zuul Access Filter - gateway has error\"}");
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 提取jwt token中的数据,获取用户id
|
|
|
|
+ *
|
|
|
|
+ * @param authentication
|
|
|
|
+ * @param request
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private Triple<Boolean, String, String> getUserIdFromToken(String authentication,
|
|
|
|
+ HttpServletRequest request) {
|
|
|
|
+ try {
|
|
|
|
+ final Claims claims = this.getJwt(authentication);
|
|
|
|
+ // claims.getExpiration();
|
|
|
|
+ // checkt expiration
|
|
|
|
+ boolean pass = DateUtil.compare(DateUtil.date(),claims.getExpiration()) < 0;
|
|
|
|
+// boolean pass = validateLogin(request);
|
|
|
|
+ if (pass){
|
|
|
|
+ String userId = claims.get("userId").toString();
|
|
|
|
+ String operatorId = claims.get("operatorId").toString();
|
|
|
|
+ return new ImmutableTriple<Boolean, String, String>(true, userId , operatorId);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ log.error("get userId from token error:{}", e.getMessage());
|
|
|
|
+ }
|
|
|
|
+ return new ImmutableTriple<Boolean, String, String>(false,null,null);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private static Claims getJwt(String jwtToken) {
|
|
|
|
+ if (jwtToken.startsWith(BEARER)) {
|
|
|
|
+ jwtToken = StringUtils.substring(jwtToken, BEARER.length());
|
|
|
|
+ }
|
|
|
|
+ Claims claims;
|
|
|
|
+ try {
|
|
|
|
+ claims = Jwts.parser() //得到DefaultJwtParser
|
|
|
|
+ .setSigningKey(signingKey.getBytes()) //设置签名的秘钥
|
|
|
|
+ .parseClaimsJws(jwtToken).getBody();
|
|
|
|
+ } catch(ExpiredJwtException e) {
|
|
|
|
+ claims = e.getClaims();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return claims;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
+ System.out.printf(getJwt("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzYXdhIiwiZXhwIjoxNjgxMzY2NTg4LCJ1c2VySWQiOjEyLCJvcGVyYXRvcklkIjo5LCJpYXQiOjE2Nzg3NzQ1ODgsImp0aSI6ImQ1ZjM3MmQ4LWM0YzEtNGZkMy05NmNlLTg3MzEyOWM3MDZjNSJ9.XP4ukA_VuOka_Siwe_DRbaYb1jh5TT4XFdz9TvEUBRs")
|
|
|
|
+ .get("userId").toString());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 检验入参(命中否决)
|
|
|
|
+ *
|
|
|
|
+ * @param i
|
|
|
|
+ * @param request
|
|
|
|
+ * @return 返回false 为命中 返回true 为未命中
|
|
|
|
+ */
|
|
|
|
+ private boolean validateParameterAuthorize(InterfacesDTO i, HttpServletRequest request) {
|
|
|
|
+// if (i == null) {
|
|
|
|
+// return true;
|
|
|
|
+// }
|
|
|
|
+// List<InterfacePmtDTO> pmts = i.getInPmts();
|
|
|
|
+// if (pmts == null || pmts.isEmpty()) {
|
|
|
|
+// return true;
|
|
|
|
+// }
|
|
|
|
+// InterfacePmt jsonPmt = null;
|
|
|
|
+// for (InterfacePmt interfacePmt : pmts) {
|
|
|
|
+// if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
|
|
|
|
+// if (interfacePmt.getDataType() == 5) {
|
|
|
|
+// jsonPmt = interfacePmt;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// if (jsonPmt == null) {
|
|
|
|
+// for (InterfacePmt interfacePmt : pmts) {
|
|
|
|
+// if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
|
|
|
|
+// if (StringUtils.equals(interfacePmt.getIsnull(), "1")) {
|
|
|
|
+// if (StringUtils.isBlank(request.getParameter(interfacePmt.getParam()))) {
|
|
|
|
+// log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
|
|
|
|
+// request.setAttribute("errorMsg", interfacePmt.getParam());
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// } else {
|
|
|
|
+// JSONObject json = null;
|
|
|
|
+// try {
|
|
|
|
+// json = JSONObject.parseObject(request.getParameter(jsonPmt.getParam()));
|
|
|
|
+// } catch (Exception e) {
|
|
|
|
+// log.error("接口入参验证失败,json解析失败e:{}",e);
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// for (InterfacePmt interfacePmt : pmts) {
|
|
|
|
+// if (interfacePmt.getIsEnable() != null && interfacePmt.getIsEnable() == 0) {
|
|
|
|
+// if (StringUtils.equals(interfacePmt.getIsnull(), "1")) {
|
|
|
|
+// if (StringUtils.startsWith(interfacePmt.getParam(),jsonPmt.getParam()+".")) {
|
|
|
|
+// String pmtName = interfacePmt.getParam().substring(jsonPmt.getParam().length()+1, interfacePmt.getParam().length());
|
|
|
|
+// if (json.get(pmtName) == null) {
|
|
|
|
+// log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
|
|
|
|
+// request.setAttribute("errorMsg", interfacePmt.getParam());
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// } else {
|
|
|
|
+// if (StringUtils.isBlank(request.getParameter(interfacePmt.getParam()))) {
|
|
|
|
+// log.info("接口入参验证失败:"+interfacePmt.getParam()+"不能为空");
|
|
|
|
+// request.setAttribute("errorMsg", interfacePmt.getParam());
|
|
|
|
+// return false;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean validateMerchantAuthorize(Integer id, String merchantCode) {
|
|
|
|
+// Result findByCode = commonClient.findByCode(id, merchantCode);
|
|
|
|
+// if (StringUtils.equals("200", findByCode.getCode())) {
|
|
|
|
+// if (findByCode.getData()!= null) {
|
|
|
|
+// return true;
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 一个简单的登录认证
|
|
|
|
+ */
|
|
|
|
+ private boolean validateLogin(HttpServletRequest request) {
|
|
|
|
+ String token = request.getHeader("token");
|
|
|
|
+ if (StringUtils.isBlank(token)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ UserDTO booleanResult = userDubboServiceClient.validateLogin(token);
|
|
|
|
+ return booleanResult != null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 一个简单的签名认证,规则: 1. 将请求参数按ascii码排序 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
|
|
|
|
+ * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
|
|
|
|
+ */
|
|
|
|
+ private boolean validateSign(HttpServletRequest request, String secret) {
|
|
|
|
+ String requestSign = request.getParameter("sign");// 获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
|
|
|
|
+ if (StringUtils.isEmpty(requestSign)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
|
|
|
|
+ keys.remove("sign");// 排除sign参数
|
|
|
|
+ Collections.sort(keys);// 排序
|
|
|
|
+
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
+ for (String key : keys) {
|
|
|
|
+ sb.append(key).append("=").append(request.getParameter(key)).append("&");// 拼接字符串
|
|
|
|
+ }
|
|
|
|
+ String linkString = sb.toString();
|
|
|
|
+ linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);// 去除最后一个'&'
|
|
|
|
+
|
|
|
|
+ String sign = DigestUtils.md5Hex(linkString + secret + pubSecret);// 混合密钥md5
|
|
|
|
+
|
|
|
|
+ return StringUtils.equals(sign, requestSign);// 比较
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getIpAddress(HttpServletRequest request) {
|
|
|
|
+ String ip = request.getHeader("x-forwarded-for");
|
|
|
|
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
+ ip = request.getHeader("Proxy-Client-IP");
|
|
|
|
+ }
|
|
|
|
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
+ ip = request.getHeader("WL-Proxy-Client-IP");
|
|
|
|
+ }
|
|
|
|
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
+ ip = request.getHeader("HTTP_CLIENT_IP");
|
|
|
|
+ }
|
|
|
|
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
|
|
|
+ }
|
|
|
|
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
|
|
+ ip = request.getRemoteAddr();
|
|
|
|
+ }
|
|
|
|
+ // 如果是多级代理,那么取第一个ip为客户端ip
|
|
|
|
+ if (ip != null && ip.indexOf(",") != -1) {
|
|
|
|
+ ip = ip.substring(0, ip.indexOf(",")).trim();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ip;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|