RichCMS支持防止恶意抓取的功能,可以在站点配置->安全中去打开设置。本文章可以看到RichCMS中防止恶意抓取的功能的Go语言实现,以下是源代码部分:

文件:/common/sdk/rees/filter/filter.go

ackage filter

import (
	"github.com/zituocn/gow"
	"github.com/zituocn/richcms/common/repository/model"
	"github.com/zituocn/richcms/common/repository/view"
)

// PluginExec  入口方法
func PluginExec(c *gow.Context, config *view.CrawlConfig) {
	if config == nil {
		return
	}
	ip := c.GetIP()
	if ip != "" && !ipWhiteList.Exist(ip) {
		// IP限流
		IPLimiter(c, ip, config.Open, config.Second, config.Tokens)

		// IP过滤
		IPFilter(c, ip, config.Open)
	}
}

func InitIPBox() {
	data, _ := new(model.IPBox).GetIPBoxList()
	InitIPBlackList(data)
	InitIPWhiteList(data)
}
  • PluginExec()函数,是一个入口函数,供RichCMS代码中的middleware部分调用。
  • InitIPBox()函数,是在程序启动时调用,实现初始化。

文件: /common/sdk/rees/filter/ip_filter.go

package filter

import (
	"github.com/zituocn/gow"
	"github.com/zituocn/logx"
	"github.com/zituocn/richcms/common/repository/enum"
	"github.com/zituocn/richcms/common/repository/model"
	"github.com/zituocn/richcms/common/sdk/rees/store"
)

var (

	// ipBlackList 黑名单
	ipBlackList *store.MemoryStore

	// ipWhiteList 白名单
	ipWhiteList *store.MemoryStore
)

// IPFilter ip过滤器
func IPFilter(c *gow.Context, ip string, open bool) {
	if !open {
		return
	}
	if ipBlackList.Exist(ip) {
		ResponsePage(c)
	}
}

func ResponsePage(c *gow.Context) {
	c.String("禁止访问")
	c.StopRun()
}

/*
白名单
*/

// InitIPWhiteList 初始化白名单IP
func InitIPWhiteList(data []*model.IPBox) {
	ipWhiteList = new(store.MemoryStore)
	i := 0
	for _, item := range data {
		if item.IP != "" && item.IPType == enum.WhiteIP {
			i++
			ipWhiteList.Store(item.IP)
		}
	}
	logx.Infof("Load IP whitelist :%d", i)
}

/*
黑名单
*/

// InitIPBlackList 初始化黑名单IP
func InitIPBlackList(data []*model.IPBox) {
	ipBlackList = new(store.MemoryStore)
	i := 0
	for _, item := range data {
		if item.IP != "" && item.IPType == enum.BanIP {
			i++
			ipBlackList.Store(item.IP)
		}
	}
	logx.Infof("Load IP blacklist :%d", i)
}
  • 此文章是IP过滤器,当已经进入黑名单或白名单的IP地址,会做分别拦截和放行的处理。
  • ResponsePage()函数的作用:当IP地址是黑名单时,向客户端响应“禁止访问”的信息。

文件:/common/sdk/rees/filter/ip_limiter.go

package filter

import (
	"github.com/zituocn/gow"
	"github.com/zituocn/gow/lib/limiter"
	"github.com/zituocn/richcms/common/repository/enum"
	"github.com/zituocn/richcms/common/repository/model"
	"github.com/zituocn/richcms/common/utils"
	"golang.org/x/time/rate"
	"sync"
	"time"
)

var (

	// IP 限流
	ipLimiter *limiter.IPLimiter
)

func newIPLimiter(r, b int) *limiter.IPLimiter {
	return limiter.NewIPLimiter(rate.Every(time.Duration(r)*time.Second), b)
}

// IPLimiter IP限流器
func IPLimiter(c *gow.Context, ip string, open bool, second, tokens int) {
	if !open {
		return
	}
	if ipLimiter == nil {
		ipLimiter = newIPLimiter(second, tokens)
	}
	limit := ipLimiter.GetLimiter(ip)
	if !limit.Allow() {
		banIP(ip)
		ResponsePage(c)
	}
}

// ReloadIPLimiter 重置 ipLimiter
func ReloadIPLimiter() {
	mu := &sync.Mutex{}
	mu.Lock()
	ipLimiter = nil
	mu.Unlock()
}

func banIP(v string) {
	if v == "" {
		return
	}

	ipBox := &model.IPBox{
		IP:      v,
		IPType:  enum.BanIP,
		Created: utils.NowUnixTime(),
	}

	//写数据到DB
	_ = ipBox.Create()
	ipBlackList.Store(v)
}
  • 此代码是IP限流器的实现,当达到配置的限量时,会进入黑名单。

在管理后台的工具菜单中,有“IP盒子”的功能,可以对白名单和进入系统的黑名单IP,进行管理。