报错信息:
java.lang.IllegalArgumentException: a header value contains a prohibited character '\f': Bearer error="unauthorized", error_description="&÷áo8÷Tû¡X"
at io.netty.handler.codec.http.DefaultHttpHeadersHeaderValueConverterAndValidator.validateValueChar(DefaultHttpHeaders.java:477)atio.netty.handler.codec.http.DefaultHttpHeadersHeaderValueConverterAndValidator.convertObject(DefaultHttpHeaders.java:453)
at io.netty.handler.codec.http.DefaultHttpHeadersHeaderValueConverterAndValidator.convertObject(DefaultHttpHeaders.java:444)atio.netty.handler.codec.DefaultHeaders.addObject(DefaultHeaders.java:327)atio.netty.handler.codec.http.DefaultHttpHeaders.add(DefaultHttpHeaders.java:135)atio.netty.handler.codec.http.HttpObjectDecoder.readHeaders(HttpObjectDecoder.java:610)atio.netty.handler.codec.http.HttpObjectDecoder.decode(HttpObjectDecoder.java:257)atio.netty.handler.codec.http.HttpClientCodecDecoder.decode(HttpClientCodec.java:225)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipelineHeadContext.channelRead(DefaultChannelPipeline.java:1410)atio.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)atio.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)atio.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)atio.netty.channel.nio.AbstractNioByteChannelNioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor4.run(SingleThreadEventExecutor.java:989)atio.netty.util.internal.ThreadExecutorMap2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:834)
排查现场:
在loadUserByUsername方法中编写以下代码
throw new UserNotFoundException("用户信息出错,请联系");
1、Postman访问gateway网关路由到该服务, 直接报以上的异常信息
a header value contains a prohibited character '\f': Bearer error="unauthorized", error_description="&÷áo8÷Tû¡X"
2、Postman单独访问该服务, 无法返回响应体
奇怪的是,异常报文是“用户名或密码错误”是正常的,并没有中文乱码
查看了很多源码,发现
其实两个现场都是一个错误导致,在OAuth2中自定义的异常的处理逻辑
其中有段代码写到:
if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
}
其中e.getSummary()内的错误信息 如果是 【中文】,则响应体出现问题
由于我自定义了异常继承了AuthenticationException异常,导致会执行上面的代码逻辑
在 WWW-Authenticate 添加 【error="unauthorized", error_description="用户信息出错,请联系"】
会出现了中文乱码导致第1和第2的错误
初步解决方案:
将代码修改为以下,核心是 使用 base64加密传输,避免中文:
if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
String encoded = Base64.getEncoder().encodeToString(e.getSummary().getBytes(StandardCharsets.UTF_8));
headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, encoded));
}
总结:
这个问题很隐蔽,查了很久 才找到 WWW-Authenticate 的中文值的问题