引言
本文代码已提交至Github(版本号:
d439ec96b39dc0adf0d697cbc6bfc87c1c3b7dc8
),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop
在上一篇博客《淘东电商项目(67) -互联网安全架构设计(方法论)》,主要讲解了互联网安全架构设计的方法,主要介绍了如下几种:
- 基于网关实现IP黑名单与名单拦截
- API接口实现Token授权认证
- 使用MD5实现API接口验证签名,防止抓包篡改数据
- 实现API接口安全加密传输(公钥和私钥互换机制)
- 基于Oauth2.0 实现API接口开放平台
- 接口参数使用网关实现防止XSS、SQL注入
- 定期工具实现代码健康扫描
本文开始讲解具体的代码实现。
本文目录结构:
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.测试
浏览器访问:
数据库黑名单,改为允许访问:
可以看到能正常访问:
本文完!