美文网首页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