注解實現策略模式


未經博主允許不得轉載:

  項目優化重構,需要對原有的開發進行優化,網關模塊的校驗存在多個不同類型的校驗,為了使業務更加區分的清楚,使用策略模式對網關的校驗進行區分。

其場景為:對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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM