微服务网关统一灵活动态鉴权url方案
做微服务时候会有一个网关的概念,网关的作用就是:统一请求鉴权请求日志记录请求路由分发这里我们来讨论第一种解决方案。比方说我们项目api接口层里大大小小数百个接口,也就是会有数百个请求路径(url),其中有些不需要登录就能访问有些需要登录来访问,传统单体应用我们怎么做?如果用shiro的话我们会配置不用鉴权的接口用“anon”声明。用token方式的话我们会用aop写个注解放在...
·
做微服务时候会有一个网关的概念,网关的作用就是:
- 统一请求鉴权
- 请求日志记录
- 请求路由分发
这里我们来讨论第一种解决方案。
比方说我们项目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<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
String checkTokenPath = "com.xxxx.base.annotation.CheckToken";
Annotation[] annotationArr;
Set<String> path;
List<String> list;
for (Map.Entry<RequestMappingInfo, HandlerMethod> 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然后做相应的判断即可。
更多推荐
已为社区贡献1条内容
所有评论(0)