用户注册登录与鉴权
本文主要介绍账号系统的相关功能
1.用户注册登录
2.session鉴权
3.用户数据隔离

用户表:
-- 创建用户表
CREATE TABLE `tbl_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
`user_pwd` varchar(256) NOT NULL DEFAULT '' COMMENT '用户encoded密码',
`email` varchar(64) DEFAULT '' COMMENT '邮箱',
`phone` varchar(128) DEFAULT '' COMMENT '手机号',
`email_validated` tinyint(1) DEFAULT 0 COMMENT '邮箱是否已验证',
`phone_validated` tinyint(1) DEFAULT 0 COMMENT '手机号是否已验证',
`signup_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '注册日期',
`last_active` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后活跃时间戳',
`profile` text COMMENT '用户属性',
`status` int(11) NOT NULL DEFAULT '0' COMMENT '账户状态(启用/禁用/锁定/标记删除等)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`user_name`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
用户注册
func UserSignup(username string, passwd string) bool {
stmt, err := mydb.DBConn().Prepare(
"insert ignore into tbl_user (`user_name`,`user_pwd`) values (?,?)")
if err != nil {
fmt.Println("Failed to insert, err:" + err.Error())
return false
}
defer stmt.Close()
ret, err := stmt.Exec(username, passwd)
if err != nil {
fmt.Println("Failed to insert, err:" + err.Error())
return false
}
if rowsAffected, err := ret.RowsAffected(); nil == err && rowsAffected > 0 {
return true
}
return false
}
处理http接口,用户注册
使用SHA1对密码进行加盐及取Sha1值加密
func SignupHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
// data, err := ioutil.ReadFile("./static/view/signup.html")
// if err != nil {
// w.WriteHeader(http.StatusInternalServerError)
// return
// }
// w.Write(data)
http.Redirect(w, r, "/static/view/signup.html", http.StatusFound)
return
}
r.ParseForm()
username := r.Form.Get("username")
passwd := r.Form.Get("password")
if len(username) < 3 || len(passwd) < 5 {
w.Write([]byte("Invalid parameter"))
return
}
// 对密码进行加盐及取Sha1值加密
encPasswd := util.Sha1([]byte(passwd + pwdSalt))
// 将用户信息注册到用户表中
suc := dblayer.UserSignup(username, encPasswd)
if suc {
w.Write([]byte("SUCCESS"))
} else {
w.Write([]byte("FAILED"))
}
}
登录服务:
-
判断请求方法(GET 请求处理):
if r.Method == http.MethodGet { http.Redirect(w, r, "/static/view/signin.html", http.StatusFound) return }- 如果请求方法是
GET,则用户是请求登录页面。代码中使用http.Redirect函数将请求重定向到登录页面signin.html。使用http.StatusFound(302 状态码)进行临时重定向。
- 如果请求方法是
-
处理 POST 请求:
- 当请求方法是
POST,意味着用户提交了登录表单,服务器开始处理登录逻辑。
- 当请求方法是
-
解析表单数据:
r.ParseForm() username := r.Form.Get("username") password := r.Form.Get("password")- 使用
r.ParseForm()解析表单数据,然后从表单中提取出username和password。
- 使用
-
对密码进行加密:
encPasswd := util.Sha1([]byte(password + pwdSalt))- 通过
util.Sha1()对用户输入的密码进行加密(假设pwdSalt是一个全局常量或配置,用于密码加盐)。加盐是为了增强密码的安全性,防止被简单破解。
- 通过
-
校验用户名及密码:
pwdChecked := dblayer.UserSignin(username, encPasswd) if !pwdChecked { w.Write([]byte("FAILED")) return }- 调用
dblayer.UserSignin()函数,将用户名和加密后的密码传递给数据库层进行验证。 - 如果验证失败(返回
false),则直接返回"FAILED"响应,并终止后续操作。
- 调用
-
生成访问凭证 (token):
token := GenToken(username) upRes := dblayer.UpdateToken(username, token) if !upRes { w.Write([]byte("FAILED")) return }- 调用
GenToken(username)函数生成一个与用户相关的 token,用于后续的用户身份验证或授权。 - 将生成的 token 存储到数据库中,调用
dblayer.UpdateToken()更新用户的 token。如果更新失败,再次返回"FAILED"并终止流程。
- 调用
-
登录成功后的响应:
resp := util.RespMsg{ Code: 0, Msg: "OK", Data: struct { Location string Username string Token string }{ Location: "http://" + r.Host + "/static/view/home.html", Username: username, Token: token, }, } w.Write(resp.JSONBytes())- 当用户名密码校验成功并生成 token 后,返回一个结构化的 JSON 响应。
RespMsg是一个自定义的结构体,包含Code(表示状态码,0为成功)、Msg(成功消息),以及Data字段(包含登录成功后的跳转位置、用户名和 token)。 - 调用
resp.JSONBytes()将该响应转换为 JSON 格式,并写入到 HTTP 响应中。
- 当用户名密码校验成功并生成 token 后,返回一个结构化的 JSON 响应。
总结:
- GET 请求:重定向到登录页面。
- POST 请求:
- 解析用户提交的表单数据;
- 验证用户名和加密后的密码;
- 生成并更新用户 token;
- 登录成功后,返回包含 token 和跳转地址的 JSON 响应。
用户信息查询接口:
说明:除了登录注册接口,其他接口都需要先调用登录接口,获取token,然后才能访问其他接口。
流程说明:
-
解析请求参数:
r.ParseForm() username := r.Form.Get("username")- 通过
r.ParseForm()解析请求中的表单参数,然后从表单中提取出username字段。 - 此步骤获取客户端传递的用户名,用于后续查询用户信息。
- 通过
-
Token 验证(注释掉的部分):
// token := r.Form.Get("token") // isValidToken := IsTokenValid(token) // if !isValidToken { // w.WriteHeader(http.StatusForbidden) // return // }- 此部分代码被注释,原本的逻辑是从请求参数中提取
token,并通过IsTokenValid()函数验证 token 的有效性。 - 如果 token 无效,服务器将返回 HTTP 403 禁止访问(
http.StatusForbidden),表示用户无权限获取信息。 - 考虑节省资源每个接口不会都进行token验证
- 此部分代码被注释,原本的逻辑是从请求参数中提取
-
查询用户信息:
user, err := dblayer.GetUserInfo(username) if err != nil { w.WriteHeader(http.StatusForbidden) return }- 调用
dblayer.GetUserInfo(username)从数据库中获取与该用户名对应的用户信息。 - 如果查询失败(
err != nil),服务器返回 HTTP 403 禁止访问(http.StatusForbidden),表示用户信息不可用或不存在。
- 调用
-
组装并响应用户数据:
resp := util.RespMsg{ Code: 0, Msg: "OK", Data: user, } w.Write(resp.JSONBytes())- 组装一个
RespMsg结构体,包含响应的状态码(Code: 0表示成功)、消息(Msg: "OK")、以及用户数据(Data: user)。 resp.JSONBytes()将该响应转换为 JSON 格式,并将其写入到 HTTP 响应中,返回给客户端。
- 组装一个
总结:
- 请求参数解析:从请求中获取用户名(以及注释掉的 token)信息。
- (注释掉的)Token 验证:原本应该验证用户请求的合法性,确保 token 有效。
- 查询用户信息:通过数据库层查询与用户名对应的用户信息,查询失败则返回 403 禁止访问。
- 返回用户数据:组装 JSON 格式的用户信息并返回给客户端。
访问鉴权接口
func HTTPInterceptor(h http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
token := r.Form.Get("token")
//验证登录token是否有效
if len(username) < 3 || !IsTokenValid(token) {
// w.WriteHeader(http.StatusForbidden)
// token校验失败则跳转到登录页面
http.Redirect(w, r, "/static/view/signin.html", http.StatusFound)
return
}
h(w, r)
})
}
在main.go中使用了 handler.HTTPInterceptor 函数来包装多个 HTTP 处理函数。
// 文件存取接口
http.HandleFunc("/file/upload", handler.HTTPInterceptor(handler.UploadHandler))
http.HandleFunc("/file/upload/suc", handler.HTTPInterceptor(handler.UploadSucHandler))
http.HandleFunc("/file/meta", handler.HTTPInterceptor(handler.GetFileMetaHandler))
http.HandleFunc("/file/query", handler.HTTPInterceptor(handler.FileQueryHandler))
http.HandleFunc("/file/download", handler.HTTPInterceptor(handler.DownloadHandler))
http.HandleFunc("/file/update", handler.HTTPInterceptor(handler.FileMetaUpdateHandler))
http.HandleFunc("/file/delete", handler.HTTPInterceptor(handler.FileDeleteHandler))
//.....

浙公网安备 33010602011771号