在Go中实现的Web服务中,我希望能够根据用户的角色限制JSON响应中返回的字段。
例如,我可能有一个角色为guest
的当前登录用户,另一个角色为admin
的用户
对于管理员,我希望json拥有所有密钥,例如
{
id: 1,
name: "John",
role: "admin"
}
并且客人没有角色密钥,例如
{
id: 1,
name: "John"
}
我现在可以封送json并返回所有字段。我需要能够限制它。
答案 0 :(得分:6)
您可以查看@Volker提出的建议并清除用户没有权限的结构字段。这可能是最容易实现的。
类似的第二个选项是创建自定义JSON编码器。仅当角色struct标签与当前用户的角色匹配时才对字段进行编码的方法。这里有一些伪代码来说明:
type T struct {
currentRole Role `json:"-"`
FieldA string `json:"field_a,omitempty", role:"guest"`
FieldB string `json:"field_b,omitempty", role:"guest"`
FieldC int `json:"field_c,omitempty", role:"admin"`
}
// Have T implement the encoding/json.Marshaler interface.
func (t *T) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
// Use some reflection magic to iterate over struct fields.
for _, field := range getStructFields(t) {
// More reflection magic to extract field tag data.
role := getFieldTag(field, "role")
// If the field tag's role matches our current role,
// we are good to go. otherwise, skip this field.
if !matchingRole(role, t.currentRole) {
continue // skip this field
}
data, err := json.Marshal(fieldValue(field))
...
_, err = buf.Write(data)
...
}
return buf.Bytes(), nil
}
如果你需要新的角色,这将是一个痛苦的维持。所以这不是我会轻易考虑做的事情。
我不完全确定您所寻找的是解决问题的正确方法。这取决于您使用代码的上下文,这在您的问题中并不明确。但是,如果这涉及一个网站,用户在网站上的能力仅由role
JSON字段的值定义,那么您正在查看安全漏洞。他们可以直接进入浏览器调试器并更改此JSON对象的值以包含"role: "admin"
字段。并且presto!即时行政权力。在模板处理期间,服务器是否应该真正地处理是否基于用户角色呈现页面的某些部分。就像发布到服务器的所有数据一样,应该再次检查和检查以确保它来自可信来源。
如果这些都不适用于你,那么无论如何都要忽视这一段。
答案 1 :(得分:1)
这个问题似乎很老,但我最近想做同样的事情。也许这将有助于未来的人。这是另一种方法:您可以定义自己的Marshal接口并使用匿名结构。
//User holds all variables
//even private ones
type User struct {
ID int64
Name string
Role string
}
//MarshalJSON gives back json user
//but only the public fields!
func (u *User) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
ID string `json:"id"`
Name string `json:"name"`
}{u.ID, u.Name})
}
在块中放置if u.Role == "admin"
语句以决定是否编组其余语句会非常容易。
答案 2 :(得分:1)
另一个选项也可用于定义输出中的字段集,以获取来自appengine数据存储区查询的结构列表。
// Setting different JSON output field for the same struct, using anonymous
// fields (inspired by inspired by http://choly.ca/post/go-json-marshalling/)
// This alternative could be used to load a resultset from an appengine datastore
// query and returned a custom field combination for the list items.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Role string `json:"-"`
LaunchCode string `json:"-"`
}
type AdminOutputUser User
func (user *AdminOutputUser) MarshalJSON() ([]byte, error) {
type Alias AdminOutputUser
return json.Marshal(&struct {
*Alias
Role string `json:"role"`
}{
(*Alias)(user),
user.Role,
})
}
type SuperadminOutputUser User
func (user *SuperadminOutputUser) MarshalJSON() ([]byte, error) {
type Alias SuperadminOutputUser
return json.Marshal(&struct {
*Alias
Role string `json:"role"`
LaunchCode string `json:"code"`
}{
(*Alias)(user),
user.Role,
user.LaunchCode,
})
}
func main() {
user := User{"007", "James Bond", "admin", "12345678"}
adminOutput := AdminOutputUser(user)
superadminOutput := SuperadminOutputUser(user)
b, _ := json.Marshal(&user)
fmt.Printf("%s\n\n", string(b))
// {"id":"007","name":"James Bond"}
b, _ = json.Marshal(&adminOutput)
fmt.Printf("%s\n\n", string(b))
// {"id":"007","name":"James Bond","role":"admin"}
b, _ = json.Marshal(&superadminOutput)
fmt.Printf("%s\n\n", string(b))
// {"id":"007","name":"James Bond","role":"admin","code":"12345678"}
}
// for appengine could do something like
// ...
// var users []AdminOutputUser // or User or SuperadminOutputUser
// q := datastore.NewQuery("User")
// keys, err := q.GetAll(ctx, &users)
// ...