问题描述:
在做gateway用户认证过程中,我们将前端传过来的token字符串进行解析以后,将用户信息存入请求头往下传递的过程中,如果用户信息中存在中文,下游服务从请求头中获取到用户信息时会出现乱码
总体来说,就是如果在gateway网关层设置了带有中文的请求头,在下游服务中获取会出现乱码
代码演示:
-
SecurityFilterJwt.java
private Mono<Void> dealJwt(String jwt, ServerWebExchange exchange, GatewayFilterChain chain, String headerKey) {try {// 获取token中的用户对象字符串String userJson = jwtUtils.checkJWT(jwt);// 将用户信息设置到请求头中ServerWebExchange serverWebExchange = setNewHeader(exchange, headerKey, userJson);return chain.filter(serverWebExchange);} catch (ExpiredJwtException e) {e.printStackTrace();return renderErrorMsg(exchange, ResponseStatusEnum.JWT_EXPIRE_ERROR);} catch (Exception e) {e.printStackTrace();return renderErrorMsg(exchange, ResponseStatusEnum.JWT_SIGNATURE_ERROR);} }public ServerWebExchange setNewHeader(ServerWebExchange exchange, String headerKey, String headerValue) {// 重新构建新的requestServerHttpRequest request = exchange.getRequest().mutate().header(headerKey, headerValue).build();// 将新的request放入exchange中,通过断点调试,上面的request在设置进去时还是好的,问题应该是出现在下面代码return exchange.mutate().request(request).build(); }
-
JwtUserInterceptor.java
package com.ajie.api.interceptor;import cn.hutool.core.net.URLDecoder; import com.ajie.api.context.UserContext; import com.ajie.common.constant.SecurityConstants; import com.ajie.common.utils.JsonUtils; import com.ajie.common.utils.StringUtils; import com.ajie.pojo.entity.Admin; import com.ajie.pojo.entity.Users; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets;/*** @Description:* @Author: ajie*/ @Slf4j @Component public class JwtUserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取上游网关层设置的用户信息String appUserJson = request.getHeader(SecurityConstants.APP_USER_JSON);String saaSUserJson = request.getHeader(SecurityConstants.SAAS_USER_JSON);String adminUserJson = request.getHeader(SecurityConstants.ADMIN_USER_JSON);if (StringUtils.isNotBlank(appUserJson)) {log.warn("从请求头获取到的用户信息:{}", appUserJson);// 将用户信息设置到threadLocal中UserContext.setUsers(JsonUtils.toBean(appUserJson, Users.class));}if (StringUtils.isNotBlank(adminUserJson)) {adminUserJson = URLDecoder.decode(adminUserJson, StandardCharsets.UTF_8);UserContext.setAdmin(JsonUtils.toBean(adminUserJson, Admin.class));}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理threadLocal,防止内存泄漏UserContext.clear();} }
-
启动微服务,调用相关接口
从截图可以看到,下游服务在从请求头获取带有中文的信息时,出现了乱码
解决方案:
可以在gateway设置新的请求头时进行URL编码,在下游获取请求头信息的时候,再通过URL解码。可以解决这个问题
代码演示:
-
SecurityFilterJwt.java
private Mono<Void> dealJwt(String jwt, ServerWebExchange exchange, GatewayFilterChain chain, String headerKey) {try {// 获取token中的用户对象字符串String userJson = jwtUtils.checkJWT(jwt);// 将用户信息设置到请求头中ServerWebExchange serverWebExchange = setNewHeader(exchange, headerKey, userJson);return chain.filter(serverWebExchange);} catch (ExpiredJwtException e) {e.printStackTrace();return renderErrorMsg(exchange, ResponseStatusEnum.JWT_EXPIRE_ERROR);} catch (Exception e) {e.printStackTrace();return renderErrorMsg(exchange, ResponseStatusEnum.JWT_SIGNATURE_ERROR);} }public ServerWebExchange setNewHeader(ServerWebExchange exchange, String headerKey, String headerValue) {// 重新构建新的requestServerHttpRequest request = exchange.getRequest().mutate()// 重点:对请求头的值做URL编码,解决下游获取信息乱码的问题.header(headerKey, URLEncodeUtil.encode(headerValue,StandardCharsets.UTF_8)).build();// 将新的request放入exchange中,通过断点调试,上面的request在设置进去时还是好的,问题应该是出现在下面代码return exchange.mutate().request(request).build(); }
-
JwtUserInterceptor.java
package com.ajie.api.interceptor;import cn.hutool.core.net.URLDecoder; import com.ajie.api.context.UserContext; import com.ajie.common.constant.SecurityConstants; import com.ajie.common.utils.JsonUtils; import com.ajie.common.utils.StringUtils; import com.ajie.pojo.entity.Admin; import com.ajie.pojo.entity.Users; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets;/*** @Description:* @Author: ajie*/ @Slf4j @Component public class JwtUserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取上游网关层设置的用户信息String appUserJson = request.getHeader(SecurityConstants.APP_USER_JSON);String saaSUserJson = request.getHeader(SecurityConstants.SAAS_USER_JSON);String adminUserJson = request.getHeader(SecurityConstants.ADMIN_USER_JSON);if (StringUtils.isNotBlank(appUserJson)) {// 重点:对请求头中的值进行URL解码,解码gateway设置请求头乱码问题appUserJson = URLDecoder.decode(appUserJson, StandardCharsets.UTF_8);log.warn("从请求头获取到的用户信息:{}", appUserJson);// 将用户信息设置到threadLocal中UserContext.setUsers(JsonUtils.toBean(appUserJson, Users.class));}if (StringUtils.isNotBlank(adminUserJson)) {adminUserJson = URLDecoder.decode(adminUserJson, StandardCharsets.UTF_8);UserContext.setAdmin(JsonUtils.toBean(adminUserJson, Admin.class));}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理threadLocal,防止内存泄漏UserContext.clear();} }
-
重新启动微服务,调用相关接口
通过截图可以看到,乱码问题成功得到解决