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
修改代码 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
额,忘了几个地方
前端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
鉴于2020其谷歌的恶劣表现,特别是youtube黄标恐怖袭击。我建议改成hcaptcha
该干嘛继续干
at 2020-04-04
13
Geetest 滑块也行?
无名
at 2020-04-04
14