zoukankan      html  css  js  c++  java
  • 注解实现策略模式

    未经博主允许不得转载:

      项目优化重构,需要对原有的开发进行优化,网关模块的校验存在多个不同类型的校验,为了使业务更加区分的清楚,使用策略模式对网关的校验进行区分。

    其场景为:对app1校验会话token,对app2 校验appid以及请求的签名,对管理台校验防重放攻击,校验nonce,时间戳等,同时为了以后进行业务的可扩展性,使用

    注解实现策略模式。

      由于在网关模块中使用策略模式,为了提高代码的可读性,使用模板模式,便于代码阅读。

      1.定义策略校验的枚举配置:

    package com.example.demo.constant;
    
    public enum AuthStrategyEnum {
    
        PORTAL("portal"),
    
        WEI_XIN("weixin"),
    
        ALI("ali");
    
        private String type;
    
        AuthStrategyEnum(String portal) {
        }
    
        public String getType(){
            return type;
        }
    }

      2.定义策略使用的注解

    package com.example.demo.config;
    
    import com.example.demo.constant.AuthStrategyEnum;
    
    import java.lang.annotation.*;
    
    @Target(ElementType.TYPE)  //作用在类上
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited  //子类可以继承此注解
    public @interface HandlerAuthStrategy {
    
        /**
         * 策略类型
         * @return
         */
        AuthStrategyEnum value();
    }

      3.定义获取策略类型的容器配置

    package com.example.demo.config;
    
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class AuthStrategyTypeConfig {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        //存放所有策略类Bean的map
        public static Map<String, Class<AuthStrategyService>> strategyBeanMap= new HashMap();
    
        /**
         * 根据类型获取校验类型的bean
         * @param type
         * @return
         */
        public AuthStrategyService getAuthStrategy(String type){
            Class<AuthStrategyService> strategyClass = strategyBeanMap.get(type);
            if(strategyClass==null) throw new IllegalArgumentException("没有对应的校验类型");
            //从容器中获取对应的策略Bean
            return applicationContext.getBean(strategyClass);
        }
    
    }

      4.在spring启动加载时,对使用策略注解的bean保存到map中,便于使用的时候直接获取

    package com.example.demo.config;
    
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    import java.util.Map;
    
    /**
     * 该类用于在spring启动加载时,对使用策略注解的bean保存到map中,便于使用的时候直接获取
     */
    public class HandlerAuthProcessor implements ApplicationContextAware {
    
        /**
         * 获取所有的策略Beanclass 加入HandlerOrderContext属性中
         * @param applicationContext
         * @throws BeansException
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            //获取所有策略注解的Bean
            Map<String, Object> orderStrategyMap = applicationContext.getBeansWithAnnotation(HandlerAuthStrategy.class);
            orderStrategyMap.forEach((k,v)->{
                Class<AuthStrategyService> orderStrategyClass = (Class<AuthStrategyService>) v.getClass();
                String type = orderStrategyClass.getAnnotation(HandlerAuthStrategy.class).value().getType();
                //将class加入map中,type作为key
                AuthStrategyTypeConfig.strategyBeanMap.put(type,orderStrategyClass);
            });
        }
    }

      5.定义策略实现的bean的接口,采用jdk8 提供的新特性,接口可以进行默认实现。

    package com.example.demo.service;
    
    /**
     * 用于定义校验的方法
     */
    public interface AuthStrategyService {
        /**
         * 校验nonce值
         */
        default void checkNonce(String nonce){
            // 默认实现校验nonce值
        }
    
        /**
         * 校验时间戳
         * @param timeStamp
         */
        default void checkTimeStamp(long timeStamp){
            // 默认实现校验时间戳
        }
    
        /**
         * 校验appId
         * @param appId
         */
        default void checkAppId(String appId){
            // 默认实现
            // todo查询数据库是否存在
        }
    
        /**
         * 校验token
         */
        void checkToken(String token);
    }

      6.对不同的策略类型进行业务实现。如果接口有默认实现,则可以直接使用,将独有的校验在自己的内部进行实现。并添加策略的注解。

    package com.example.demo.service.impl;
    
    import com.example.demo.config.HandlerAuthStrategy;
    import com.example.demo.constant.AuthStrategyEnum;
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.stereotype.Service;
    
    /**
     * 阿里接入校验
     */
    @Service
    @HandlerAuthStrategy(value = AuthStrategyEnum.ALI)
    public class AliPayAuthStrategyServiceImpl implements AuthStrategyService {
        @Override
        public void checkToken(String token) {
            // 通过第三方的服务进行token校验
        }
    }
    package com.example.demo.service.impl;
    
    import com.example.demo.config.HandlerAuthStrategy;
    import com.example.demo.constant.AuthStrategyEnum;
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.stereotype.Service;
    
    /**
     * 管理台网关校验实现
     */
    @Service
    @HandlerAuthStrategy(value = AuthStrategyEnum.PORTAL)
    public class PortalAuthStrategyServiceImpl implements AuthStrategyService {
        @Override
        public void checkAppId(String appId) {
        }
    
        @Override
        public void checkToken(String token) {
            // 管理台不需要校验token
        }
    }
    package com.example.demo.service.impl;
    
    import com.example.demo.config.HandlerAuthStrategy;
    import com.example.demo.constant.AuthStrategyEnum;
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.stereotype.Service;
    
    /**
     * 微信app校验
     */
    @Service
    @HandlerAuthStrategy(value = AuthStrategyEnum.WEI_XIN)
    public class WeixinAuthStrategyServiceImpl implements AuthStrategyService {
        @Override
        public void checkToken(String token) {
            // jwt解析校验token有效性
        }
    }

      7.在过滤器中采用模版+策略的方式进行接口认证校验

    package com.example.demo.filter;
    
    import com.example.demo.config.AuthStrategyTypeConfig;
    import com.example.demo.mapper.AuthRequestMapper;
    import com.example.demo.service.AuthStrategyService;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.servlet.http.HttpServletRequest;
    
    public class AuthFilter {
    
        @Autowired
        private AuthStrategyTypeConfig authStrategyTypeConfig;
    
        @Autowired
        private AuthRequestMapper dbMapper;
    
        public void filter(HttpServletRequest request){
            // 解析当前请求的路径
            String requestPath = request.getRequestURI();
            String appId = request.getHeader("appId");
            String nonce = request.getHeader("nonce");
            String timestamp = request.getHeader("timestamp");
            String token = request.getHeader("token");
            // 从数据库查询app对应的接口的配置策略类型
            String strategyType = dbMapper.getAuthStrategy(requestPath,appId);
            // 根据type获取当前的校验类型
            AuthStrategyService service = authStrategyTypeConfig.getAuthStrategy(strategyType);
            // 配置校验模版。会根据配置的策略bean进行不同的校验,
            // 接口定义的时候,对共有的校验则进行默认实现,只需要对独有的校验独自实现即可
            service.checkAppId(appId);
            service.checkNonce(nonce);
            service.checkAppId(timestamp);
            service.checkToken(token);
        }
    }
  • 相关阅读:
    魔法跳舞链 51Nod
    反射
    JDBC---后端服务器与数据库交互的桥梁
    多线程
    IO流
    继承与重写
    java.util包
    多态
    Java.lang包
    异常
  • 原文地址:https://www.cnblogs.com/zjdxr-up/p/14460720.html
Copyright © 2011-2022 走看看