背景
将zuul网关作为认证入口,但是下游服务需要用户id,此时有两种解决方案。
1:将token 放入header中,在每次需要的时候去解析。(同时也是缺点,浪费资源)
2:在网关认证完成后,会获取到用户信息,将用户id放入参数中,若是本身也有userid,可以修改掉userid,同时也防止了登录用户获取到或修改其他用户的系统信息。(优点多,缺点有可能需要修改认证逻辑)
所以我们采用第二种(我设计的项目就是用的这种方案)
代码
注意,Get请求和post请求,分别修改的是url参数和body参数,所以两种处理方案是不一样的。以下分别为处理url参数和body参数的方案。
import com.alibaba.fastjson.JSON;import com.boya.common.util.RequestUtils;import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContext;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import static com.netflix.zuul.context.RequestContext.getCurrentContext;@Component@Slf4jpublic class GetRequestFilter extends ZuulFilter {//定义filter的类型,有pre、route、post、error四种@Overridepublic String filterType() {return "route";}//定义filter的顺序,数字越小表示顺序越高,越先执行@Overridepublic int filterOrder() {return 6;}//表示是否需要执行该filter,true表示执行,false表示不执行@Overridepublic boolean shouldFilter() {HttpServletRequest req = RequestContext.getCurrentContext().getRequest();if (StringUtils.equalsIgnoreCase(req.getMethod(), "get")) {log.info("Get 请求的url : " + req.getRequestURI().toString());return true;}return false;}public Object run() {SecurityContext context = SecurityContextHolder.getContext();Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String currentPrincipalName = authentication.getName();RequestContext ctx = getCurrentContext();HttpServletRequest request = ctx.getRequest();if (log.isDebugEnabled()) {log.debug(String.format("当前用户: %s", currentPrincipalName));log.debug(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));}Map<String, String[]> requestParameterMap = request.getParameterMap();if (log.isDebugEnabled()) {log.debug(String.format("header 参数 >>> %s", JSON.toJSONString(RequestUtils.getHeaders(request))));log.debug(String.format("request 参数 >>> %s", JSON.toJSONString(requestParameterMap)));}Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();if (requestQueryParams == null) {requestQueryParams = new HashMap<>();}//将要新增的参数添加进去,被调用的微服务可以直接 去取,就想普通的一样,框架会直接注入进去ArrayList<String> arrayList = new ArrayList<>();arrayList.add(currentPrincipalName);requestQueryParams.put("userId", arrayList);ctx.setRequestQueryParams(requestQueryParams);return null;}}
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.http.ServletInputStreamWrapper;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContext;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.stereotype.Component;import org.springframework.util.StreamUtils;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.IOException;import java.io.InputStream;import java.nio.charset.Charset;import java.util.*;import java.util.stream.Collectors;import static com.netflix.zuul.context.RequestContext.getCurrentContext;@Component@Slf4jpublic class BodyParamerFilter extends ZuulFilter {static String principalParamName = "userId";//定义filter的类型,有pre、route、post、error四种@Overridepublic String filterType() {return "route";}//定义filter的顺序,数字越小表示顺序越高,越先执行@Overridepublic int filterOrder() {return 7;}//表示是否需要执行该filter,true表示执行,false表示不执行@Overridepublic boolean shouldFilter() {HttpServletRequest req = RequestContext.getCurrentContext().getRequest();if (StringUtils.equalsIgnoreCase(req.getMethod(), "post") || StringUtils.equalsIgnoreCase(req.getMethod(), "post")) {log.info("非Get请求的url = " + req.getRequestURI().toString());return true;}return false;}public Object run() {SecurityContext securityContext = SecurityContextHolder.getContext();Authentication authentication = SecurityContextHolder.getContext().getAuthentication();String currentPrincipalName = authentication.getName();RequestContext context = getCurrentContext();HttpServletRequest request = context.getRequest();if (log.isDebugEnabled()) {log.debug(String.format("当前用户: %s", currentPrincipalName));log.debug(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));}InputStream in = (InputStream) context.get("requestEntity");if (in == null) {try {in = context.getRequest().getInputStream();} catch (IOException e) {e.printStackTrace();}}log.info("accessToken:" + currentPrincipalName);log.info("params:" + context.getRequestQueryParams());log.info("contentLength:" + context.getRequest().getContentLength());log.info("contentType:" + context.getRequest().getContentType());Map<String, List<String>> requestQueryParams = context.getRequestQueryParams();if (requestQueryParams == null) {requestQueryParams = new HashMap<>();} else {requestQueryParams.remove(principalParamName);}if (!"anonymousUser".equalsIgnoreCase(currentPrincipalName)) {//将要新增的参数添加进去,被调用的微服务可以直接 去取,就想普通的一样,框架会直接注入进去ArrayList<String> arrayList = new ArrayList<>();arrayList.add(currentPrincipalName);requestQueryParams.put(principalParamName, arrayList);context.setRequestQueryParams(requestQueryParams);}String contentType = request.getContentType();String body = null;try {body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));} catch (IOException e) {e.printStackTrace();}body = bodyParamModify(body, currentPrincipalName, contentType);// context.set("requestEntity", new// ByteArrayInputStream(body.getBytes("UTF-8")));final byte[] reqBodyBytes = body.getBytes();context.setRequest(new HttpServletRequestWrapper(getCurrentContext().getRequest()) {@Overridepublic ServletInputStream getInputStream() throws IOException {return new ServletInputStreamWrapper(reqBodyBytes);}@Overridepublic int getContentLength() {return reqBodyBytes.length;}@Overridepublic long getContentLengthLong() {return reqBodyBytes.length;}});return null;}private String bodyParamModify(String body, String principalName, String contentType) {log.info("原始 body : " + body);if (body == null) {return principalParamName + "=" + principalName;}if (contentType.contains("application/json")) {JSONObject json = JSON.parseObject(body);json.remove(principalParamName);json.put(principalParamName, principalName);body = json.toJSONString();} else if (contentType.contains("x-www-form-urlencoded")) {body = Arrays.stream(body.split("&")).filter(s -> !s.startsWith(principalParamName)).collect(Collectors.joining("&"));if (!StringUtils.equals(principalName, "anonymousUser")) {body += "&" + principalParamName + "=" + principalName;}}log.info("转换后的body : " + body);return body;}}
