问题描述:
在做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) { // 重新构建新的request ServerHttpRequest 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 { @Override public 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; } @Override public 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) { // 重新构建新的request ServerHttpRequest 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 { @Override public 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; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 清理threadLocal,防止内存泄漏 UserContext.clear(); } }
-
重新启动微服务,调用相关接口
通过截图可以看到,乱码问题成功得到解决