淘東電商項目(68) -互聯網安全架構設計(黑名單攔截及MD5加簽)

引言

本文代碼已提交至Github(版本號:d439ec96b39dc0adf0d697cbc6bfc87c1c3b7dc8),有興趣的同學可以下載來看看:https://github.com/ylw-github/taodong-shop

在上一篇博客《淘東電商項目(67) -互聯網安全架構設計(方法論)》,主要講解了互聯網安全架構設計的方法,主要介紹瞭如下幾種:

  1. 基於網關實現IP黑名單與名單攔截
  2. API接口實現Token授權認證
  3. 使用MD5實現API接口驗證簽名,防止抓包篡改數據
  4. 實現API接口安全加密傳輸(公鑰和私鑰互換機制)
  5. 基於Oauth2.0 實現API接口開放平臺
  6. 接口參數使用網關實現防止XSS、SQL注入
  7. 定期工具實現代碼健康掃描

本文開始講解具體的代碼實現。

本文目錄結構:
l____引言
l____ 1.實現思路
l____ 2.代碼實現
l________ 2.1 數據庫代碼
l________ 2.2 建造者模式
l________ 2.3 建造者模式使用
l____ 3.測試

1.實現思路

在這裏插入圖片描述
在上圖,可以看到,外網訪問時會經過我們的網關,如果我們需要對訪問者做出安全措施,必須在網關上做處理,如:“ip黑名單處理”、“MD5加簽處理”等。

在SpringCloud中,這些處理均在zuul網關過濾器中處理,僞代碼如下:

public class GatewayFilter extends ZuulFilter {

	@Override
	public Object run() throws ZuulException {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		HttpServletResponse response = ctx.getResponse();
		response.setContentType("UTF-8");
		// TODO 1.驗證ip地址是否在黑名單裏,如果是則拒絕訪問
		// TODO 2.通過MD5驗籤,判斷參數是否被篡改
		// TODO 3.其它......
		return null;
	}

可以看到上面的代碼是一步一步去處理的,其實是可以優化這種寫法的,本文用到“建造者模式”。

2.代碼實現

2.1 數據庫代碼

先貼上黑名單表的sql:

CREATE TABLE `blacklist` (
  `ID` int(11) NOT NULL COMMENT '主鍵id',
  `IP_ADDRES` varchar(255) DEFAULT NULL COMMENT 'ip地址',
  `RESTRICTION_TYPE` varchar(255) DEFAULT NULL COMMENT '限制類型',
  `AVAILABILITY` varchar(255) DEFAULT NULL COMMENT '是否可用',
  `REVISION` int(11) DEFAULT NULL COMMENT '樂觀鎖',
  `CREATED_BY` varchar(32) DEFAULT NULL COMMENT '創建人',
  `CREATED_TIME` datetime DEFAULT NULL COMMENT '創建時間',
  `UPDATED_BY` varchar(32) DEFAULT NULL COMMENT '更新人',
  `UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新時間',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2 建造者模式

建造者模式之前我的博客有講解過,有興趣的童鞋可以參閱:

2.3 建造者模式使用

①定義接口:

/**
 * description: 網關行爲建造者
 * create by: YangLinWei
 * create time: 2020/5/20 9:09 上午
 */
public interface GatewayBuild {

	/**
	 * 黑名單攔截
	 */
	Boolean blackBlock(RequestContext ctx, String ipAddres, HttpServletResponse response);

	/**
	 * 參數驗證
	 */
	Boolean toVerifyMap(RequestContext ctx, String ipAddres, HttpServletRequest request);

}

②接口實現:

/**
 * description: 參數驗證
 * create by: YangLinWei
 * create time: 2020/5/20 9:09 上午
 */
@Slf4j
@Component
public class VerificationBuild implements GatewayBuild {
	@Autowired
	private BlacklistMapper blacklistMapper;

	@Override
	public Boolean blackBlock(RequestContext ctx, String ipAddres, HttpServletResponse response) {
		// 2.查詢數據庫黑名單
		Blacklist meiteBlacklist = blacklistMapper.findBlacklist(ipAddres);
		if (meiteBlacklist != 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;
	}

	private void resultError(RequestContext ctx, String errorMsg) {
		ctx.setResponseStatusCode(401);
		ctx.setSendZuulResponse(false);
		ctx.setResponseBody(errorMsg);

	}
}

③定義組裝類:

@Component
public class GatewayDirector {
	@Resource(name = "verificationBuild")
	private GatewayBuild gatewayBuild;

	public void direcot(RequestContext ctx, String ipAddres, HttpServletResponse response, HttpServletRequest request) {
		/**
		 * 黑名單攔截
		 */
		Boolean blackBlock = gatewayBuild.blackBlock(ctx, ipAddres, response);
		if (!blackBlock) {
			return;
		}
		/**
		 * 參數驗證
		 */
		Boolean verifyMap = gatewayBuild.toVerifyMap(ctx, ipAddres, request);
		if (!verifyMap) {
			return;
		}
	}

}

④過濾器裏使用:

/**
 * description: 網關攔截
 * create by: YangLinWei
 * create time: 2020/5/20 9:10 上午
 */
@Component
@Slf4j
public class GatewayFilter extends ZuulFilter {

	@Autowired
	private GatewayDirector gatewayDirector;

	@Override
	public Object run() throws ZuulException {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		HttpServletResponse response = ctx.getResponse();
		response.setContentType("UTF-8");
		// 1.獲取ip地址
		String ipAddres = getIpAddr(request);
		if (StringUtils.isEmpty(ipAddres)) {
			resultError(ctx, "未能夠獲取到ip地址");
		}
		gatewayDirector.direcot(ctx, ipAddres, response, request);
		return null;
	}

	private void resultError(RequestContext ctx, String errorMsg) {
		ctx.setResponseStatusCode(401);
		ctx.setSendZuulResponse(false);
		ctx.setResponseBody(errorMsg);

	}

	@Override
	public boolean shouldFilter() {

		return true;
	}

	@Override
	public int filterOrder() {
		return 0;
	}

	/**
	 * 在方法之前攔截
	 * 
	 * @return
	 */
	@Override
	public String filterType() {

		return "pre";
	}

	/**
	 * 獲取Ip地址
	 * 
	 * @param request
	 * @return
	 */
	public String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("X-Forwarded-For");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

}

④application.yml配置過濾的地址:

###服務啓動端口號
server:
  port: 80
###服務註冊到eureka地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8100/eureka
###服務名稱(服務註冊到eureka名稱)
spring:
  application:
    name:  taodong-shop-basics-zuul
  ###數據庫相關連接
  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/taodong-gateway?useUnicode=true&characterEncoding=utf8&useSSL=false
### 配置網關反向代理
zuul:
  routes:
    api-a:
      ### 以 /api-weixin/訪問轉發到會員服務
      path: /api-weixin/**
      serviceId: taodong-shop-service-weixin
    api-b:
      ### 以 /api-member/訪問轉發到訂單服務
      path: /api-member/**
      serviceId: taodong-shop-service-member
    api-c:
      ### 以 /api-member/訪問轉發到訂單服務
      path: /api-pay/**
      serviceId: taodong-shop-service-pay

taodong-shop:
  zuul:
    swagger:
      document: '[ { "name": "taodong-shop-service-member", "location": "/taodong-shop-service-member/v2/api-docs",
                "version": "2.0" }, { "name": "taodong-shop-service-weixin", "location":
                "/taodong-shop-service-weixin/v2/api-docs", "version": "2.0" } ]'
### mybatis 日誌打印
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    com.ylw.basics.zuul.mapper: debug

3.測試

瀏覽器訪問:
http://127.0.0.1/api-pay/cratePayToken?payAmount=8882&orderId=20200513141452&userId=27&productName=玉米香腸,可以看到被攔截了。
在這裏插入圖片描述
數據庫黑名單,改爲允許訪問:
在這裏插入圖片描述
可以看到能正常訪問:
在這裏插入圖片描述

本文完!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章