用户注册登录与鉴权

本文主要介绍账号系统的相关功能
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"))
	}
}

登录服务:

  1. 判断请求方法(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 状态码)进行临时重定向。
  2. 处理 POST 请求

    • 当请求方法是 POST,意味着用户提交了登录表单,服务器开始处理登录逻辑。
  3. 解析表单数据

    r.ParseForm()
    username := r.Form.Get("username")
    password := r.Form.Get("password")
    
    • 使用 r.ParseForm() 解析表单数据,然后从表单中提取出 usernamepassword
  4. 对密码进行加密

    encPasswd := util.Sha1([]byte(password + pwdSalt))
    
    • 通过 util.Sha1() 对用户输入的密码进行加密(假设 pwdSalt 是一个全局常量或配置,用于密码加盐)。加盐是为了增强密码的安全性,防止被简单破解。
  5. 校验用户名及密码

    pwdChecked := dblayer.UserSignin(username, encPasswd)
    if !pwdChecked {
        w.Write([]byte("FAILED"))
        return
    }
    
    • 调用 dblayer.UserSignin() 函数,将用户名和加密后的密码传递给数据库层进行验证。
    • 如果验证失败(返回 false),则直接返回 "FAILED" 响应,并终止后续操作。
  6. 生成访问凭证 (token)

    token := GenToken(username)
    upRes := dblayer.UpdateToken(username, token)
    if !upRes {
        w.Write([]byte("FAILED"))
        return
    }
    
    • 调用 GenToken(username) 函数生成一个与用户相关的 token,用于后续的用户身份验证或授权。
    • 将生成的 token 存储到数据库中,调用 dblayer.UpdateToken() 更新用户的 token。如果更新失败,再次返回 "FAILED" 并终止流程。
  7. 登录成功后的响应

    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 响应中。

总结:

  1. GET 请求:重定向到登录页面。
  2. POST 请求
    • 解析用户提交的表单数据;
    • 验证用户名和加密后的密码;
    • 生成并更新用户 token;
    • 登录成功后,返回包含 token 和跳转地址的 JSON 响应。

用户信息查询接口:

说明:除了登录注册接口,其他接口都需要先调用登录接口,获取token,然后才能访问其他接口。

流程说明:

  1. 解析请求参数

    r.ParseForm()
    username := r.Form.Get("username")
    
    • 通过 r.ParseForm() 解析请求中的表单参数,然后从表单中提取出 username 字段。
    • 此步骤获取客户端传递的用户名,用于后续查询用户信息。
  2. 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验证
  3. 查询用户信息

    user, err := dblayer.GetUserInfo(username)
    if err != nil {
        w.WriteHeader(http.StatusForbidden)
        return
    }
    
    • 调用 dblayer.GetUserInfo(username) 从数据库中获取与该用户名对应的用户信息。
    • 如果查询失败(err != nil),服务器返回 HTTP 403 禁止访问(http.StatusForbidden),表示用户信息不可用或不存在。
  4. 组装并响应用户数据

    resp := util.RespMsg{
        Code: 0,
        Msg:  "OK",
        Data: user,
    }
    w.Write(resp.JSONBytes())
    
    • 组装一个 RespMsg 结构体,包含响应的状态码(Code: 0 表示成功)、消息(Msg: "OK")、以及用户数据(Data: user)。
    • resp.JSONBytes() 将该响应转换为 JSON 格式,并将其写入到 HTTP 响应中,返回给客户端。

总结:

  1. 请求参数解析:从请求中获取用户名(以及注释掉的 token)信息。
  2. (注释掉的)Token 验证:原本应该验证用户请求的合法性,确保 token 有效。
  3. 查询用户信息:通过数据库层查询与用户名对应的用户信息,查询失败则返回 403 禁止访问。
  4. 返回用户数据:组装 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))
        //.....
posted @ 2024-09-07 20:17  daligh  阅读(94)  评论(0)    收藏  举报