美文网首页go
【Go Web开发】授予用户权限

【Go Web开发】授予用户权限

作者: Go语言由浅入深 | 来源:发表于2022-03-28 22:02 被阅读0次

上一篇文章我们的权限模型和权限检查中间件已经可以正常运行了。但此时,当新用户注册一个帐户时,他们没有任何权限。在本节中,我们将修改这个设置,使新用户在默认情况下自动被授予“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)

相关文章

网友评论

    本文标题:【Go Web开发】授予用户权限

    本文链接:https://www.haomeiwen.com/subject/mmdmjrtx.html