微服务网关统一灵活动态鉴权url方案

做微服务时候会有一个网关的概念,网关的作用就是:

  • 统一请求鉴权
  • 请求日志记录
  • 请求路由分发

这里我们来讨论第一种解决方案。
比方说我们项目api接口层里大大小小数百个接口,也就是会有数百个请求路径(url),
其中有些不需要登录就能访问有些需要登录来访问,传统单体应用我们怎么做?

  • 如果用shiro的话我们会配置不用鉴权的接口用“anon”声明。
  • 用token方式的话我们会用aop写个注解放在需要鉴权的接口上,然后利用aop的切面或者配合拦截器,过滤器来做。

但是如果放到微服务层面引入网关的概念,所以鉴权这一步我们不应该放在原来的api服务里面去做了,应该在网关层就开始潘全请求接口是否要鉴权。通常做法有几种:

  • 我们在网关的过滤器里写死个hashmap,然后加载类的时候将要鉴权的路径(url)put进去,然后请求到网关层直接拿hashmap去判断
  • 我们将需要鉴权的路径(url)写在配置文件里,然后通过注入方式放在网关过去器的私有变量里,然后请求到网关层直接拿变量去判断
  • 我们将需要鉴权的路径(url)写在配置中心里,然后通过注入方式放在网关过去器的私有变量里,然后请求到网关层直接拿变量去判断
  • 我们定义一个启动项目时就能运行的方法,把所有注解了需要鉴权的接口路径持久化到DB或缓存里,然后请求到网关层直接拿DB或缓存去判断

他们各自优缺点是啥呢?

  • 写个demo适用,快速方便,但是毕竟一个正常项目少则几十个接口多则上百个,硬编码方式写在网关过滤器里实在不优雅。
  • 消除了硬编码的问题,但是我们需要人肉维护配置文件,修改路径还要重启服务,耦合度高
  • 通过配置中心热加载修改做到实时更新,虽说不用重启服务,但是对于大量的接口路径(url)依然要人肉维护
  • 无需人肉维护,只要在需要鉴权的接口上加上一个自定义的注解,比方说@CheckToken,然后每次项目启动时候就会把这些接口对应的路径(url)持久化到数据库,无论接口是加了,减了,还是修改了,全自动扫描持久化,方便。

至此我们也发现了,我们想要的目的是无论有多少接口我们不想人肉去加加减减,我们只想能自动扫描存起来,然后在网关里判断这次的请求是否需要鉴权。
这里展示下如何在项目启动时候把需要鉴权的接口持久化到缓存里(为什么不放DB,因为缓存更合适):

/**
 *  服务启动扫描@CheckToken的接口,并将其路径持久化到redis
 * @author zhanghang
 * @date 2019/4/30
 */
@Component
public class InitRunner implements ApplicationRunner {

    @Autowired
    private WebApplicationContext applicationContext;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void run(ApplicationArguments args) {
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map map = mapping.getHandlerMethods();
        String checkTokenPath = "com.xxxx.base.annotation.CheckToken";
        Annotation[] annotationArr;
        Set path;
        List list;
        for (Map.Entry e : map.entrySet()){
            annotationArr = e.getValue().getMethod().getDeclaredAnnotations();
            for (Annotation a : annotationArr){
                if (checkTokenPath.equals(a.annotationType().getName())){
                    path = e.getKey().getPatternsCondition().getPatterns();
                    list = new ArrayList<>(path);
                    redisTemplate.opsForSet().add("tokenPaths",list.get(0));
                    break;
                }
            }
        }
        //这一块代码是临时测试,真正的需要放在网关的过滤器里面去判断
        String requestPath1 = "/getUserInfo";
        Boolean hasToken1 = redisTemplate.opsForSet().isMember("tokenPaths",requestPath1);
        System.out.println(hasToken1);//true
        String requestPath2 = "/getUser";
        Boolean hasToken2 = redisTemplate.opsForSet().isMember("tokenPaths",requestPath2);
        System.out.println(hasToken2);//false
    }
}

通过redis的set数据接口我们能很方便的判断出,当前请求的路径是否需要鉴权,返回true,说明在redis中找到了该url,然后从请求的header中取出token,解析token然后做相应的判断即可。

你可能感兴趣的:(spring-boot)