需求1:
1、实现对登陆用户身份信息的获取
2、每个微服务都需要获取这个请求头信息,如何实现?
需求分析:
- 在每个微服务都编写一个SpringMVC的拦截器:HandlerInterceptor
- 在拦截器中获取请求头中的authorization信息,也就是userId,并保存到ThreadLocal中
- 在后续的业务中,可以直接从ThreadLocal中获取userId
思路实现分析:
解决线程安全的问题:
1. 加锁:让多线程在同一时刻变成单线程运行
2.ThreadLocal:实现线程不共享,将用户信息存到ThreadLocal中
1、定义一个工具类,把ThreadLocal封装成一个静态方法
//ThreadLocal的工具类,封装了set、get、remove方法
public class UserHolder {
//静态常量----把tl暴露出去
private static final ThreadLocal<Long> tl = new ThreadLocal<>();
//静态调取,别人调用时就不需要new对象了
public static void setUser (Long userId){
tl.set(userId);
}
//在当前线程中,get不需要传key,谁执行这行代码在线程中取就行
public static Long getUser(){
return tl.get();
}
//清理线程中数据信息,避免哦造成内存泄漏
public static void removeUser(){
tl.remove();
}
}
2、定义一个拦截器,拦截请求——interceports.UserInterceptor
//登陆拦截器——-要想让他生效,需要注册到SpringMVC中
//实现一个接口
public class UserInterceptor implements HandlerInterceptor {
//前置拦截:登陆用户获取
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1、获取请求头
String authorization = request.getHeader("authorization");
//判断
if(StringUtils.isBlank(authorization)){
//没有用户信息,未登录
//throw new RuntimeException("用户未登陆");
//或者——未登陆,抛个403
response.setStatus(403);
return false;
}
//转换用户id
Long userId = Long.valueOf(authorization);
//2、存入ThreadLocal
UserHolder.setUser(userId);
//3、放行
return true;
}
//后置拦截
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
//最终拦截-----最后,调个方法,做个清理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
3、拦截器定义完毕,需要让拦截器生效——注册到Spring MVC中去。
//SprintMVC
@Configuration
public class MvcConfig implements WebMvcConfigurer {
//添加拦截器——我们自己定义的拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInterceptor()).addPathPatterns("/**");
}
}
需求2:
结合上述案例,我们的访问请求是经过网关gateway要带上头信息访问别的微服务即便服务中做了拦截也没问题,当微服务之间调用时,就不会带上请求头信息,就会被上述拦截器拦截,而Feign的调用没有经过网关,所以不会有登陆用户这个头信息。
因此在实际开发中,我们需要给所有由Feign发起的请求都添加请求头。
提示:可以基于Feign的拦截器来实现,参考文档:Feign的拦截器RequestInterceptor - 腾讯云开发者社区-腾讯云
1、在Feign中暴露某一个服务接口——UserClient
@FeignClient(value = "userservice")
public interface UserClient {
@GetMapping("/address/{id}")
//Address为用户地址的实体类
Address findAddressById(@PathVariable("id") Long id);
}
2、定义一个MyFeignInterceptor的feign拦截器
/**
* 定义一个feign拦截器:
* 作用:
* 给所有有feign发起的请求都加一个请求头信息,那么我们测试的由order向service发送的请求就都带上了请求头信息
* 现在是写在的具体的某一个服务中。
*
*/
@Slf4j
@Component
public class MyFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("authorization", "2");
}
}
3、还有其他的方式可以实现该业务
利用spring自动装配也是一种优雅的写法。
本文含有隐藏内容,请 开通VIP 后查看