背景
将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
@Slf4j
public class GetRequestFilter extends ZuulFilter {
//定义filter的类型,有pre、route、post、error四种
@Override
public String filterType() {
return "route";
}
//定义filter的顺序,数字越小表示顺序越高,越先执行
@Override
public int filterOrder() {
return 6;
}
//表示是否需要执行该filter,true表示执行,false表示不执行
@Override
public 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
@Slf4j
public class BodyParamerFilter extends ZuulFilter {
static String principalParamName = "userId";
//定义filter的类型,有pre、route、post、error四种
@Override
public String filterType() {
return "route";
}
//定义filter的顺序,数字越小表示顺序越高,越先执行
@Override
public int filterOrder() {
return 7;
}
//表示是否需要执行该filter,true表示执行,false表示不执行
@Override
public 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()) {
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(reqBodyBytes);
}
@Override
public int getContentLength() {
return reqBodyBytes.length;
}
@Override
public 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;
}
}