2049增加Google验证码代码

By 张怀义 at 2020-04-01

参考文章 https://www.socketloop.com/tutorials/golang-recaptcha-example

1.去这里 http://www.google.com/recaptcha/ 申请secret key

2.修改代码

https://github.com/Terminus2049/2049bbs/blob/master/view/default/desktop/userlogin.html
<p><label>验证码: <input type="text" id="captchaSolution" class="sl w200" value="" /></label></p>

改为

<p><label>验证码: <div class="g-recaptcha" data-sitekey="[第一步申请的secret key]"></div></label></p>

另外 修改 form_post 函数

    function form_post(){
        var name = $('#name').val();
        var password = $('#password').val();
        var password2 = $('#password2').val();
        var g-recaptcha-response= $('#g-recaptcha-response').val();
        if(name && password && g-recaptcha-response){
            if(password && password2) {
                if(password != password2){
                    $.toast('密码两次输入不同');
                    $('#password').val('');
                    $('#password2').val('');
                    return false;
                }
            }

            $.ajax({
                type: "POST",
                url: "/{ {.Act} }",
                data: JSON.stringify({'act': '{ {.Act} }', 'name': name, 'password': md5(password), 'g-recaptcha-response': g-recaptcha-response}),
                dataType: "json",
                success: function(data){
                    if(data.retcode==200){
                        window.location.href = "/";
                    }else{
                            reload();
                        $.toast(data.retmsg);
                    }
                },
                fail: function(errMsg) {
                    $.toast(errMsg);
                }
            });
        }else{
            $.toast('用户名、密码和验证码必填');
        }
        return false;
    }

mobile版本,参考desktop继续修改 https://github.com/Terminus2049/2049bbs/blob/master/view/default/mobile/userlogin.html

2049, Google, 验证码, 代码, 增加


修改代码 https://github.com/Terminus2049/2049bbs/blob/df0914d35b4be3ccf6ae1e8da525b355dd54bd01/controller/user.go

登录的时候,访问 UserLogin 函数 修改如下

func (h *BaseHandler) UserLogin(w http.ResponseWriter, r *http.Request) {
	type pageData struct {
		PageData
		Act       string
		Token     string
		CaptchaId string
	}
	act := strings.TrimLeft(r.RequestURI, "/")
	title := "登录"
	if act == "register" {
		title = "注册"
	}

	tpl := h.CurrentTpl(r)
	evn := &pageData{}
	evn.SiteCf = h.App.Cf.Site
	evn.Title = title
	evn.Keywords = ""
	evn.Description = ""
	evn.IsMobile = tpl == "mobile"

	evn.ShowSideAd = true
	evn.PageName = "user_login_register"

	evn.Act = act

	token := h.GetCookie(r, "token")
	if len(token) == 0 {
		token := xid.New().String()
		h.SetCookie(w, "token", token, 1)
	}

	h.Render(w, tpl, evn, "layout.html", "userlogin.html")
}

提交代码修改如下

func (h *BaseHandler) UserLoginPost(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")

	token := h.GetCookie(r, "token")
	if len(token) == 0 {
		w.Write([]byte(`{"retcode":400,"retmsg":"token cookie missed"}`))
		return
	}

	act := strings.TrimLeft(r.RequestURI, "/")

	type recForm struct {
		Name            string `json:"name"`
		Password        string `json:"password"`
		Response       string `json:"g-recaptcha-response"`
	}

	type response struct {
		normalRsp
	}

	decoder := json.NewDecoder(r.Body)
	var rec recForm
	err := decoder.Decode(&rec)
	if err != nil {
		w.Write([]byte(`{"retcode":400,"retmsg":"json Decode err:` + err.Error() + `"}`))
		return
	}
	defer r.Body.Close()

	if len(rec.Name) == 0 || len(rec.Password) == 0 {
		w.Write([]byte(`{"retcode":400,"retmsg":"name or pw is empty"}`))
		return
	}
	nameLow := strings.ToLower(rec.Name)
	if !util.IsUserName(nameLow) {
		w.Write([]byte(`{"retcode":400,"retmsg":"name fmt err"}`))
		return
	}

                // did we get a proper recaptcha response? if null, redirect back to sigup page
                 if rec.Response == "" {
                         // user press submit button without passing reCAPTCHA test
                         // abort
                         http.Redirect(w, r, "/", 301)
                         return // return control to stop execution, otherwise it will continue
                 }

                 // get end user's IP address
                 remoteip, _, _ := net.SplitHostPort(r.RemoteAddr)

                 fmt.Println("remote ip : ", remoteip)

                 // to verify if the recaptcha is REAL. we must send
                 // secret + response + remoteip(optional) to postURL

                 secret := "[你的 secret key]"
                 postURL := "https://www.google.com/recaptcha/api/siteverify"

                 postStr := url.Values{"secret": {secret}, "response": {rec.Response}, "remoteip": {remoteip}}

                 responsePost, err := http.PostForm(postURL, postStr)

                 if err != nil {
                         fmt.Println(err)
                         return
                 }

                 defer responsePost.Body.Close()
                 body, err := ioutil.ReadAll(responsePost.Body)

                 if err != nil {
                         fmt.Println(err)
                         return
                 }

                 // this part is for server side verification
                 var APIResp JSONAPIResponse

                 json.Unmarshal(body, &APIResp)
                 fmt.Println(APIResp)

	db := h.App.Db
	timeStamp := uint64(time.Now().UTC().Unix())

	if act == "login" {
		bn := "user_login_token"
		key := []byte(token + ":loginerr")
		if db.Zget(bn, key).State == "ok" {
			// todo
			//w.Write([]byte(`{"retcode":400,"retmsg":"name and pw not match"}`))
			//return
		}
		uobj, err := model.UserGetByName(db, nameLow)
		if err != nil {
			w.Write([]byte(`{"retcode":405,"retmsg":"json Decode err:` + err.Error() + `","newCaptchaId":"` + captcha.New() + `"}`))
			return
		}
		if uobj.Password != rec.Password {
			db.Zset(bn, key, uint64(time.Now().UTC().Unix()))
			w.Write([]byte(`{"retcode":405,"retmsg":"name and pw not match","newCaptchaId":"` + captcha.New() + `"}`))
			return
		}
		sessionid := xid.New().String()
		uobj.LastLoginTime = timeStamp
		uobj.Session = sessionid
		jb, _ := json.Marshal(uobj)
		db.Hset("user", youdb.I2b(uobj.Id), jb)
		h.SetCookie(w, "SessionID", strconv.FormatUint(uobj.Id, 10)+":"+sessionid, 365)
	} else {
		// register
		siteCf := h.App.Cf.Site
		if siteCf.CloseReg {
			w.Write([]byte(`{"retcode":400,"retmsg":"stop to new register"}`))
			return
		}
		if db.Hget("user_name2uid", []byte(nameLow)).State == "ok" {
			w.Write([]byte(`{"retcode":405,"retmsg":"name is exist","newCaptchaId":"` + captcha.New() + `"}`))
			return
		}

		userId, _ := db.HnextSequence("user")
		flag := 5
		if siteCf.RegReview {
			flag = 1
		}

		if userId == 1 {
			flag = 99
		}

		uobj := model.User{
			Id:            userId,
			Name:          rec.Name,
			Password:      rec.Password,
			Flag:          flag,
			RegTime:       timeStamp,
			LastLoginTime: timeStamp,
			Session:       xid.New().String(),
		}

		// 从指定的用户中随机选一个头像作为新注册用户头像
		// 指定用户必须连续,取最小id和最大id

		rand.Seed(time.Now().UnixNano())
		min := siteCf.AvatarMinId // 2539
		max := siteCf.AvatarMaxId // 2558
		sampleID := rand.Intn(max-min+1) + min
		uidStr := strconv.FormatUint(uint64(sampleID), 10)
		uobj.Avatar = uidStr
		uobj.IgnoreLimitedUsers = true

		jb, _ := json.Marshal(uobj)
		db.Hset("user", youdb.I2b(uobj.Id), jb)
		db.Hset("user_name2uid", []byte(nameLow), youdb.I2b(userId))
		db.Hset("user_flag:"+strconv.Itoa(flag), youdb.I2b(uobj.Id), []byte(""))

		h.SetCookie(w, "SessionID", strconv.FormatUint(uobj.Id, 10)+":"+uobj.Session, 365)
	}

	h.DelCookie(w, "token")

	rsp := response{}
	rsp.Retcode = 200
	json.NewEncoder(w).Encode(rsp)
}
张怀义 at 2020-04-01
1

安全可靠,小二可以自己比对下我的修改 @小二 如果有任何问题,都可以@我

张怀义 at 2020-04-01
2

额,忘了几个地方

前端html 增加

               <script src='https://www.google.com/recaptcha/api.js'></script>

后端golang部分

 type JSONAPIResponse struct {
         Success     bool      `json:"success"`
         ChallengeTS time.Time `json:"challenge_ts"` // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
         Hostname    string    `json:"hostname"`     // the hostname of the site where the reCAPTCHA was solved
         ErrorCodes  []int     `json:"error-codes"`  //optional
 }

增加

                 json.Unmarshal(body, &APIResp)
                 fmt.Println(APIResp)

增加判断APIResp.Success 是true还是false 的代码就可以了

张怀义 at 2020-04-01
3

多谢大牛

小二 at 2020-04-01
4

@小二 #4 不知道能帮到多少忙

张怀义 at 2020-04-01
5

@小二 #4 @张怀义 #3 请问有没有可能上线帖子编辑功能,或者是如 stackoverflow 一样的错别字编辑功能呢?经常发完帖子之后发现帖子里面有错别字

origin at 2020-04-02
6

@yourmom #7 何苦认为i我是小二呢?我可能真的发高烧了。。。得隔离了,你暂时见不到我了

张怀义 at 2020-04-02
7

@张怀义 #5 就差一个 $LaTex$ 预览的痛点没有解决。

https://github.com/Terminus2049/2049bbs/issues/11

大神帮帮忙

小二 at 2020-04-02
8

@小二 #10 最近感觉不行。有空我去试试。我不是大神,你才是

张怀义 at 2020-04-02
9

@小二 #4 不能自建验证码么 ༼ ಠ ▃ ಠೃ ༽

puf夏 at 2020-04-02
10

@puf夏 #13 现在的验证码就是自建的。

小二 at 2020-04-03
11

@小二 #14 你不是调的谷歌验证?

puf夏 at 2020-04-03
12

鉴于2020其谷歌的恶劣表现,特别是youtube黄标恐怖袭击。我建议改成hcaptcha

该干嘛继续干 at 2020-04-04
13

Geetest 滑块也行?

无名 at 2020-04-04
14