【Go Web 開發】授予用戶權限
上一篇文章我們的權限模型和權限檢查中間件已經可以正常運行了。但此時,當新用戶註冊一個帳戶時,他們沒有任何權限。在本節中,我們將修改這個設置,使新用戶在默認情況下自動被授予 “movies:read” 權限。
更新權限模型
爲了給用戶授予權限,我們需要更新 PermissionModel,添加 AddForUser() 方法,爲用戶添加一個或多個權限碼到數據庫中。我們的想法是,按以下方式在處理程序中使用該函數:
//爲ID = 2的用戶添加"movies:read"和"movies:write"權限
app.models.Permissions.AddForUser(2, "movies:read", "movies:write")
該函數在數據庫中執行的 SQL 語句如下所示:
INSERT INTO users_permissions
SELECT $1, permissions.id FROM permissions WHERE permissions.code = ANY($2)
在這個 SQL 語句中 $1 參數是用戶 ID,$2 參數是我們需要爲用戶添加的權限碼列表,類似 {'movies:read', 'movies:write'}。
因此,這裏發生的事是第二行的 SELECT 語句創建了一個 “臨時” 表,其中的行由用戶 ID 和數組中相應權限代碼的 ID 組成。然後將這個臨時表的內容插入到 user_permissions 表中。
下面我們在 internal/data/permissions.go 文件中創建 AddForUser() 方法:
File: internal/data/permissions.go
package data
...
//爲特定用戶添加授權碼。這裏我們使用可變參數。
func (m PermissionModel)AddForUser(userID int64, codes ...string) error {
query := `
INSERT INTO users_permissions
SELECT $1, permissions.id FROM permissions WHERE permissions.code = ANY($2)`
ctx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
_, err := m.DB.ExecContext(ctx, query, userID, pq.Array(codes))
return err
}
更新註冊處理程序 (register handler)
現在數據庫處理完成了,我們更新 registerUserHandler 這樣在新用戶註冊的時自動爲用戶創建 "movies:read" 權限。如下所示:
File:cmd/api/users.go
package main
...
func (app *application) registerUserHandler(w http.ResponseWriter, r *http.Request) {
//創建匿名結構體接收客戶端發送用戶信息
var input struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
//解析請求內容到匿名結構體只能夠
err := app.readJSON(w, r, &input)
if err != nil {
app.badRequestResponse(w, r, err)
return
}
//將input中到用戶信息拷貝到User結構體。注意需要將激活信息設置爲false,
//該操作是非必需的因爲默認值就是false,單獨設置下可讀性更好。
user := &data.User{
Name: input.Name,
Email: input.Email,
Activated: false,
}
//使用Password.Set方法處理密碼
err = user.Password.Set(input.Password)
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
v := validator.New()
//校驗user結構體
if data.ValidateUser(v, user); !v.Valid() {
app.failedValidationResponse(w, r, v.Errors)
return
}
//插入用戶信息到數據庫
err = app.models.Users.Insert(user)
if err != nil {
switch {
//如果錯誤是ErrDuplicateEmail,使用v.AddError()方法手動添加校驗錯誤信息
case errors.Is(err, data.ErrDuplicateEmail):
v.AddError("email", "a user with this email address already exists")
app.failedValidationResponse(w, r, v.Errors)
default:
app.serverErrorResponse(w, r, err)
}
return
}
//爲新註冊用戶添加"movies:read"權限
err = app.models.Permissions.AddForUser(user.ID, "movies:read")
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
//用戶數據插入表之後,爲用戶生成新的激活token
token, err := app.models.Tokens.New(user.ID, 3 * 24 * time.Hour, data.ScopeActivation)
if err != nil {
app.serverErrorResponse(w, r, err)
return
}
//使用background創建goroutine異步發送郵件
app.background(func() {
//現在要傳入多個數據到郵件模版,我們創建一個map
data := map[string]interface{}{
"activationToken": token.Plaintext,
"userID": user.ID,
}
// 發送歡迎郵件,並傳入map作爲動態數據
err = app.mailer.Send(user.Email, "/user_welcome.tmpl", data)
if err != nil {
app.logger.Error(err, nil)
}
})
//將返回碼改爲202,表示客戶端請求被接受,但處理沒有完成。
err = app.writeJSON(w, http.StatusAccepted, envelope{"user":user}, nil)
if err != nil {
app.serverErrorResponse(w, r, err)
}
}
...
我們用 grace@example.com 郵箱新註冊一個用戶來測試下前面代碼是否正常。
$ BODY='{"name": "Grace Smith", "email": "grace@example.com", "password": "pa55word"}'
$ curl -d "$BODY" localhost:4000/v1/users
{
"user": {
"id": 4,
"create_at": "2022-01-08T16:08:17+08:00",
"name": "Grace Smith",
"email": "grace@example.com",
"activated": false
}
}
如果你打開 psql,執行以下 SQL 查詢應該可以看到新註冊的用戶有 movies:read 權限。
greenlight=> select email, code from users
inner join users_permissions on users.id = users_permissions.user_id
inner join permissions on users_permissions.permission_id = permissions.id
where users.email = 'grace@example.com';
email | code
-------------------+-------------
grace@example.com | movies:read
(1 row)
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/KYuaCvblWxtpLQlqoJIhYA