引言
在現代微服務架構中,用户管理系統是核心組件之一,負責用户的身份認證和權限管理。隨着系統規模的擴大,如何高效地管理用户權限、確保系統的安全性成為了一個重要挑戰。基於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 權限認證流程
- 用户登錄:用户通過登錄接口提交用户名和密碼,系統驗證用户身份後生成JWT(JSON Web Token)並返回給客户端。
- 請求攜帶Token:客户端在後續請求中攜帶JWT,通常放在請求頭的
Authorization字段中。 - 網關校驗Token:Spring Cloud Gateway在接收到請求後,首先通過自定義的全局過濾器校驗JWT的有效性。如果Token無效或過期,網關直接返回401 Unauthorized錯誤。
- 權限校驗:在Token校驗通過後,網關根據用户的角色和請求的路徑,判斷用户是否有權限訪問該資源。如果沒有權限,返回403 Forbidden錯誤。
- 路由到目標服務:如果權限校驗通過,網關將請求路由到目標微服務。
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