github.com/godaddy-x/freego@v1.0.156/geetest/geetest_controller.go (about)

     1  package geetest
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/godaddy-x/freego/cache"
     8  	"github.com/godaddy-x/freego/ex"
     9  	"github.com/godaddy-x/freego/geetest/sdk"
    10  	"github.com/godaddy-x/freego/node"
    11  	"github.com/godaddy-x/freego/utils"
    12  	"github.com/godaddy-x/freego/zlog"
    13  	"io/ioutil"
    14  	"net/http"
    15  	"net/url"
    16  	"time"
    17  )
    18  
    19  var (
    20  	config = Config{}
    21  )
    22  
    23  type Config struct {
    24  	GeetestID  string `json:"geetestID"`
    25  	GeetestKey string `json:"geetestKey"`
    26  	Debug      bool   `json:"debug"`
    27  	Boundary   int    `json:"boundary"` // 界限值0 - 100
    28  }
    29  
    30  // 发送GET请求
    31  func httpGet(getURL string, params map[string]string) (string, error) {
    32  	q := url.Values{}
    33  	if params != nil {
    34  		for key, val := range params {
    35  			q.Add(key, val)
    36  		}
    37  	}
    38  	req, err := http.NewRequest(http.MethodGet, getURL, nil)
    39  	if err != nil {
    40  		return "", errors.New("NewRequest fail")
    41  	}
    42  	req.URL.RawQuery = q.Encode()
    43  	client := &http.Client{Timeout: time.Duration(5) * time.Second}
    44  	res, err := client.Do(req)
    45  	if err != nil {
    46  		return "", err
    47  	}
    48  	defer res.Body.Close()
    49  	body, err := ioutil.ReadAll(res.Body)
    50  	if err != nil {
    51  		return "", err
    52  	}
    53  	if res.StatusCode == 200 {
    54  		return string(body), nil
    55  	}
    56  	return "", nil
    57  }
    58  
    59  // 从geetest获取bypass状态
    60  func CheckServerStatus(conf Config) {
    61  	if len(conf.GeetestID) == 0 || len(conf.GeetestKey) == 0 {
    62  		return
    63  	}
    64  	config = conf
    65  	redisStatus := "fail"
    66  	for true {
    67  		params := make(map[string]string)
    68  		params["gt"] = config.GeetestID
    69  		resBody, err := httpGet(BYPASS_URL, params)
    70  		if resBody == "" {
    71  			redisStatus = "fail"
    72  		} else {
    73  			resMap := make(map[string]interface{})
    74  			err = json.Unmarshal([]byte(resBody), &resMap)
    75  			if err != nil {
    76  				redisStatus = "fail"
    77  			}
    78  			if resMap["status"] == "success" {
    79  				redisStatus = "success"
    80  			} else {
    81  				redisStatus = "fail"
    82  			}
    83  		}
    84  		client, err := cache.NewRedis()
    85  		if err != nil {
    86  			fmt.Println(err)
    87  			return
    88  		}
    89  		if err := client.Put(GEETEST_BYPASS_STATUS_KEY, redisStatus); err != nil {
    90  			fmt.Println(err)
    91  			return
    92  		}
    93  		if config.Debug {
    94  			fmt.Println("bypass状态已经获取并存入redis,当前状态为-", redisStatus)
    95  		}
    96  		time.Sleep(time.Duration(CYCLE_TIME) * time.Second)
    97  	}
    98  }
    99  
   100  // 获取redis缓存的bypass状态
   101  func GetBypassCache() (status string) {
   102  	client, err := cache.NewRedis()
   103  	if err != nil {
   104  		zlog.Error("cache client failed", 0, zlog.AddError(err))
   105  		return ""
   106  	}
   107  	status, err = client.GetString(GEETEST_BYPASS_STATUS_KEY)
   108  	if err != nil {
   109  		zlog.Error("cache client getString failed", 0, zlog.AddError(err))
   110  		return ""
   111  	}
   112  	return status
   113  }
   114  
   115  // 验证初始化接口,GET请求
   116  func FirstRegister(ctx *node.Context) (sdk.GeetestLibResultData, error) {
   117  	/*
   118  		   必传参数
   119  		       digestmod 此版本sdk可支持md5、sha256、hmac-sha256,md5之外的算法需特殊配置的账号,联系极验客服
   120  		   自定义参数,可选择添加
   121  			   user_id 客户端用户的唯一标识,确定用户的唯一性;作用于提供进阶数据分析服务,可在register和validate接口传入,不传入也不影响验证服务的使用;若担心用户信息风险,可作预处理(如哈希处理)再提供到极验
   122  			   client_type 客户端类型,web:电脑上的浏览器;h5:手机上的浏览器,包括移动应用内完全内置的web_view;native:通过原生sdk植入app应用的方式;unknown:未知
   123  			   ip_address 客户端请求sdk服务器的ip地址
   124  	*/
   125  	filterObject, err := GetFilterObject(ctx)
   126  	if err != nil {
   127  		return sdk.GeetestLibResultData{}, ex.Throw{Msg: err.Error()}
   128  	}
   129  	client, err := cache.NewRedis()
   130  	if err != nil {
   131  		return sdk.GeetestLibResultData{}, ex.Throw{Msg: err.Error()}
   132  	}
   133  	// 判定是否触发验证机制, 如已经有触发验证则不能跳过, 1-100范围,命中数值>Boundary则不触发验证
   134  	_, status := ValidStatusCode(filterObject, 1)
   135  	if (len(config.GeetestID) == 0 && len(config.GeetestKey) == 0) || (status != 1 && utils.CheckRangeInt(config.Boundary, 1, 100) && utils.ModRand(100) > config.Boundary) {
   136  		if err := client.Put(utils.AddStr("geetest.", filterObject), 2, 1800); err != nil {
   137  			return sdk.GeetestLibResultData{}, ex.Throw{Msg: err.Error()}
   138  		}
   139  		return sdk.GeetestLibResultData{Status: 1}, nil
   140  	}
   141  	digestmod := "hmac-sha256"
   142  	params := map[string]string{
   143  		"digestmod":   digestmod,
   144  		"user_id":     filterObject,
   145  		"client_type": "web",
   146  		"ip_address":  ctx.RequestCtx.LocalAddr().String(),
   147  	}
   148  	gtLib := sdk.NewGeetestLib(config.GeetestID, config.GeetestKey, config.Debug)
   149  	var result *sdk.GeetestLibResult
   150  	if GetBypassCache() == "success" {
   151  		result = gtLib.Register(digestmod, params)
   152  	} else {
   153  		result = gtLib.LocalRegister()
   154  	}
   155  	if err := client.Put(utils.AddStr("geetest.", filterObject), 1, 1800); err != nil {
   156  		return sdk.GeetestLibResultData{}, ex.Throw{Msg: err.Error()}
   157  	}
   158  	bs := utils.Str2Bytes(result.Data)
   159  	return sdk.GeetestLibResultData{
   160  		Challenge:  utils.GetJsonString(bs, "challenge"),
   161  		Gt:         utils.GetJsonString(bs, "gt"),
   162  		NewCaptcha: utils.GetJsonBool(bs, "new_captcha"),
   163  		Success:    utils.GetJsonInt(bs, "success"),
   164  	}, nil
   165  }
   166  
   167  // 二次验证接口,POST请求
   168  func SecondValidate(ctx *node.Context) (map[string]string, error) {
   169  	filterObject, err := GetFilterObject(ctx)
   170  	if err != nil {
   171  		return nil, ex.Throw{Msg: err.Error()}
   172  	}
   173  	b, status := ValidStatusCode(filterObject, 1)
   174  	if !b {
   175  		return nil, ex.Throw{Msg: "validator not initialized"}
   176  	}
   177  	if status == 2 {
   178  		return map[string]string{"result": "success"}, nil
   179  	}
   180  	challenge := utils.GetJsonString(ctx.JsonBody.RawData(), "geetest_challenge")
   181  	validate := utils.GetJsonString(ctx.JsonBody.RawData(), "geetest_validate")
   182  	seccode := utils.GetJsonString(ctx.JsonBody.RawData(), "geetest_seccode")
   183  	if len(challenge) == 0 || len(validate) == 0 || len(seccode) == 0 {
   184  		return nil, ex.Throw{Msg: "challenge/validate/seccode parameters is nil"}
   185  	}
   186  	gtLib := sdk.NewGeetestLib(config.GeetestID, config.GeetestKey, config.Debug)
   187  	bypassStatus := GetBypassCache()
   188  	var result *sdk.GeetestLibResult
   189  	if bypassStatus == "success" {
   190  		result = gtLib.SuccessValidate(challenge, validate, seccode)
   191  	} else {
   192  		result = gtLib.FailValidate(challenge, validate, seccode)
   193  	}
   194  	if result.Status != 1 {
   195  		return nil, ex.Throw{Msg: result.Msg}
   196  	}
   197  	client, err := cache.NewRedis()
   198  	if err != nil {
   199  		return nil, ex.Throw{Msg: err.Error()}
   200  	}
   201  	if err := client.Put(utils.AddStr("geetest.", filterObject), 2, 1800); err != nil { // 设置验证成功状态
   202  		return nil, ex.Throw{Msg: err.Error()}
   203  	}
   204  	return map[string]string{"result": "success"}, nil
   205  }
   206  
   207  func GetFilterObject(ctx *node.Context) (string, error) {
   208  	filterMethod := utils.GetJsonString(ctx.JsonBody.RawData(), "filterMethod")
   209  	filterObject := utils.GetJsonString(ctx.JsonBody.RawData(), "filterObject")
   210  	if len(filterMethod) == 0 || len(filterObject) == 0 {
   211  		return "", utils.Error("filterMethod or filterObject is nil")
   212  	}
   213  	return CreateFilterObject(filterMethod, filterObject), nil
   214  }
   215  
   216  func CreateFilterObject(filterMethod, filterObject string) string {
   217  	return utils.MD5(utils.AddStr(filterMethod, filterObject))
   218  }
   219  
   220  // 验证状态码
   221  func ValidStatusCode(filterObject string, statusCode int64) (bool, int64) {
   222  	client, err := cache.NewRedis()
   223  	if err != nil {
   224  		return false, 0
   225  	}
   226  	status, err := client.GetInt64(utils.AddStr("geetest.", filterObject))
   227  	if err != nil {
   228  		return false, 0
   229  	}
   230  	if statusCode == 1 { // 是否可以进行二次验证
   231  		return status == 1 || status == 2, status
   232  	}
   233  	if statusCode == 2 { // 是否可以验证成功状态
   234  		return status == statusCode, status
   235  	}
   236  	return false, 0
   237  }
   238  
   239  // 验证完成状态
   240  func ValidSuccess(filterObject string) bool {
   241  	b, _ := ValidStatusCode(filterObject, 2)
   242  	return b
   243  }
   244  
   245  // 清除验证状态
   246  func CleanStatus(filterObject string) error {
   247  	client, err := cache.NewRedis()
   248  	if err != nil {
   249  		return ex.Throw{Msg: err.Error()}
   250  	}
   251  	if err := client.Del(utils.AddStr("geetest.", filterObject)); err != nil {
   252  		return ex.Throw{Msg: err.Error()}
   253  	}
   254  	return nil
   255  }