博客 / 詳情

返回

基於Spring Cloud Gateway + RBAC的用户管理系統:權限認證與全局校驗

引言

在現代微服務架構中,用户管理系統是核心組件之一,負責用户的身份認證和權限管理。隨着系統規模的擴大,如何高效地管理用户權限、確保系統的安全性成為了一個重要挑戰。基於Spring Cloud Gateway和RBAC(Role-Based Access Control,基於角色的訪問控制)的用户管理系統,能夠有效地解決這一問題。本文將介紹如何利用Spring Cloud Gateway實現權限認證與全局校驗,並結合RBAC模型構建一個安全的用户管理系統。

1. Spring Cloud Gateway簡介

Spring Cloud Gateway是Spring Cloud生態系統中的一個API網關,旨在為微服務架構提供簡單、有效的方式來路由請求、進行權限認證、負載均衡等。它基於Spring 6、Spring Boot 3構建,支持異步非阻塞的請求處理,適合高併發的場景。

Spring Cloud Gateway的核心功能包括:

  • 路由:根據請求的路徑、方法、頭信息等將請求路由到不同的微服務。
  • 過濾器:在請求到達目標服務之前或之後執行一些邏輯,如權限校驗、請求日誌記錄等。
  • 負載均衡:通過與Spring Cloud LoadBalancer集成,實現請求的負載均衡。
  • 熔斷器:通過與Spring Cloud CircuitBreaker集成,提供服務的熔斷和降級功能。

2. RBAC(基於角色的訪問控制)模型

RBAC(Role-Based Access Control)是一種常見的權限管理模型,它將權限與角色關聯,用户通過分配角色來獲得相應的權限。RBAC模型的核心概念包括:

  • 用户(User):系統的使用者,可以是個人或應用程序。
  • 角色(Role):一組權限的集合,用户通過分配角色來獲得權限。
  • 權限(Permission):系統中對資源的操作權限,如“讀取”、“寫入”、“刪除”等。

RBAC模型的優勢在於:

  • 靈活性:通過角色的分配和回收,可以靈活地管理用户的權限。
  • 可維護性:權限的管理集中在角色上,減少了直接對用户進行權限管理的複雜性。
  • 安全性:通過角色的層級關係和權限的繼承,可以更好地控制系統的安全性。

3. 基於Spring Cloud Gateway的權限認證與全局校驗

在微服務架構中,API網關是系統的入口,所有的外部請求都會經過網關。因此,將權限認證和全局校驗的邏輯放在網關層是非常合適的。Spring Cloud Gateway提供了強大的過濾器機制,可以在請求到達目標服務之前進行權限校驗。

3.1 權限認證流程

  1. 用户登錄:用户通過登錄接口提交用户名和密碼,系統驗證用户身份後生成JWT(JSON Web Token)並返回給客户端。
  2. 請求攜帶Token:客户端在後續請求中攜帶JWT,通常放在請求頭的Authorization字段中。
  3. 網關校驗Token:Spring Cloud Gateway在接收到請求後,首先通過自定義的全局過濾器校驗JWT的有效性。如果Token無效或過期,網關直接返回401 Unauthorized錯誤。
  4. 權限校驗:在Token校驗通過後,網關根據用户的角色和請求的路徑,判斷用户是否有權限訪問該資源。如果沒有權限,返回403 Forbidden錯誤。
  5. 路由到目標服務:如果權限校驗通過,網關將請求路由到目標微服務。

3.2 全局校驗的實現

Spring Cloud Gateway的全局校驗可以通過自定義GlobalFilter來實現。以下是一個簡單的全局校驗過濾器示例:

public class GlobalGatewayFilter implements GlobalFilter, Ordered {

    private final AuthProperties authProperties;
    private final JwtUtils jwtUtils;
    private final RedisUtils redisUtils;
...
    /**
     * Token過濾器
     *
     * @param exchange exchange
     * @param chain    chain
     * @return Mono
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
...
        String token = getToken(request);
...
        DecodedJWT jwt;
        try {
            jwt = jwtUtils.verifyJwt(token);
        } catch (TokenExpiredException e) {
            return unauthorized(requestUrl, exchange.getResponse(), "token is expired.");
        } catch (JWTVerificationException e) {
            return unauthorized(requestUrl, exchange.getResponse(), "token verification failed.");
        }
        if (jwt == null) {
            return unauthorized(requestUrl, exchange.getResponse(), "token is not illegal.");
        }
...
        if (!verifyAuthorization(jwt, requestUrl, request.getMethod())) {
            return unauthorized(requestUrl, exchange.getResponse(), "request access denied, may be unauthorized.");
        }
...
        //set username in request header
        ServerHttpRequest newRequest = request.mutate()
                .header("X-Forwarded-For", newHeader)
                .header(SystemConstant.HEADER_AUTHORIZATION, "")
                .header(AuthConstants.USERNAME, username)
                .header(AuthConstants.AUTH_WAREHOUSE, "").build();
        return chain.filter(exchange.mutate().request(newRequest).build());
   }
...
}

在這個示例中,AuthFilter首先從請求頭中獲取JWT,並校驗其有效性。如果Token無效,直接返回401錯誤。如果Token有效,則從Token中提取用户角色,並根據請求的路徑判斷用户是否有權限訪問該資源。如果沒有權限,返回403錯誤。如果權限校驗通過,請求將繼續傳遞到後續的過濾器鏈。

資源訪問權限驗證代碼如下:

    public boolean verifyAuthorization(DecodedJWT jwt, String requestUrl, HttpMethod httpMethod) throws JWTVerificationException {
...
        Claim authorities = jwt.getClaim(AuthConstants.AUTH_MENUS);
        if (null == authorities) {
            return false;
        }
        List<String> authoritySet = authorities.asList(String.class);
        if (CollectionUtils.isEmpty(authoritySet)) {
            return false;
        }
        if (authoritySet.contains(AuthConstants.SUPPER_PERMISSION)) {
            return true;
        }
        String url = httpMethod.name().toLowerCase() + ":" + requestUrl;
        return authoritySet.stream().anyMatch(url::startsWith);
    }
這裏 Claim authorities = jwt.getClaim(AuthConstants.AUTH_MENUS) 我們先把用户的權限保存在token中,然後從token中獲取用户的權限,但是如果用户的權限太大,可能會造成token過大,影響性能,則需要通過其他的方式,比如去用户管理服務中查詢角色的權限,然後緩存下來,這樣token中只需要保存用户角色信息就可以。

3.3 RBAC權限管理

在RBAC模型中,權限的管理通常通過數據庫來實現。以下是一個簡單的權限管理表結構:

  • 用户表(User):存儲用户的基本信息。
  • 角色表(Role):存儲角色的基本信息。
  • 權限表(Permission):存儲權限的基本信息。
  • 用户角色表(UserRole):用户與角色的關聯表。
  • 角色權限表(RolePermission):角色與權限的關聯表。

通過這些表,可以實現用户、角色和權限的靈活管理。在權限校驗時,系統可以根據用户的角色查詢其擁有的權限,並判斷是否有權限訪問當前資源。

3.4 用户登錄

用户登錄代碼

    @PostMapping("/signin")
    public AuthModel authenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
        Authentication authentication = authenticationManager
                .authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
...
        TokenResponse tokenResponse = jwtUtils.generateJwtCookie(authorities, userDetails.getUsername(), authWarehouseCodes,
                userDetails.getUser().getTenantName());
        UserModel userModel = UserModel.builder().id(userDetails.getUser().getId())
                .username(userDetails.getUsername()).icon(userDetails.getUser().getAvatar()).build();
        return AuthModel.builder().token(tokenResponse.getToken()).user(userModel).expiresIn(tokenResponse.getExpiresIn()).build();
    }

token生成代碼

    public String generateToken(List<String> authorityList, String userName, Set<String> authWarehouseCodes, String tenantName) {
        Algorithm algorithm = Algorithm.HMAC256(tokenSecret);
        return JWT.create()
                .withClaim(AuthConstants.USERNAME, userName)
                .withClaim(AUTH_MENUS, authorityList)
                .withExpiresAt(new Date(System.currentTimeMillis() + tokenExpiration * 1000L))
                .sign(algorithm);
    }

4. 總結

基於Spring Cloud Gateway和RBAC的用户管理系統,能夠有效地實現權限認證與全局校驗。通過將權限校驗邏輯放在網關層,可以減少每個微服務的重複代碼,提高系統的安全性和可維護性。RBAC模型的引入,使得權限管理更加靈活和可控。在實際項目中,可以根據業務需求進一步擴展和優化這一方案,以滿足複雜的權限管理需求。

通過本文的介紹,希望讀者能夠理解如何利用Spring Cloud Gateway和RBAC構建一個安全的用户管理系統,並在實際項目中應用這些技術。

代碼見Github: openwes
Gitee: https://gitee.com/pigTear/open-wes

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.