淘东电商项目(72) -互联网安全架构设计(责任链模式重构网关流程)

简介: 淘东电商项目(72) -互联网安全架构设计(责任链模式重构网关流程)

引言

本文代码已提交至Github(版本号:a27a585f2c3e71c17d59da1e1ea1480da1e90ba5),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

在之前博客《淘东电商项目(67) -互联网安全架构设计(方法论)》,主要讲解了互联网安全架构设计的方法,主要介绍了如下几种:

  • 基于网关实现IP黑名单与名单拦截
  • API接口实现Token授权认证
  • 使用MD5实现API接口验证签名,防止抓包篡改数据
  • 实现API接口安全加密传输(公钥和私钥互换机制)
  • 基于Oauth2.0 实现API接口开放平台
  • 接口参数使用网关实现防止XSS、SQL注入
  • 定期工具实现代码健康扫描

上面的打勾代码实现在前面博客已经讲解完,本文主要讲解网关代码的逻辑重构。

本文目录结构:

l____引言

l____ 1.建造者模式的缺陷

l____ 2.责任链模式重构代码

l________ 2.1 责任链模式原理

l________ 2.2 代码实现

l____ 3.测试

1.建造者模式的缺陷

在前面的几篇博客,可以知道网络的流程处理使用了建造者模式,代码结构如下,有建造者接口(GatewayBuild)、建造者实现类(VerificationBuild)、建造者管理类(GatewayDirector):

其中建造者实现类的代码如下:

/**
 * description: 参数验证
 * create by: YangLinWei
 * create time: 2020/5/20 9:09 上午
 */
@Slf4j
@Component
public class VerificationBuild implements GatewayBuild {
    @Autowired
    private BlacklistMapper blacklistMapper;
    @Autowired
    private AuthorizationServiceFeign verificaCodeServiceFeign;
    @Override
    public Boolean blackBlock(RequestContext ctx, String ipAddres, HttpServletResponse response) {
        // 2.查询数据库黑名单
        Blacklist blacklist = blacklistMapper.findBlacklist(ipAddres);
        if (blacklist != null) {
            resultError(ctx, "ip:" + ipAddres + ",Insufficient access rights");
            return false;
        }
        log.info(">>>>>>ip:{},验证通过>>>>>>>", ipAddres);
        // 3.将ip地址传递到转发服务中
        response.addHeader("ipAddres", ipAddres);
        log.info(">>>>>>ip:{},验证通过>>>>>>>", ipAddres);
        return true;
    }
    @Override
    public Boolean toVerifyMap(RequestContext ctx, String ipAddres, HttpServletRequest request) {
        // 4.外网传递参数验证
        Map<String, String> verifyMap = SignUtil.toVerifyMap(request.getParameterMap(), false);
        if (!SignUtil.verify(verifyMap)) {
            resultError(ctx, "ip:" + ipAddres + ",Sign fail");
            return false;
        }
        return true;
    }
    @Override
    public Map<String, List<String>> filterParameters(HttpServletRequest request, RequestContext ctx) {
        Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
        if (requestQueryParams == null) {
            requestQueryParams = new HashMap<>();
        }
        Enumeration em = request.getParameterNames();
        while (em.hasMoreElements()) {
            String name = (String) em.nextElement();
            String value = request.getParameter(name);
            ArrayList<String> arrayList = new ArrayList<>();
            // 将参数转化为html参数 防止xss攻击 < 转为&lt;
            arrayList.add(StringEscapeUtils.escapeHtml(value));
            requestQueryParams.put(name, arrayList);
            log.info(">>>>>>过滤参数name:{},arrayList:{}>>>>>>>", name, arrayList);
        }
        return requestQueryParams;
    }
    @Override
    public Boolean apiAuthority(RequestContext ctx, HttpServletRequest request) {
        String servletPath = request.getServletPath();
        log.info(">>>>>servletPath:" + servletPath + ",servletPath.substring(0, 5):" + servletPath.substring(0, 5));
        if (!servletPath.substring(0, 7).equals("/public")) {
            return true;
        }
        String accessToken = request.getParameter("accessToken");
        log.info(">>>>>accessToken验证:" + accessToken);
        if (StringUtils.isEmpty(accessToken)) {
            resultError(ctx, "AccessToken cannot be empty");
            return false;
        }
        // 调用接口验证accessToken是否失效
        BaseResponse<JSONObject> appInfo = verificaCodeServiceFeign.getAppInfo(accessToken);
        log.info(">>>>>>data:" + appInfo.toString());
        if (!isSuccess(appInfo)) {
            resultError(ctx, appInfo.getMsg());
            return false;
        }
        return true;
    }
    private void resultError(RequestContext ctx, String errorMsg) {
        ctx.setResponseStatusCode(401);
        ctx.setSendZuulResponse(false);
        ctx.setResponseBody(errorMsg);
    }
    // 接口直接返回true 或者false
    public Boolean isSuccess(BaseResponse<?> baseResp) {
        if (baseResp == null) {
            return false;
        }
        if (!baseResp.getCode().equals(Constants.HTTP_RES_CODE_200)) {
            return false;
        }
        return true;
    }
}

可以看到,随着业务的需求增加,如果还要加其它的过滤条件,将会都在这个类里编写,是相当冗余的,因此建造者模式不再适用于当前的网关过滤功能,所以本文将使用“责任链模式”来替代“建造者模式”。

2.责任链模式重构代码

2.1 责任链模式原理

之前有写过过责任链模式的博客,有兴趣的同学可以参阅下:《设计模式17 - 责任链模式【Chain of Responsibility Pattern】》

对于网关过滤功能,我画出了一张关于责任链模式的原理图:

上图流程描述:首先责任链调用方调用责任链工厂去获取第一个Handler,然后执行黑名单Handler,执行完后,黑名单handler会执行加签验证handler,加签Handler执行完后,加签Handler会执行AccessToken验证handler,依次类推。只要其中的一个handler不同过,其关联的下一个handler都不能执行。

好了,下面直接讲解代码实现。

2.2 代码实现

首先看看代码结构:

①handler接口(GateWayHandler):

/**
 * description: 网关Handler接口
 * create by: YangLinWei
 * create time: 2020/5/21 3:33 下午
 */
public interface GatewayHandler {
    /**
     * 网关拦截处理请求
     */
    Boolean service(RequestContext ctx, String ipAddres, HttpServletRequest request, HttpServletResponse response);
    /**
     * 设置下一个
     */
    void setNextHandler(GatewayHandler gatewayHandler);
    /**
     * 获取下一个Handler
     *
     * @return
     */
    public GatewayHandler getNextHandler();
}

②handler实现接口的父接口(BaseHandler公用方法抽取):

/**
 * description: handler基类
 * create by: YangLinWei
 * create time: 2020/5/21 3:38 下午
 */
public class BaseHandler {
    protected GatewayHandler gatewayHandler;
    public void setNextHandler(GatewayHandler gatewayHandler) {
        this.gatewayHandler = gatewayHandler;
    }
    public GatewayHandler getNextHandler() {
        return gatewayHandler;
    }
    protected void resultError(RequestContext ctx, String errorMsg) {
        ctx.setResponseStatusCode(401);
        // 网关响应为false 不会转发服务
        ctx.setSendZuulResponse(false);
        ctx.setResponseBody(errorMsg);
    }
    // 接口直接返回true 或者false
    public Boolean isSuccess(BaseResponse<?> baseResp) {
        if (baseResp == null) {
            return false;
        }
        if (!baseResp.getCode().equals(Constants.HTTP_RES_CODE_200)) {
            return false;
        }
        return true;
    }
}

③handler子类实现(以BlackListHandler为例子):

/**
 * description: 黑名单处理handler
 * create by: YangLinWei
 * create time: 2020/5/21 3:37 下午
 */
@Component
@Slf4j
public class BlackListHandler extends BaseHandler implements GatewayHandler {
    @Autowired
    private BlacklistMapper blacklistMapper;
    @Override
    public Boolean service(RequestContext ctx, String ipAddres, HttpServletRequest request, HttpServletResponse response) {
        // 2.查询数据库黑名单
        Blacklist blacklist = blacklistMapper.findBlacklist(ipAddres);
        if (blacklist != null) {
            resultError(ctx, "ip:" + ipAddres + ",Insufficient access rights");
            return Boolean.FALSE;
        }
        log.info(">>>>>>ip:{},验证通过>>>>>>>", ipAddres);
        // 3.将ip地址传递到转发服务中
        response.addHeader("ipAddres", ipAddres);
        log.info(">>>>>>ip:{},验证通过>>>>>>>", ipAddres);
        if (gatewayHandler != null) {
            gatewayHandler.service(ctx, ipAddres, request, response);
        }
        return Boolean.TRUE;
    }
}

④责任链链工厂(FactoryHandler):

public class FactoryHandler {
    public static GatewayHandler getHandler() {
        // 1.黑名单拦截
        GatewayHandler handler1 = (GatewayHandler) SpringContextUtil.getBean("blackListHandler");
        // 2.API接口参数接口验签
        GatewayHandler handler2 = (GatewayHandler) SpringContextUtil.getBean("verifySignHandler");
        handler1.setNextHandler(handler2);
        // 3.参数过滤
        GatewayHandler handler3 = (GatewayHandler) SpringContextUtil.getBean("filterParamHandler");
        handler1.setNextHandler(handler3);
        //4.验证accessToken
        GatewayHandler handler4 = (GatewayHandler) SpringContextUtil.getBean("apiAuthorityHandler");
        handler3.setNextHandler(handler4);
        return handler1;
    }
}

⑤责任链调用者(ResponsibilityClient):

@Component
public class ResponsibilityClient {
    public void responsibility(RequestContext ctx, String ipAddres, HttpServletRequest request,
                               HttpServletResponse response) {
        GatewayHandler handler = FactoryHandler.getHandler();
        handler.service(ctx, ipAddres, request, response);
    }
}

3.测试

仿照上一篇博客的测试流程,浏览器先输入错误的accessToken:http://127.0.0.1/public/api-pay/cratePayToken?payAmount=9999&orderId=20200513141452&userId=27&productName=apple&accessToken=11111

可以看到拒绝访问,浏览器输入正确的token,访问:http://127.0.0.1/public/api-pay/cratePayToken?payAmount=9999&orderId=20200513141452&userId=27&productName=apple&accessToken=authfdc563ec2ec049ea8fc66ab777215bb5,可以看到访问成功。

本文完!

目录
相关文章
|
4月前
|
存储 缓存 安全
某鱼电商接口架构深度剖析:从稳定性到高性能的技术密码
某鱼电商接口架构揭秘:分层解耦、安全加固、性能优化三维设计,实现200ms内响应、故障率低于0.1%。详解三层架构、多引擎存储、异步发布、WebSocket通信与全链路防护,助力开发者突破电商接口“三难”困境。
|
7月前
|
消息中间件 监控 前端开发
如何开发项目管理系统中的项目结项板块?(附架构图+流程图+代码参考)
在企业项目管理中,“项目结项”是关键环节,常因流程不清、文档不全、审批滞后等问题导致交付困难。本文介绍如何通过“项目结项”模块实现线上化管理,涵盖结项申请、审批流程、成果上传、权限控制等功能,帮助团队高效完成项目收尾,避免成果丢失与流程混乱。内容包括功能设计、业务流程、系统架构、数据库设计、核心代码实现、前端交互及优化建议,助力项目管理系统快速落地并稳定运行。
|
4月前
|
缓存 Cloud Native 中间件
《聊聊分布式》从单体到分布式:电商系统架构演进之路
本文系统阐述了电商平台从单体到分布式架构的演进历程,剖析了单体架构的局限性与分布式架构的优势,结合淘宝、京东等真实案例,深入探讨了服务拆分、数据库分片、中间件体系等关键技术实践,并总结了渐进式迁移策略与核心经验,为大型应用架构升级提供了全面参考。
|
8月前
|
安全 测试技术 API
电商API接口开发:基础架构搭建全攻略
本文详细解析了电商API接口从零搭建基础架构的全流程。首先通过需求分析明确业务功能与接口规范,选定数据格式(如JSON)及通信方式(如RESTful)。接着在架构设计阶段选择合适的技术栈、数据库方案,并引入API网关实现统一管理。开发实现部分涵盖认证授权、数据访问、日志记录与异常处理等核心功能。安全防护则强调数据加密、传输安全及速率限制策略。测试优化阶段包括单元测试、集成测试、性能与安全测试,确保接口稳定性。最后通过工具生成清晰的API文档并实施版本控制,为开发者提供便利。整体流程系统化、模块化,助力打造高效、安全的电商API接口。
|
8月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
508 102
|
6月前
|
人工智能 自然语言处理 JavaScript
Github又一AI黑科技项目,打造全栈架构,只需一个统一框架?
Motia 是一款现代化后端框架,融合 API 接口、后台任务、事件系统与 AI Agent,支持 JavaScript、TypeScript、Python 多语言协同开发。它提供可视化 Workbench、自动观测追踪、零配置部署等功能,帮助开发者高效构建事件驱动的工作流,显著降低部署与运维成本,提升 AI 项目落地效率。
610 0
|
7月前
|
数据挖掘 项目管理 Python
如何开发项目管理系统中的项目启动板块?(附架构图+流程图+代码参考)
本文介绍了项目管理系统中“项目启动”板块的设计与实现,涵盖功能模块、业务流程、开发技巧及效果展示,并提供代码参考和常见问题解答,助力企业高效搭建项目管理平台。
|
7月前
|
缓存 Java 数据库
Java 项目分层架构实操指南及长尾关键词优化方案
本指南详解基于Spring Boot与Spring Cloud的Java微服务分层架构,以用户管理系统为例,涵盖技术选型、核心代码实现、服务治理及部署实践,助力掌握现代化Java企业级开发方案。
353 2
|
7月前
|
存储 Java 数据库连接
简单学Spring Boot | 博客项目的三层架构重构
本案例通过采用三层架构(数据访问层、业务逻辑层、表现层)重构项目,解决了集中式开发导致的代码臃肿问题。各层职责清晰,结合依赖注入实现解耦,提升了系统的可维护性、可测试性和可扩展性,为后续接入真实数据库奠定基础。
593 0
|
7月前
|
监控 前端开发 BI
如何开发项目管理系统中的项目收支板块?(附架构图+流程图+代码参考)
本文深入讲解项目管理系统中项目收支模块的设计与实现,涵盖预算、收入与支出管理,以及报表分析功能。内容包括模块功能概述、业务流程、开发技巧与实现方法,并提供数据库设计及前后端代码示例,助力企业打造高效的项目财务管控系统。