溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringCloud如何實現(xiàn)微服務(wù)數(shù)據(jù)權(quán)限控制

發(fā)布時間:2021-11-25 09:03:18 來源:億速云 閱讀:270 作者:小新 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細講解有關(guān)SpringCloud如何實現(xiàn)微服務(wù)數(shù)據(jù)權(quán)限控制,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

    舉個例子:

    有一批業(yè)務(wù)員跟進全國的銷售訂單。他們被按城市進行劃分,一個業(yè)務(wù)員跟進3個城市的訂單,為了保護公司的業(yè)務(wù)數(shù)據(jù)不能被所有人都掌握,故每個業(yè)務(wù)員只能看到自己負責(zé)城市的訂單數(shù)據(jù)。所以從系統(tǒng)來講每個業(yè)務(wù)員都有訪問銷售訂單的功能,然后再需要配置每個業(yè)務(wù)員負責(zé)的城市,以此對訂單數(shù)據(jù)進行篩選。

    要實現(xiàn)此功能有很多方法,如果系統(tǒng)中多個地方都需要類似的需求,那我們就可以將其提出來做成一個通用的功能。這里我介紹一個相對簡單的解決方案,以供參考。

    一、 整體架構(gòu)

    SpringCloud如何實現(xiàn)微服務(wù)數(shù)據(jù)權(quán)限控制

    數(shù)據(jù)權(quán)限為作一個注解的形式掛在每一個需要數(shù)據(jù)權(quán)限控制的Controller上,由于和具體的程序邏輯有關(guān)故有一定的入侵性,且需要數(shù)據(jù)庫配合使用。

    二、 實現(xiàn)流程

    SpringCloud如何實現(xiàn)微服務(wù)數(shù)據(jù)權(quán)限控制

    1.瀏覽器傳帶查詢權(quán)限范圍參數(shù)訪問Controller,如cities

    POST http://127.0.0.1:8000/order/query
    accept: */*
    Content-Type: application/json
    token: 1e2b2298-8274-4599-a26f-a799167cc82f
    
    {"cities":["cq","cd","bj"],"userName":"string"}

    2.通過注解攔截權(quán)限范圍參數(shù),并根據(jù)預(yù)授權(quán)范圍比較,回寫在授權(quán)范圍內(nèi)的權(quán)限范圍參數(shù)

    cities = ["cq","cd"]

    3.通過參數(shù)傳遞到DAO層,在SQL語句中拼裝出查詢條件,實現(xiàn)數(shù)據(jù)的過濾

    select * from order where city in ('cq','cd')

    三、 實現(xiàn)步驟

    1. 注解實現(xiàn)

    注解的完整代碼,請詳見源代碼

    1)創(chuàng)建注解

    @Retention(value = RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.METHOD})
    @Documented
    public @interface ScopeAuth {
    
        String token() default "AUTH_TOKEN";
        String scope() default "";
        String[] scopes() default {};
    }

    此注解為運行時RetentionPolicy.RUNTIME作用在方法上ElementType.METHOD

    token:獲取識別唯一用戶的標(biāo)識,與用戶數(shù)據(jù)權(quán)限存儲有關(guān)

    scope,scopes:預(yù)請求的數(shù)據(jù)權(quán)限范圍

    2) AOP實現(xiàn)注解

    public class ScopeAuthAdvice {
      
        @Around("@annotation(scopeAuth)")
        public Object before(ProceedingJoinPoint thisJoinPoint, ScopeAuth scopeAuth) throws Throwable {
            // ... 省略過程
            // 獲取token
            String authToken = getToken(args, scopeAuth.token(), methodSignature.getMethod());
                // 回寫范圍參數(shù)
            setScope(scopeAuth.scope(), methodSignature, args, authToken);
            
            return thisJoinPoint.proceed();
        }
    
        /**
         * 設(shè)置范圍
         */
        private void setScope(String scope, MethodSignature methodSignature, Object[] args, String authToken) {
            // 獲取請求范圍
            Set<String> requestScope = getRequestScope(args, scope, methodSignature.getMethod());
            ScopeAuthAdapter adapter = new ScopeAuthAdapter(supplier);
            // 已授權(quán)范圍
            Set<String> authorizedScope = adapter.identifyPermissionScope(authToken, requestScope);
            // 回寫新范圍
            setRequestScope(args, scope, authorizedScope, methodSignature.getMethod());
        }
    
        /**
         * 回寫請求范圍
         */
        private void setRequestScope(Object[] args, String scopeName, Collection<String> scopeValues, Method method) {
            // 解析 SPEL 表達式
            if (scopeName.indexOf(SPEL_FLAG) == 0) {
                ParseSPEL.setMethodValue(scopeName, scopeValues, method, args);
            }
        }
    }

    此為演示代碼省略了過程,主要功能為通過token拿到預(yù)先授權(quán)的數(shù)據(jù)范圍,再與本次請求的范圍做交集,最后回寫回原參數(shù)。

    過程中用到了較多的SPEL表達式,用于計算表達式結(jié)果,具體請參考ParseSPEL文件

    3)權(quán)限范圍交集計算

    public class ScopeAuthAdapter {
    
        private final AuthQuerySupplier supplier;
    
        public ScopeAuthAdapter(AuthQuerySupplier supplier) {
            this.supplier = supplier;
        }
    
        /**
         * 驗證權(quán)限范圍
         * @param token
         * @param requestScope
         * @return
         */
        public Set<String> identifyPermissionScope(String token, Set<String> requestScope) {
            Set<String> authorizeScope = supplier.queryScope(token);
    
            String ALL_SCOPE = "AUTH_ALL";
            String USER_ALL = "USER_ALL";
    
            if (authorizeScope == null) {
                return null;
            }
    
            if (authorizeScope.contains(ALL_SCOPE)) {
                // 如果是全開放則返回請求范圍
                return requestScope;
            }
    
            if (requestScope == null) {
                return null;
            }
    
            if (requestScope.contains(USER_ALL)){
                // 所有授權(quán)的范圍
                return authorizeScope;
            }
    
            // 移除不同的元素
            requestScope.retainAll(authorizeScope);
    
            return requestScope;
        }
    }

    此處為了方便設(shè)置,有兩個關(guān)鍵字范圍

    • AUTH_ALL:預(yù)設(shè)所有范圍,全開放的意思,為數(shù)據(jù)庫預(yù)先設(shè)置值,請求傳什么值都通過

    • USER_ALL:請求所有授權(quán)的范圍,請求時傳此值則會以數(shù)據(jù)庫預(yù)設(shè)值為準(zhǔn)

    4) spring.factories自動導(dǎo)入類配置

    org.springframework.boot.autoconfigure.AutoConfigurationImportSelector=\
      fun.barryhome.cloud.annotation.ScopeAuthAdvice

    如果注解功能是單獨項目存在,在使用時有可能會存在找不到引入文件的問題,可通過此配置文件自動載入需要初始化的類

    2. 注解使用

    @ScopeAuth(scopes = {"#orderDTO.cities"}, token = "#request.getHeader(\"X-User-Name\")")
    @PostMapping(value = "/query")
    public String query(@RequestBody OrderDTO orderDTO, HttpServletRequest request) {
        return Arrays.toString(orderDTO.getCities());
    }

    在需要使用數(shù)據(jù)權(quán)限的controller方法上增加@ScopeAuth注解

    scopes = {"#orderDTO.cities"}:表示取輸入?yún)?shù)orderDTO的cities值,這里是表達式必須加#

    實際開發(fā)過程中,需要將orderDTO.getCities()帶入后續(xù)邏輯中,在DAO層將此拼裝在SQL中,以實現(xiàn)數(shù)據(jù)過濾功能

    3. 實現(xiàn)AuthStoreSupplier

    AuthStoreSupplier接口為數(shù)據(jù)權(quán)限的存儲接口,與AuthQuerySupplier配合使用,可按實際情況實現(xiàn)

    此接口為非必要接口,可由數(shù)據(jù)庫或Redis存儲(推薦),一般在登錄的同時保存在Redis中

    4. 實現(xiàn)AuthQuerySupplier

    AuthQuerySupplier接口為數(shù)據(jù)權(quán)限查詢接口,可按存儲方法進行查詢,推薦使用Redis

    @Component
    public class RedisAuthQuerySupplier implements AuthQuerySupplier {
    
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
    
        /**
         * 查詢范圍
         */
        @Override
        public Set<String> queryScope(String key) {
            String AUTH_USER_KEY = "auth:logic:user:%s";
            String redisKey = String.format(AUTH_USER_KEY, key);
    
            List<String> range = redisTemplate.opsForList().range(redisKey, 0, -1);
    
            if (range != null) {
                return new HashSet<>(range);
            } else {
                return null;
            }
        }
    }

    在分布式結(jié)構(gòu)里,也可將此實現(xiàn)提出到權(quán)限模塊,采用遠程調(diào)用方式,進一步解耦

    5. 開啟數(shù)據(jù)權(quán)限

    @EnableScopeAuth
    @EnableDiscoveryClient
    @SpringBootApplication
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    }

    關(guān)于“SpringCloud如何實現(xiàn)微服務(wù)數(shù)據(jù)權(quán)限控制”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI