Gateway网关设置请求头乱码

问题描述:

​ 在做gateway用户认证过程中,我们将前端传过来的token字符串进行解析以后,将用户信息存入请求头往下传递的过程中,如果用户信息中存在中文,下游服务从请求头中获取到用户信息时会出现乱码

​ 总体来说,就是如果在gateway网关层设置了带有中文的请求头,在下游服务中获取会出现乱码

代码演示:

  1. 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();
    }
    
  2. 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();
        }
    }
    
  3. 启动微服务,调用相关接口

    image-20240805105737916

    从截图可以看到,下游服务在从请求头获取带有中文的信息时,出现了乱码

解决方案:

​ 可以在gateway设置新的请求头时进行URL编码,在下游获取请求头信息的时候,再通过URL解码。可以解决这个问题

代码演示:

  1. 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();
    }
    
  2. 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();
        }
    }
    
  3. 重新启动微服务,调用相关接口

    image-20240805110452046

    通过截图可以看到,乱码问题成功得到解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值