微服務網關統一靈活動態鑑權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<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然後做相應的判斷即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章