????????過濾器模式(Filter Pattern)或標準模式(Criteria Pattern)是一種設計模式,這種模式允許開發人員使用不同的標準來過濾一組對象,通過邏輯運算以解耦的方式把它們連接起來。這種類型的設計模式屬于結構型模式,它結合多個標準來獲得單一標準。
??????? 業務場景:每次請求通過網關,需要驗證請求頭是否攜帶 token,sign簽名等
??????? 類圖:
AuthService:所有濾器類都必須實現的接口
AuthTokenServiceImpl:Token驗證過濾器
AuthSignServiceImpl:簽名驗證過濾器
AuthFactory:過濾器工廠,利用SpringBoot功能特性,實現自動獲取過濾器
AuthDTO:過濾器所需要的參數
AuthGatewayFilterFactory:權限校驗過濾器(gateway)
AuthService:
/*** @Author: wmh* @Description: 權限校驗過濾器* @Date: 2023/8/3 18:19* @Version: 1.0*/
public interface AuthService {/*** @Description: 過濾方法* @Param authDTO: 網關上下文* @return: String* @Author: wmh* @Date: 2023/8/3 18:12*/String apply(AuthDTO authDTO);}
返回值可以定義為統一返回值(R)等,為了演示方便,就返回字符串了
AuthTokenServiceImpl:
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;/*** @Author: wmh* @Description: token校驗* @Date: 2023/8/3 18:21* @Version: 1.0*/
@Slf4j
@Order(0)
@Service
public class AuthTokenServiceImpl implements AuthService {/*** @Description: 驗證token* @Param authDTO: 網關上下文* @return: com.norinaviation.atm.common.base.data.R* @Author: wmh* @Date: 2023/8/3 19:31*/@Override@SneakyThrowspublic String apply(AuthDTO authDTO) {String tokenHeader = authDTO.getHeaders().getFirst(CommonConstant.X_TOKEN);if (StrUtil.isBlank(appId)) {return "appId不能為空";}if (StrUtil.isBlank(tokenHeader)) {return "TOKEN不能為空";}JWT jwt = JWTUtil.parseToken(tokenHeader);boolean verifyKey = jwt.setKey(CommonConstant.JWT_TOKEN.getBytes()).verify();// 驗證token是否正確if (!verifyKey) {log.info("appId:{}, TOKEN auth fail, TOKEN:{}", appId, tokenHeader);return "TOKEN認證失敗";}boolean verifyTime = jwt.validate(0);// 驗證token是否過期if (!verifyTime) {log.info("appId:{}, TOKEN expired, TOKEN:{}", appId, tokenHeader);return "TOKEN已過期";}return "success";}}
AuthSignServiceImpl:
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
/*** @Author: wmh* @Description: 驗簽校驗* @Date: 2023/8/3 18:24* @Version: 1.0*/
@Slf4j
@Order(1)
@Service
public class AuthSignServiceImpl implements AuthService {/*** @Description: 驗證簽名* @Param authDTO: 網關上下文* @return: Stirng* @Author: wmh* @Date: 2023/8/3 19:30*/@Override@SneakyThrowspublic Stirng apply(AuthDTO authDTO) {// 簽名邏輯,業務代碼就不公開了return "success";}}
AuthFactory:
import cn.hutool.core.util.ObjectUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.*;/*** @Author: wmh* @Description: 權限工廠* @Date: 2023/8/7 15:54* @Version: 1.0*/
@Component
public class AuthFactory implements ApplicationContextAware {/*** 過濾方式*/private List<AuthService> authFilters = new ArrayList<>();/*** 獲取應用上下文并獲取相應的接口實現類* @param applicationContext* @throws BeansException*/@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {// 獲取實現類Map<Integer, AuthService> authServiceMap = new HashMap<>();applicationContext.getBeansOfType(AuthService.class).values().stream().forEach(authService -> {if (ObjectUtil.isNull(authService.getClass().getAnnotation(Order.class))) {authServiceMap.put(CommonConstant.DEFAULT_ORDER, authService);}else {authServiceMap.put(authService.getClass().getAnnotation(Order.class).value(), authService);}});// 根據order排序authServiceMap.entrySet().stream().sorted(Comparator.comparing(e -> e.getKey())).forEach(map -> {authFilters.add(map.getValue());});}/*** @Description: 是否全部符合過濾條件* @Param authDTO: 網關上下文* @return: String* @Author: wmh* @Date: 2023/8/3 19:27*/public String apply(AuthDTO authDTO) {for (AuthService filter : authFilters) {String str = filter.apply(authDTO);if (!StrUtil.equals(str, "success")) {return str;}}return "success";}}
AuthDTO:
import lombok.Data;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;/*** @Author: wmh* @Description: 網關上下文* @Date: 2023/8/3 19:09* @Version: 1.0*/
@Data
public class AuthDTO {/*** cache headers*/private HttpHeaders headers;/*** cache json body*/private String cacheBody;/*** cache formdata*/private MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();}
此類為gateway網關需要,只展示使用過濾鏈的代碼塊
AuthGatewayFilterFactory:
/*** @Author: wmh* @Description: 權限校驗過濾器* @Date: 2023/8/3 19:15* @Version: 1.0*/
@Slf4j
@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory {@Autowiredprivate AuthFactory authFactory;@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {ServerHttpRequest serverHttpRequest = exchange.getRequest();...// 獲取request bodyGatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);AuthDTO authDTO = new AuthDTO();authDTO.setHeaders(gatewayContext.getHeaders());authDTO.setCacheBody(gatewayContext.getCacheBody());authDTO.setFormData(gatewayContext.getFormData());// 驗證String strr = authFactory.apply(authDTO);...return chain.filter(exchange);};}}
Gateway相關:SpringCloud-Gateway實現網關_springcloud配置網關_W_Meng_H的博客-CSDN博客網關作為流量的入口,常用的功能包括路由轉發、權限校驗、限流等Spring Cloud 是Spring官方推出的第二代網關框架,由WebFlux+Netty+Reactor實現的響應式的API網關,它不能在傳統的servlet容器工作,也不能構建war包。基于Filter的方式提供網關的基本功能,例如說安全認證、監控、限流等。_springcloud配置網關https://blog.csdn.net/W_Meng_H/article/details/129775851
CommonConstant(常量類):
/*** @Author: wmh* @Description: 常用變量* @Date: 2023/3/30 10:29* @Version: 1.0*/
@Component
public class CommonConstant {// JWT密鑰public static String JWT_TOKEN;// 請求頭中的tokenpublic static final String X_TOKEN = "X-TOKEN";// 請求頭中的簽名public static final String X_SIGN = "X-SIGN";// 請求頭中的appIdpublic static final String X_APPID = "X-APPID";// 請求頭中的時間戳public static final String X_TIMESTAMP = "X-TIMESTAMP";}