1

我是 Go 新手,正在尝试根据 postgresql 数据库中的用户名检查密码。

我无法进行美元替换,也不愿求助于连接字符串。

我目前正在使用松鼠,但也尝试过,但运气不佳。

我有以下代码:

    package datalayer

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "net/http"

    sq "github.com/Masterminds/squirrel"
    _ "github.com/jackc/pgx/v4/stdlib"
    "golang.org/x/crypto/bcrypt"

    "github.com/gin-gonic/gin"
)

var (
    // for the database
    db *sql.DB
)

func InitDB(sqlDriver string, dataSource string) error {
    var err error

    // Connect to the postgres db  (sqlDriver is literal string "pgx")
    db, err = sql.Open(sqlDriver, dataSource)

    if err != nil {
        panic(err)
    }
    return db.Ping()
}

// Create a struct that models the structure of a user, both in the request body, and in the DB
type Credentials struct {
    Password string `json:"password", db:"password"`
    Username string `json:"username", db:"username"`
}

func Signin(c *gin.Context) {
    // Parse and decode the request body into a new `Credentials` instance
    creds := &Credentials{}
    err := json.NewDecoder(c.Request.Body).Decode(creds)


    if err != nil {
        // If there is something wrong with the request body, return a 400 status
        c.Writer.WriteHeader(http.StatusBadRequest)
        return
    }
    query := sq.
        Select("password").
        From("users").
        Where("username = $1", creds.Username).
        PlaceholderFormat(sq.Dollar)

        // The line below doesn't substitute the $ sign, it shows this:  SELECT password FROM users WHERE username = $1 [rgfdgfd] <nil>
    fmt.Println(sq.
        Select("password").
        From("users").
        Where("username = $1", creds.Username).
        PlaceholderFormat(sq.Dollar).ToSql())

    rows, sqlerr := query.RunWith(db).Query()
    if sqlerr != nil {
        panic(fmt.Sprintf("QueryRow failed: %v", sqlerr))
    }

    if err != nil {
        // If there is an issue with the database, return a 500 error
        c.Writer.WriteHeader(http.StatusInternalServerError)
        return
    }
    // We create another instance of `Credentials` to store the credentials we get from the database
    storedCreds := &Credentials{}
    // Store the obtained password in `storedCreds`
    err = rows.Scan(&storedCreds.Password)
    if err != nil {
        // If an entry with the username does not exist, send an "Unauthorized"(401) status
        if err == sql.ErrNoRows {
            c.Writer.WriteHeader(http.StatusUnauthorized)
            return
        }
        // If the error is of any other type, send a 500 status
        c.Writer.WriteHeader(http.StatusInternalServerError)
        return
    }

    // Compare the stored hashed password, with the hashed version of the password that was received
    if err = bcrypt.CompareHashAndPassword([]byte(storedCreds.Password), []byte(creds.Password)); err != nil {
        // If the two passwords don't match, return a 401 status
        c.Writer.WriteHeader(http.StatusUnauthorized)
    }
    fmt.Printf("We made it !")
    // If we reach this point, that means the users password was correct, and that they are authorized
    // The default 200 status is sent
}

当我检查 pgAdmin 时,我看到以下内容,这表明美元符号未被替换:

在此处输入图像描述

4

1 回答 1

2

占位符的替换是由 postgres 服务器完成的,它不应该是 Go 代码或松鼠的工作来进行替换。

当您执行带参数的查询时,数据库驱动程序必须执行的大致内容如下所示:

  1. 使用查询字符串,占位符不变,parse请求被发送到 postgres 服务器以创建准备好的语句
  2. 使用新创建语句的参数值和标识符,bind发送请求以通过创建门户使语句准备好执行。门户(与游标相似但不同)表示准备执行或已部分执行的语句,其中填充了任何缺失的参数值。
  3. 使用门户的标识符将execute请求发送到服务器,然后服务器执行门户的查询。

请注意,上述步骤只是一个粗略的概述,实际上在 db 客户端和服务器之间涉及更多的请求-响应周期。

pgAdmin我而言,我相信它向您显示的是parse请求创建的准备好的声明,尽管我无法确定,因为我不熟悉它。


理论上,类似 的帮助squirrel程序库或类似的驱动程序库pgx可以自己实现参数替换,然后向服务器发送一个简单的查询。但是,总的来说,考虑到 SQL 注入的可能性,我认为最好将其留给 postgres 服务器的权限。


PlaceholderFormat工作是将占位符简单地转换为指定的格式。例如,您可以使用 MySQL 格式编写 SQL (?,?,...),然后调用该PlaceholderFormat(sql.Dollar)方法将其转换为 PostgreSQL 格式($1,$2,...)

于 2021-07-25T07:31:15.783 回答