DailyMart是一個ToC的在線購物商城,目前僅支持通過瀏覽器訪問。在商城中的所有操作都需要用戶先登錄。為了實現這一需求,我們可以采用以下技術方案:
詳細交互流程如下圖1所示:
圖1:PC認證流程
這種架構在初期可以滿足業務的發展需求。然而,隨著業務的擴展,我們需要考慮到現在大部分用戶使用手機進行購物的情況。因此,DailyMart也需要支持手機端訪問。但與瀏覽器不同,手機端的認證機制可能會有所不同。
例如,瀏覽器端的Token有效期通常設定為1小時,而手機端的Token有效期通常設置為7天或更長。此外,瀏覽器端的Token采用JWT這種去中心化的認證機制,而手機端的Token采用中心化的認證機制,需要調用手機端服務進行登錄認證。
同時,為了擴展業務,其他一些第三方應用可能也需要調用DailyMart的后端服務來獲取數據,對于第三方的應用一般采用appId + appSecret的方式進行認證,同時需要對接口參數進行簽名防止出現篡改和重放。(此方案在前文中有詳細說明,可以通過鏈接跳轉訪問查看。)
現在的問題是,如何在原有架構的基礎上滿足這三種不同形式的認證需求呢?
圖片
要解決這個問題,最關鍵在于如何判斷請求的來源,是來自瀏覽器端的請求、手機端的請求還是第三方的請求?
我們可以通過請求路徑進行區分,對于不同端的請求使用不同的路徑進行標識,可以做如下約定:
最終規定接口的完整請求路徑為:/服務名/api/來源標識/接口路徑/,如:http://localhost:9090/customer-service/api/pd/customer/info
這樣在SpringCloud Gateway網關先獲取請求的路徑,再根據請求的路徑判斷請求來源,最后根據請求來源實現不同的認證方案。
解決這個問題的關鍵在于如何判斷請求的來源,即是來自瀏覽器端、手機端還是第三方應用?
我們可以通過請求路徑進行區分,對于不同端的請求使用不同的路徑進行標識。例如:
最終,我們規定接口的完整請求路徑為:/服務名/api/來源標識/接口路徑/,例如:http://localhost:9090/customer-service/api/pd/customer/info
這樣,在SpringCloud Gateway網關中,我們需要創建一個過濾器,首先獲取請求的路徑,然后根據請求的路徑判斷請求來源,最后根據請求來源實現不同的認證方案。
有了解決方案,我們就很容易完成代碼實現了。
為了滿足多端認證的需求,在網關服務中我們可以抽取一個公共的認證接口ApiAuthenticator,具體的認證邏輯由具體實現類實現。
圖片
在上面的類圖中,ProtectedApiAuthenticator用于實現第三方的認證邏輯,DefaultApiAuthenticator用于實現瀏覽器端的認證邏輯。
在網關過濾器ApiAuthenticatorFilter中,我們首先根據請求路徑獲取請求來源,然后根據請求來源找到對應的實現類。
@Component@Slf4jpublic class ApiAuthenticatorFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI uri = exchange.getRequest().getURI(); String rawPath = uri.getRawPath(); // 靜態接口直接過濾 if (handleExcludeUrl(rawPath)) { return chain.filter(exchange); } // 獲取認證邏輯 ApiAuthenticator apiAuthenticator = getApiAuthenticator(rawPath); AuthenticatorResult authenticatorResult = apiAuthenticator.auth(exchange); if (!authenticatorResult.isResult()) { return Mono.error(new HttpServerErrorException( HttpStatus.METHOD_NOT_ALLOWED, authenticatorResult.getMessage())); } return chain.filter(exchange); } /** * 確定認證策略 * @param rawPath 請求路徑 */ private ApiAuthenticator getApiAuthenticator(String rawPath) { String[] parts = rawPath.split("/"); if (parts.length >= 4) { String parameter = parts[3]; return switch (parameter) { case PROTECT_PATH -> new ProtectedApiAuthenticator(); case PRIVATE_PATH -> new PrivateApiAuthenticator(); case PUBLIC_PATH -> new PublicApiAuthenticator(); case DEFAULT_PATH -> new DefaultApiAuthenticator(); default -> throw new IllegalStateException("Unexpected value: " + parameter); }; } return new DefaultApiAuthenticator(); } }
以下是瀏覽器端的認證邏輯,它會驗證JWT token的有效性。如果token失效,則直接返回錯誤提示給用戶,引導其重新登錄。
@Component@Slf4jpublic class DefaultApiAuthenticator implements ApiAuthenticator { @Override public AuthenticatorResult auth(ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders httpHeaders = request.getHeaders(); // 獲取JWT請求頭 Authorization String token = httpHeaders.getFirst(HttpHeaders.AUTHORIZATION); if (Objects.nonNull(token)) { try { String subjectFromJWT = JwtUtil.getSubjectFromJWT(token); log.info("用戶請求token: {} , 身份Subject:{}", token, subjectFromJWT); //重新設置請求頭 mutateNewHeader(exchange, subjectFromJWT); return new AuthenticatorResult(true, "認證通過"); } catch (ParseException | JOSEException e) { log.error("token解析失敗"); return new AuthenticatorResult(false, "Token錯誤,請重新登錄!"); } } return new AuthenticatorResult(false, "Token為空,請重新登錄!"); }}
本文提出了一種靈活、可擴展的方案,以滿足 DailyMart 在業務發展過程中的多端認證需求。通過使用請求路徑區分不同端的請求來源,并在 SpringCloud Gateway 網關中實現相應的過濾器進行認證,方案具有靈活性、可擴展性和可維護性。
本文鏈接:http://www.www897cc.com/showinfo-26-76482-0.htmlSpringCloud微服務中如何實現多端認證?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com