基于【 责任链模式】二 || 网关zuul过滤器封装

一、基于责任链模式封装网关拦截

上一篇文章中已经使用建造者模式对网关拦截进行封装,存在一个问题,在连接器build中,每一个拦截都要进行true判断,代码看起来冗余,下面使用责任链模式封装

1、基于责任链设计模式封装: 创建网关请求拦截接口

/**
 * @Description: 基于责任链设计模式封装: 网关请求拦截
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:09
 * @Version: V1.0
 */
public interface GatewayHandler {
    /**
     * Handler处理器: 网关拦截处理请求
     */
    public void service(RequestContext ctx, String ipAddress, HttpServletRequest request, HttpServletResponse response);

    /**
     * 指向下一个handler
     */
    public void setNextHandler(GatewayHandler nextGatewayHandler);

    /**
     * 获取下一个Handler
     *
     * @return
     */
    public GatewayHandler getNextHandler();
}

2、实现接口业务逻辑

/**
 * @Description: 相同的Handler业务逻辑重构
 * @Author: kevin
 * @CreateTime: 2019-08-26 23:02
 * @Version: V1.0
 */
public class BaseHandler {

    protected GatewayHandler nextGatewayHandler;

    public void setNextHandler(GatewayHandler nextGatewayHandler) {
        this.nextGatewayHandler=nextGatewayHandler;
    }

    /**
     * 网关拦截
     * @param ctx
     * @param code
     * @param errorMsg
     */
    protected void resultError(RequestContext ctx, String errorMsg) {
        ctx.setResponseStatusCode(401);
        // 网关响应为false 不会转发服务
        ctx.setSendZuulResponse(false);
        ctx.setResponseBody(errorMsg);

    }
}
/**
 * @Description: [流程1] 基于责任链设计模式封装: 黑名单拦截
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:17
 * @Version: V1.0
 */
@Slf4j
//@Component
public class BlackBlockHandler extends BaseHandler implements GatewayHandler {



    @Override
    public void service(RequestContext ctx, String ipAddress, HttpServletRequest request, HttpServletResponse response) {
        // 4.从数据库动态读取,判断是黑名单,设置网关响应为false
        if("127.0.0.1".equals(ipAddress)){
            resultError(ctx, "ip:" + ipAddress + ",Insufficient access rights");
            // 直接停止了,不继续走后面的Handler
            return;
        }
        log.info(">>>>>>ip:{},验证通过>>>>>>>", ipAddress);
        // 执行下一个流程Handler
        nextGatewayHandler.service(ctx,ipAddress,request,response);
    }



    @Override
    public GatewayHandler getNextHandler() {
        return null;
    }


}
/**
 * @Description: [流程2] 基于责任链设计模式封装: 参数验签拦截
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:18
 * @Version: V1.0
 */
@Slf4j
//@Component
public class ParamsValidateHandler extends BaseHandler  implements GatewayHandler {
    @Override
    public void service(RequestContext ctx, String ipAddress, HttpServletRequest request, HttpServletResponse response) {
        // 5.外网传递参数签名拦截
        Map<String, String> verifyMap = SignUtil.toVerifyMap(request.getParameterMap(), false);
        if(!SignUtil.verify(verifyMap)){
            resultError(ctx, "ip:" + ipAddress + ",Sign fail");
            return;
        }
        // 执行下一个流程Handler
        nextGatewayHandler.service(ctx,ipAddress,request,response);
    }

    @Override
    public void setNextHandler(GatewayHandler gatewayHandler) {

    }

    @Override
    public GatewayHandler getNextHandler() {
        return null;
    }
}
/**
 * @Description: [流程3] 基于责任链设计模式封装: API权限控制
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:19
 * @Version: V1.0
 */
@Slf4j
//@Component
public class ApiAuthorizeHandler extends BaseHandler implements GatewayHandler {
    @Override
    public void service(RequestContext ctx, String ipAddress, HttpServletRequest request, HttpServletResponse response) {
        String servletPath = request.getServletPath();
        log.info(">>>>>servletPath:" + servletPath + ",servletPath.substring(0, 5):" + servletPath.substring(0, 5));
        // 内部服务调用不需要accessToken验证,如果public开头,表示授权公司访问接口
        if (!servletPath.substring(0, 7).equals("/public")) {
            // 执行下一个流程Handler
            nextGatewayHandler.service(ctx,ipAddress,request,response);
            return;
        }
        String accessToken = request.getParameter("accessToken");
        log.info(">>>>>accessToken验证:" + accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            resultError(ctx, "AccessToken cannot be empty");
            return;
        }
        // 调用接口验证accessToken是否失效
        BaseResponse<JSONObject> appInfo = verificaCodeServiceFeign.getAppInfo(accessToken);
        log.info(">>>>>>data:" + appInfo.toString());
        if (!isSuccess(appInfo)) {
            resultError(ctx, appInfo.getMsg());
            return;
        }
        // 执行下一个流程Handler
        nextGatewayHandler.service(ctx,ipAddress,request,response);
    }



    @Override
    public GatewayHandler getNextHandler() {
        return null;
    }
}

3、基于责任链设计模式封装: 使用工厂获取Handler

/**
 * @Description: 基于责任链设计模式封装: 使用工厂获取Handler
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:27
 * @Version: V1.0
 */
public class FactoryHandler {

    /**
     * 责任链启动方式:
     *
     * 1.将每一个Handler存放到集合中实现遍历
     *  可以把handler放入数据库,设置开关,不用每次修改代码
     *
     * @return
     */
    public static GatewayHandler getOneHandler() {
        // 1.黑名单判断
        GatewayHandler gatewayHandler1 = (GatewayHandler) SpringContextUtil.getBean("blackBlockHandler");
        // 2.api验证签名
        GatewayHandler gatewayHandler2 = (GatewayHandler) SpringContextUtil.getBean("paramsValidateHandler");
        gatewayHandler1.setNextHandler(gatewayHandler2);
        // 3.api接口验证token
        GatewayHandler gatewayHandler3 = (GatewayHandler) SpringContextUtil.getBean("apiAuthorizeHandler");
        gatewayHandler2.setNextHandler(gatewayHandler3);
        return gatewayHandler1;

    }
}

4、基于责任链设计模式封装: 责任链调用方

/**
 * @Description: 基于责任链设计模式封装: 责任链调用方
 * @Author: kevin
 * @CreateTime: 2019-08-26 22:35
 * @Version: V1.0
 */
//@Component
public class ResponsibilityClient {
    /**
     * 责任链调用
     */
    public void responsibility(RequestContext ctx, String ipAddress, HttpServletRequest request,
                               HttpServletResponse response) {
        GatewayHandler oneHandler = FactoryHandler.getOneHandler();
        oneHandler.service(ctx, ipAddress, request, response);
    }
}

5、网关过滤器中调用

@Override
    public Object run() throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();
        // 1.获取请求对象
        HttpServletRequest request = ctx.getRequest();
        // 2.获取响应对象
        HttpServletResponse response = ctx.getResponse();

        // 3.获取客户端真实ip地址
        String ipAddr = IpUtil.getIpAddr(request);
        if (StringUtils.isEmpty(ipAddr)) {
            resultError(ctx, "未能够获取到ip地址");
        }

        // 4 设计模式二: 基于责任链模式封装
        responsibilityClient.responsibility(ctx,ipAddr,request,response);

        return null;



    }

 

上一篇:Socket网络编程之客户端


下一篇:ipaddress-5.0.2.jar 使用方法