文章
水区

2049增加Google验证码代码

参考文章 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

菜单
  1. 张怀义  

    额,忘了几个地方

    前端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 的代码就可以了

  2. 张怀义  

    修改代码 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)
    }
    
  3. 张怀义  

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

  4. 小二   默认开启批量屏蔽受限用户发言功能,可在设置中手动取消。

    多谢大牛

  5. 张怀义  

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

  6. origin  

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

  7. 张怀义  

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

  8. 小二   默认开启批量屏蔽受限用户发言功能,可在设置中手动取消。

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

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

    大神帮帮忙

  9. 张怀义  

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

  10. puf夏   ༼ ಠ ▃ ಠೃ ༽

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

  11. 小二   默认开启批量屏蔽受限用户发言功能,可在设置中手动取消。

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

  12. puf夏   ༼ ಠ ▃ ಠೃ ༽

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

  13. 该干嘛继续干  

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

  14. 无名  

    Geetest 滑块也行?