• 网站首页
  • 微服务
  • 设计模式
  • 并发编程
  • 源码分析
  • 中间件
  • 数据库
  • 文献
  • 工具
  • 其他
  • 责任链设计模式之如何给网关设计一款专属的权限控制?

    发布时间: 2019-12-19 17:49首页:架构技术精选 > 设计模式 > 阅读()

    什么是责任链模式

    客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。使编程更有灵活性。

    关键点

    1、有多个对象共同对一个任务进行处理。
    2、这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。
    3、一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。
    4、客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。
    
    

    责任链模式环境

    1.抽象处理者(Handler)角色:定义出一个处理请求的接口
    2.具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。

    责任链模式优缺点

    优点:
    职责链模式的最主要功能就是:动态组合,请求者和接受者解耦。
    请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
    动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
    
    缺点:
    产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
    不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。
    

    责任链模式应用场景

    1.多条件流程判断 权限控制
    2.ERP系统 流程审批 总经理、人事经理、项目经理
    3.Java过滤器的底层实现Filter 
    比如:在Java过滤器中客户端发送请求到服务器端,过滤会经过参数过滤、session过滤、表单过滤、隐藏过滤、检测请求头过滤
    

    网关权限控制责任链模式

    在网关作为微服务程序的入口,拦截客户端所有的请求实现权限控制 ,比如先判断Api接口限流、黑名单、用户会话信息拦截。

    Api接口限流→黑名单拦截→用户会话信息拦截

    GatewayHandler抽象角色

    /**
     * @title: GatewayHandler
     */
    public abstract class GatewayHandler {
        /**
         * 执行下一个handler
         */
        protected GatewayHandler nextGatewayHandler;
        public void setNextGatewayHandler(GatewayHandler nextGatewayHandler) {
            this.nextGatewayHandler = nextGatewayHandler;
        }
    
        //实现的handler 处理方案 强制必须实现
        public abstract void service();
    
        protected void nextService() {
            if (nextGatewayHandler != null)
                // 指向下一关
                nextGatewayHandler.service();
        }
    }
    

    具体Handler实现

    @Slf4j
    @Component
    public class CurrentLimitHandler extends GatewayHandler {
        @Override
        public void service() {
            log.info("第一关 API接口限流操作.....");
            //指向下一关
            nextService();
        }
    }
    @Component
    @Slf4j
    public class BlacklistHandler extends GatewayHandler {
        @Override
        public void service() {
            log.info("第二关 黑名单拦截.......");
            //指向下一关
            nextService();
        }
    }
    @Component
    @Slf4j
    public class ConversationHandler extends GatewayHandler {
        @Override
        public void service() {
            log.info("第三关 用户的会话信息拦截.......");
            }
    }

    基于数据库实现

    相关SQL语句

    CREATE TABLE `gateway_handler` (
      `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
      `handler_name` varchar(32) DEFAULT NULL COMMENT 'handler名称',
      `handler_id` varchar(32) DEFAULT NULL COMMENT 'handler主键id',
      `prev_handler_id` varchar(32) DEFAULT NULL,
      `next_handler_id` varchar(32) DEFAULT NULL COMMENT '下一个handler',
      PRIMARY KEY (`ID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
    
    -- ----------------------------
    -- Records of gateway_handler
    -- ----------------------------
    INSERT INTO `gateway_handler` VALUES ('16', 'Api接口限流', 'currentLimitHandler', null, 'blacklistHandler');
    INSERT INTO `gateway_handler` VALUES ('17', '黑名单拦截', 'blacklistHandler', 'currentLimitHandler', 'conversationHandler');
    INSERT INTO `gateway_handler` VALUES ('18', '会话验证', 'conversationHandler', 'blacklistHandler', null);
    

    Utils

    @Component
    public class SpringUtils implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        //获取applicationContext
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        //通过name获取 Bean.
        public static Object getBean(String name){
            return getApplicationContext().getBean(name);
        }
    
        //通过class获取Bean.
        public static <T> T getBean(Class<T> clazz){
            return getApplicationContext().getBean(clazz);
        }
    
        //通过name,以及Clazz返回指定的Bean
        public static <T> T getBean(String name,Class<T> clazz){
            return getApplicationContext().getBean(name, clazz);
        }
    
    }

    GatewayHandlerService

    /**
     * @title: GatewayHandlerService
     */
    @Service
    public class GatewayHandlerService {
        @Autowired
        private GatewayHandlerMapper gatewayHandlerMapper;
        private GatewayHandler firstGatewayHandler;
        /**
         * 获取数据库第一个handeler封装
         */
        public GatewayHandler getFirstGatewayHandler() {
            if (firstGatewayHandler != null) {
                return firstGatewayHandler;
            }
            //1.【第一个实体】
            GatewayHandlerEntity firstGatewayHandlerEntity = gatewayHandlerMapper.getFirstGatewayHandler();
            if(firstGatewayHandlerEntity==null){ return null; }
            //2.【第一个HandlerId】
            String firstHhandlerId = firstGatewayHandlerEntity.getHandlerId();
            //3.【第二个HandlerId】
            String secondHandlerId = firstGatewayHandlerEntity.getNextHandlerId();
            //4.【第一个bean对象】
            GatewayHandler firstGatewayHandler = SpringUtils.getBean(firstHhandlerId, GatewayHandler.class);
            //5.【tmpHandler为第一个bean对象】
            GatewayHandler tmpHandler=firstGatewayHandler;
            //如果下一个HandlerId不为null,则继续
            while (!StringUtils.isBlank(secondHandlerId)){
                //6.【第二个bean对象】->11.【第三个bean对象】
                GatewayHandler secondGatewayHandler = SpringUtils.getBean(secondHandlerId, GatewayHandler.class);
                //7.【设置第一个bean对象的next为第二个bean对象】->12.【设置第二个bean对象的next为第三个bean对象】
                tmpHandler.setNextGatewayHandler(secondGatewayHandler);
                //8.【第二个实体】->13.【第三个实体】
                GatewayHandlerEntity secondHandlerEntity = gatewayHandlerMapper.getByHandler(secondHandlerId);
                if (secondHandlerEntity==null){ break; }
                //9.【第三个HandlerId】->14.【第四个HandlerId】此时为null,后面会退出循环
                secondHandlerId=secondHandlerEntity.getNextHandlerId();
                //10.【tmpHandler为第二个bean对象】->15.【tmpHandler为第三个bean对象】
                tmpHandler=secondGatewayHandler;
            }
            //16.赋值
            this.firstGatewayHandler=firstGatewayHandler;
            //17.返回第一个firstHandler对象
            return  firstGatewayHandler;
        }
    }

    数据库访问层

    GatewayHandlerMapper

    public interface GatewayHandlerMapper {
    
        /**
         * 获取第一个GatewayHandler
         */
        @Select("SELECT  handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid  FROM gateway_handler WHERE  prev_handler_id is null;;")
        public GatewayHandlerEntity getFirstGatewayHandler();
    
        @Select("SELECT  handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid   FROM gateway_handler WHERE  handler_id=#{handlerId}")
        public GatewayHandlerEntity getByHandler(String handlerId);
    
    }

    Entity层

    @Data
    public class GatewayHandlerEntity implements Serializable, Cloneable {
        /**
         * 主键ID
         */
        private Integer id;
        /**
         * handler名称
         */
        private String handlerName;
        /**
         * handler主键id
         */
        private String handlerId;
        /**
         * 下一个handler
         */
        private String nextHandlerId;
    
    }
    

    Controller层

    /**
     * @title: HandlerController
     */
    @RestController
    public class HandlerController {
        @Autowired
        private GatewayHandlerService gatewayHandlerService;
    
        @RequestMapping("/client")
        public String client() {
            GatewayHandler firstGatewayHandler = gatewayHandlerService.getFirstGatewayHandler();
            firstGatewayHandler.service();
            return "success";
        }
    }
    

    相关配置信息

    application.yml

    ###服务启动端口号
    server:
      port: 8080
    
    spring:
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/design_pattern?useUnicode=true&characterEncoding=UTF-8&useSSL=true
        username: root
        password: root
        driver-class-name: com.mysql.jdbc.Driver
    
    
    ####打印MyBatias日志
    logging:
      level:
        ### 开发环境使用DEBUG 生产环境info或者error
        com.xuyu.mapper: DEBUG
    

    Maven依赖信息

     <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.1.RELEASE</version>
        </parent>
        <dependencies>
            <!-- sprinboot web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
            </dependency>
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.1.1</version>
            </dependency>
            <!-- mysql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
        </dependencies>

    启动类

    /**
     * @title: AppHandler
     */
    @SpringBootApplication
    @MapperScan("com.xuyu.mapper")
    @EnableAutoConfiguration
    public class AppHandler {
        public static void main(String[] args) {
            SpringApplication.run(AppHandler.class);
            
        }
    }

    效果

    2019-05-09 23:33:33.136  INFO 40524 --- [nio-8080-exec-6] c.xuyu.handler.impl.CurrentLimitHandler  : 第一关 API接口限流操作.....
    2019-05-09 23:33:33.136  INFO 40524 --- [nio-8080-exec-6] com.xuyu.handler.impl.BlacklistHandler   : 第二关 黑名单拦截.......
    2019-05-09 23:33:33.136  INFO 40524 --- [nio-8080-exec-6] c.xuyu.handler.impl.ConversationHandler  : 第三关 用户的会
    特别声明:文章内容仅供参考,不造成任何投资建议。投资者据此操作,风险自担。

    网站首页 - 微服务 - 设计模式 - 并发编程 - 源码分析 - 中间件 - 数据库 - 文献 - 工具 - 其他

    本站不良内容举报联系客服QQ:483966038 官方微信:MYKT-xiaowei 服务热线:

    未经本站书面特别授权,请勿转载或建立镜像

    Copyright © 2016-2020 架构技术精选 版权所有 XMl地图