关于feign:107FeignClient调用携带登录用户信息

9次阅读

共计 4238 个字符,预计需要花费 11 分钟才能阅读完成。

定义 ContextHolder

ContextHolder 用来寄存和获取线程变量中的 用户 id、用户名称、Token 等信息。

/**
 * 获取以后线程变量中的 用户 id、用户名称、Token 等信息
 * 留神:必须在网关通过申请头的办法传入,同时在 Interceptor 拦截器设置值。否则这里无奈获取
 *
 *
 */
public class SecurityContextHolder
{private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();

    public static void set(String key, Object value)
    {Map<String, Object> map = getLocalMap();
        map.put(key, value == null ? StringUtils.EMPTY : value);
    }

    public static String get(String key)
    {Map<String, Object> map = getLocalMap();
        return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY));
    }

    public static <T> T get(String key, Class<T> clazz)
    {Map<String, Object> map = getLocalMap();
        return StringUtils.cast(map.getOrDefault(key, null));
    }

    public static Map<String, Object> getLocalMap()
    {Map<String, Object> map = THREAD_LOCAL.get();
        if (map == null)
        {map = new ConcurrentHashMap<String, Object>();
            THREAD_LOCAL.set(map);
        }
        return map;
    }

    public static void setLocalMap(Map<String, Object> threadLocalMap)
    {THREAD_LOCAL.set(threadLocalMap);
    }

    public static String  getUserId()
    {return  get(SecurityConstants.DETAILS_USER_ID);
    }

    public static void setUserId(String account)
    {set(SecurityConstants.DETAILS_USER_ID, account);
    }

    public static String getUserName()
    {return get(SecurityConstants.DETAILS_USERNAME);
    }

    public static void setUserName(String username)
    {set(SecurityConstants.DETAILS_USERNAME, username);
    }

    public static String getUserKey()
    {return get(SecurityConstants.USER_KEY);
    }

    public static void setUserKey(String userKey)
    {set(SecurityConstants.USER_KEY, userKey);
    }

    public static void remove()
    {THREAD_LOCAL.remove();
    }

    public static void setUserType(String userType) {set(SecurityConstants.USER_TYPE, userType);
    }

    public static String getUserType()
    {return get(SecurityConstants.USER_TYPE);
    }
}

利用模块的拦截器寄存登录信息

@Component
public class SjcjInterceptor implements HandlerInterceptor {
    @Autowired
    private RemoteUserService remoteUserService;
    @Autowired
    private TokenService tokenService;

    // 超管的 id=1
    @Value("${admin.userId:1}")
    private String adminUserId;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceof HandlerMethod)) {return true;}
        String userId = SecurityContextHolder.getUserId();
        if(StringUtils.isEmpty(userId)){
            userId = adminUserId;
            R<LoginUser> userResult = remoteUserService.getUserIdInfo(userId, SecurityConstants.INNER);

            if (R.FAIL == userResult.getCode())
            {throw new ServiceException(userResult.getMsg());
            }
            LoginUser userInfo = userResult.getData();
            Map<String, Object> rspMap = tokenService.createToken(userInfo, 1);

            SecurityContextHolder.setUserId(userInfo.getUserid());
            SecurityContextHolder.setUserName(userInfo.getUsername());
            SecurityContextHolder.set(SecurityConstants.LOGIN_USER, userInfo);
            SecurityContextHolder.set(SecurityConstants.AUTHORIZATION_HEADER,rspMap.get("access_token"));
        }

        return true;
    }
    
}

feign 申请拦截器

该拦截器从申请 requst 取用户信息,或者从 SecurityContextHolder 取登录信息,并保留到 RequestTemplate。

/**
 * feign 申请拦截器
 *
 *
 */
@Component
public class FeignRequestInterceptor implements RequestInterceptor
{
    @Override
    public void apply(RequestTemplate requestTemplate)
    {HttpServletRequest httpServletRequest = ServletUtils.getRequest();
        if (StringUtils.isNotNull(httpServletRequest))
        {Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest);
            // 传递用户信息申请头,避免失落
            String userId = headers.get(SecurityConstants.DETAILS_USER_ID);
            //cjq 2022/10/12
            // 从 request 没取到,则从 SecurityContextHolder 从取
            // 适应于无前端的状况,在接口调用前,把用户信息放到 SecurityContextHolder
            if(StringUtils.isEmpty(userId)){userId = SecurityContextHolder.get(SecurityConstants.DETAILS_USER_ID);
            }
            if (StringUtils.isNotEmpty(userId))
            {requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId);
            }
            String userName = headers.get(SecurityConstants.DETAILS_USERNAME);
            if (StringUtils.isEmpty(userName)){userName = SecurityContextHolder.get(SecurityConstants.DETAILS_USERNAME);
            }
            if (StringUtils.isNotEmpty(userName))
            {requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName);
            }
            String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER);
            if (StringUtils.isNotEmpty(authentication)){authentication = SecurityContextHolder.get(SecurityConstants.AUTHORIZATION_HEADER);
            }
            if (StringUtils.isNotEmpty(authentication))
            {requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication);
            }

            // 配置客户端 IP
            requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr(ServletUtils.getRequest()));
        }
    }
}

模块间调用服务 api,就能够取到设置的用户登录信息了。

能够应用 SecurityContextHolder 获取相干的信息了。

正文完
 0