企业级项目开发实践——登陆用户信息获取逻辑分析及实现

需求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 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自动装配也是一种优雅的写法。

你可能感兴趣的:(企业级开发项目分析实现,java,开发语言)