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
张怀义 修改代码 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) }张怀义 额,忘了几个地方
前端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 的代码就可以了
小二默认开启批量屏蔽受限用户发言功能,可在设置中手动取消。 多谢大牛
该干嘛继续干 鉴于2020其谷歌的恶劣表现,特别是youtube黄标恐怖袭击。我建议改成hcaptcha
无名 Geetest 滑块也行?