问题和场景
默认http传输不能中途修改http内容, 但是有很多时候都需要去修改请求头信息来达到某些目的,比如我这个情况:
我需要去修改Authorization请求头来替换token,我在网上看了好多博客都是HttpServletRequest类的或者是想用反射解决,但是我这里(ServerHttpRequest)并不适用,于是我去求助大佬后得出2个解决方案,并且ServerHttpRequest和HttpServletRequest都适用。
目录(解决方案自己根据需求去查看,觉得有用点个赞哦)
如果直接set值会有如下问题,java.lang.UnsupportedOperationException:null;
at org.springframework.http.ReadOnlyHttpHeaders.set:
解决(ServerHttpRequest)
方案一 直接开放权限
这个方法主要是为了兼容以前的代码,因为这个header到其他方法里面还会改 所以我改了header对象
//设置为可修改的
headers= HttpHeaders.writableHttpHeaders(headers);
//设置请求头
headers.set(HttpHeaders.AUTHORIZATION,authorization);
方案二 去修改header
这个是等于重新build对象 ,貌似更方便
exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION,authorization).build();
上面2种方法都可以实现这个效果
修改请求前:
修改请求后:
完整代码如下:
package org.dromara.soul.bootstrap.filter;
import com.alibaba.nacos.client.utils.StringUtils;
import org.dromara.soul.bootstrap.template.RedisStrTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.annotation.Nullable;
import javax.xml.soap.MimeHeaders;
import java.lang.reflect.Field;
import java.util.List;
/**
* @Auther: whhh
* @Date: 2021/4/1 10:46
* @Description: token替换
*/
@Order(-98)
@Component
public class GetTokenFilter implements WebFilter {
@Autowired
private RedisStrTemplate redisStrTemplate;
@Override
public Mono<Void> filter(@Nullable final ServerWebExchange exchange, @Nullable final WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
//判断是否包含认证头
if (request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
HttpHeaders headers = request.getHeaders();
//获取认证集合
List<String> keys = headers.get(HttpHeaders.AUTHORIZATION);
if (keys != null) {
//多个认证取第一个不为空的
for (String token : keys) {
if (token != null && !token.equals("")) {
//从Redis获取token
String a = (String) redisStrTemplate.get(token);
String authorization = a.substring(1,a.length()-1);//
//方法一 设置为可修改的
headers= HttpHeaders.writableHttpHeaders(headers);
//设置请求头
headers.set(HttpHeaders.AUTHORIZATION,authorization);
//方法二 bulid
// exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION,authorization).build();
if (request.getMethod() == HttpMethod.OPTIONS) {
exchange.getResponse().setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
}
}
}
return chain.filter(exchange);
}
}
PS:我这个filter是soul网关(shenyu)的哦,可能chain有区别,你可以参考直接创建一个filter
解决(HttpServletRequest)
你可以自定义个RemoteAddrFilter,然后通过覆盖HttpServletRequestWrapper去替换原有的方法,基于HttpServletRequest 创建SafeboxRequestWrapper对象,然后把SafeboxRequestWrapper对象作为HttpServletRequest传递下去
代码如下:
HeaderMapRequestWrapper类
package org.dromara.soul.bootstrap.filter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* @Auther: whhh
* @Date: 2021/4/26 19:00
* @Description:
*/
public class HeaderMapRequestWrapper extends HttpServletRequestWrapper{
public HeaderMapRequestWrapper(HttpServletRequest request) {
super(request);
}
private Map<String, String> headerMap = new HashMap<String, String>();
/**
* add a header with given name and value
*
* @param name
* @param value
*/
public void addHeader(String name, String value) {
headerMap.put(name, value);
}
@Override
public String getHeader(String name) {
String headerValue = super.getHeader(name);
if (headerMap.containsKey(name)) {
headerValue = headerMap.get(name);
}
return headerValue;
}
/**
* get the Header names
*/
@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
for (String name : headerMap.keySet()) {
names.add(name);
}
return Collections.enumeration(names);
}
@Override
public Enumeration<String> getHeaders(String name) {
List<String> values = Collections.list(super.getHeaders(name));
if (headerMap.containsKey(name)) {
values.add(headerMap.get(name));
}
return Collections.enumeration(values);
}
}
GetTokenFilter类
package org.dromara.soul.bootstrap.filter;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Auther: whhh
* @Date: 2021/4/26 18:58
* @Description:
*/
@Order(-98)
@Component
public class GetTokenFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);
//校验请求request Header中是否有对应值
String authorization = request.getParameter(HttpHeaders.AUTHORIZATION);
if (language !=null && !"".equals(authorization)) {
//如果get请求url中带有这个参数,则request中新增一个header
requestWrapper.addHeader(HttpHeaders.AUTHORIZATION, authorization);
// Goes to default servlet.
chain.doFilter(requestWrapper, response);
}
// Goes to default servlet.
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}