问题和场景
默认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() {
}
}
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论