一.如下几种方法不行
- 使用HttpServletRequest对象的setAttribute()方法在zuul设置,在下面的微服务用getAttribute()获取。是否可行?
不行,可能zuul和微服务中的request对象存在他们各自tomcat容器中,在微服务重新创建request对象时不会重新设置attr里面的信息? - 使用setHeader,getHeader是否可行?
不行 - 使用ServletInputStreamWrapper(userData),request.getInputStream()是否可行?
部分post请求不行。 - 使用zuulRequestHeader
可行
ZuulFilter里面使用:
RequestContext context = RequestContext.getCurrentContext();
context.addZuulRequestHeader(“key”, “value”);
后面微服务:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userJson = request.getHeader(“key”);
使用setHeader,getHeader不行,代码如下:
此方法在请求头设置用户信息并不能被后面微服务收到:
package com.mars.cloud.zuul.service.filter;
import cn.hutool.json.JSONUtil;
import com.mars.cloud.auth.model.bean.UserDetailsImpl;
import com.mars.cloud.user.model.dto.RequestUserDto;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 15:49 <br>
* @Author: <br>
* @Description: 网关过滤器,在认证完成后,调微服务之前,在请求头设置用户信息
*/
@Slf4j
@Component
public class SetUserDetailFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.ROUTE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
// 获取请求上下文
RequestContext context = RequestContext.getCurrentContext();
// 获取到request
HttpServletRequest request = context.getRequest();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (null != authentication && null != authentication.getPrincipal() && authentication.getPrincipal() instanceof UserDetailsImpl) {
final UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
if (null != user) {
String userName = user.getUserName();
log.info("{}设置用户信息....", userName);
RequestUserDto requestUserDto = new RequestUserDto()
.setUserName(userName)
.setUserId(user.getUserId())
.setEmail(user.getEmail())
.setMobile(user.getMobile())
.setRoleId(user.getRoleId());
MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
mutableRequest.putHeader(RequestUserDto.REQUEST_ATTR_KEY, JSONUtil.toJsonStr(requestUserDto));
context.setRequest(mutableRequest);
}
}
return null;
}
final class MutableHttpServletRequest extends HttpServletRequestWrapper {
// holds custom header and value mapping
private final Map<String, String> customHeaders;
public MutableHttpServletRequest(HttpServletRequest request) {
super(request);
this.customHeaders = new HashMap<String, String>();
}
public void putHeader(String name, String value) {
this.customHeaders.put(name, value);
}
@Override
public String getHeader(String name) {
// check the custom headers first
String headerValue = customHeaders.get(name);
if (headerValue != null) {
return headerValue;
}
// else return from into the original wrapped object
return ((HttpServletRequest) getRequest()).getHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
// create a set of the custom header names
Set<String> set = new HashSet<String>(customHeaders.keySet());
// now add the headers from the wrapped request object
@SuppressWarnings("unchecked")
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
// add the names of the request headers into the list
String n = e.nextElement();
set.add(n);
}
// create an enumeration from the set and return
return Collections.enumeration(set);
}
}
}
二. InputStream部分请求可用(发现post不行)
使用ServletInputStreamWrapper(userData),request.getInputStream():
1)在zuul设置用户信息
package com.mars.cloud.zuul.service.filter;
import cn.hutool.json.JSONUtil;
import com.mars.cloud.auth.model.bean.UserDetailsImpl;
import com.mars.cloud.user.model.dto.RequestUserDto;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 15:49 <br>
* @Author: <br>
* @Description: 网关过滤器,在认证完成后,调微服务之前,在请求头设置用户信息
*/
@Slf4j
@Component
public class SetUserDetailFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.ROUTE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
// 获取请求上下文
RequestContext context = RequestContext.getCurrentContext();
// 获取到request
HttpServletRequest request = context.getRequest();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (null != authentication && null != authentication.getPrincipal() && authentication.getPrincipal() instanceof UserDetailsImpl) {
final UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
if (null != user) {
String userName = user.getUserName();
log.info("{}设置用户信息....", userName);
RequestUserDto requestUserDto = new RequestUserDto()
.setUserName(userName)
.setUserId(user.getUserId())
.setEmail(user.getEmail())
.setMobile(user.getMobile())
.setRoleId(user.getRoleId());
// 这里边一定要使用 key1=value1&key2=value2....key_n=value_n 形式的,要不然你也获取不到
byte[] userData = (RequestUserDto.REQUEST_ATTR_KEY + "=" + JSONUtil.toJsonStr(requestUserDto)).getBytes();
context.setRequest(new HttpServletRequestWrapper(request) {
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStreamWrapper(userData);
}
@Override
public int getContentLength() {
return userData.length;
}
@Override
public long getContentLengthLong() {
return userData.length;
}
});
}
}
return null;
}
}
2)后续微服务接收:使用工具类,方便随时调用
package com.mars.cloud.user.util;
import cn.hutool.json.JSONUtil;
import com.mars.cloud.user.model.dto.RequestUserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 22:04 <br>
* @Author: <br>
* @Description: 用户工具类
*/
@Slf4j
public class UserUtil {
/**
* 获取当前登录用户,前提是请求从网关进入,并在网关设置了用户信息
*
* @return 用户对象,没有返回null
*/
public static RequestUserDto getCurUser() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
RequestUserDto requestUserDto = null;
try (ServletInputStream ris = request.getInputStream()) {
StringBuilder content = new StringBuilder();
byte[] b = new byte[1024];
int lens = -1;
while ((lens = ris.read(b)) > 0) {
content.append(new String(b, 0, lens));
}
String strContent = content.toString();
String userJson = StringUtil.getParam(strContent, RequestUserDto.REQUEST_ATTR_KEY);
requestUserDto = JSONUtil.toBean(userJson, RequestUserDto.class);
} catch (IOException e) {
log.error("UserUtil -> getCurUser,获取用户信息失败");
}
return requestUserDto;
}
}
注:点击跳转从"key1=value1&key2=value2…",根据key获取value的工具类
3)后续微服务接收:使用自定义参数解析器,这样就可以在controller层通过注解就可获取用户信息对象
package com.mars.cloud.user.model.annotation;
import java.lang.annotation.*;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 19:44 <br>
* @Author: <br>
* @Description: 自定义参数解析器注解
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UserAnnotation {
}
package com.mars.cloud.user.handlerResolver;
import com.mars.cloud.user.model.annotation.UserAnnotation;
import com.mars.cloud.user.util.UserUtil;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 19:50 <br>
* @Author: <br>
* @Description: 自定义参数解析器
*/
public class RequestUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(UserAnnotation.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
// 工具类返回用户信息对象
return UserUtil.getCurUser();
}
}
package com.mars.cloud.user.handlerResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @Project: <br>
* @CreateDate: 2021/01/29 19:47 <br>
* @Author: <br>
* @Description: 添加自定义参数解析器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestUserHandlerMethodArgumentResolver());
}
}
controller层使用:
@GetMapping(value = "/v1/obj/certificate")
public APIResponse<Certificate> certificate(@UserAnnotation RequestUserDto CurUser) {
// 直接可以使用CurUser对象
return APIResponseUtils.success(userMqttService.certificate(CurUser));
}