过滤器模式(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";}