本文大致記錄了eudore-website認證鑑權體系的實現,實現了acl、rbac、pbac鑑權和ak、token、bearer認證,完整細節請查看源碼。
在線demo,用戶密碼均爲guest。
認證設計
eudore-website使用ak、token、bearer認證三種綜合認證,原理通過web請求中間件使用請求信息獲得用戶信息,保存到請求上下文中然後供後續使用。
bearer認證
bearer認證原理是利用jwt非對稱簽名防止數據篡改。
最初就初始化jwt解析對象,然後處理請求Authorization Header,解析出jwt的數據,從中提取到userid和username信息,然後設置請求上下文的參數中。
func(ctx eudore.Context) {
data, err := jwtParse.ParseBearer(ctx.GetHeader(eudore.HeaderAuthorization))
if err == nil {
ctx.SetParam("UID", eudore.GetString(data["userid"]))
ctx.SetParam("UNAME", eudore.GetString(data["name"]))
return
}
...
}
然後客戶端請求添加Authorization Header.
例如基於mithriljs封裝ajax添加Header:
if(typeof m.request !== 'undefined') {
var oldrequest = m.request
m.request = function(args) {
// add header
if(!("headers" in args)) {
args["headers"] = {}
}
if(Base.lang != "") {
args["headers"]["Accept-Language"] = Base.lang
}
if(Base.bearer != "") {
args["headers"]["Authorization"] = Base.bearer
}
if(requestid!="") {
args["headers"]["X-Parent-Id"] = requestid
}
return oldrequest(args)
}
}
具有一個全局遍歷Base保存用戶相關信息,例如Base.bearer,在使用m.request方法時,就自動給參數添加bearer信息。
curl直接-H指定header即可。
token認證
token認證原理使用token加載到對應的用戶信息
token供api使用。
創建數據表tb_auth_access_token,裏面保存token對應的用戶信息。
CREATE TABLE tb_auth_access_token(
"userid" INTEGER PRIMARY KEY,
"token" VARCHAR(32),
"expires" TIMESTAMP,
"createtime" TIMESTAMP DEFAULT (now())
);
從請求中提取到token參數,然後數據庫查詢tb_auth_access_token表,找到用戶信息,設置到請求上下文中。
stmtQueryAccessToken, err := db.Prepare("SELECT userid,(SELECT name FROM tb_auth_user_info WHERE id = userid) FROM tb_auth_access_token WHERE token=$1 and expires > now()")
func(ctx eudore.Context) {
...
token := ctx.GetQuery("token")
if token != "" {
var userid string
var username string
err := stmtQueryAccessToken.QueryRow(token).Scan(&userid, &username)
if err == nil {
ctx.SetParam("UID", userid)
ctx.SetParam("UNAME", username)
return
}
ctx.Error(err)
}
...
}
ak認證
ak認證的原理例如非對稱加密實現,用戶有效校驗
ak和token一樣用於api使用,但是ak更加複雜和安全。
accesskey表明是那個ak,accesssecrect是簽名使用的私鑰,然後客戶端和服務端使用accesssecrect簽名一個數據得到簽名結果signature,如果signature相同就是表示accesssecrect相同,那麼用戶使用的ak就是有效的。
ak認證創建tb_auth_access_key表,保存ak和用戶信息和token表相識。
CREATE TABLE tb_auth_access_key(
"userid" INTEGER PRIMARY KEY,
"accesskey" VARCHAR(32),
"accesssecrect" VARCHAR(32),
"expires" TIMESTAMP,
"createtime" TIMESTAMP DEFAULT (now())
);
ak認證先提取到accesskey、signature、expires三個參數,用於ak認證使用,accesskey對應ak記錄、signature是ak簽名結果、expires是簽名過期時間。
先檢查下有效時間是否有效,且有效時間不大於60分鐘。
然後數據庫查詢一下accesskey對應的accesssecrect和用戶數據。
再計算一下簽名結果,如果結果和signature一樣那麼就是通過,然後設置用戶數據。
當前簽名格式是accesskey-expires,過於簡單,但是也可以用。
stmtQueryAccessKey, err := db.Prepare("SELECT userid,(SELECT name FROM tb_auth_user_info WHERE id = userid),accesssecrect FROM tb_auth_access_key WHERE accesskey=$1 and expires > $2")
func(ctx eudore.Context) {
...
key, signature, expires := ctx.GetQuery("accesskey"), ctx.GetQuery("signature"), ctx.GetQuery("expires")
if key != "" && signature != "" && expires != "" {
tunix, err := strconv.ParseInt(expires, 10, 64)
if err != nil {
ctx.Error(err)
return
}
ttime := time.Unix(tunix, 0)
if ttime.After(time.Now().Add(60 * time.Minute)) {
ctx.Errorf("accesskey expires is to long, max 60 min")
return
}
//
var userid, username, scerect string
err = stmtQueryAccessKey.QueryRow(key, ttime).Scan(&userid, &username, &scerect)
if err != nil {
ctx.Error(err)
return
}
h := hmac.New(sha1.New, []byte(scerect))
fmt.Fprintf(h, "%s-%s", key, expires)
if signature != base64.StdEncoding.EncodeToString(h.Sum(nil)) {
ctx.Errorf("signature is invalid")
return
}
ctx.SetParam("UID", userid)
ctx.SetParam("UNAME", username)
}
}
eudore RAM 鑑權設計
eudore-website使用acl、rbac、pbac三種複合鑑權設計,按順序依次處理,某個對象可以處理就返回結果。
RAM
eudore定義了一個ram接口,ram接口會傳遞用戶id、用戶行爲信息給ram鑑權使用,然後ram對象返回處理結果和是否處理。
// RamHandler 定義Ram處理接口
type RamHandler interface {
RamHandle(int, string, eudore.Context) (bool, bool)
// return1 驗證結果 return2 是否驗證
}
RamHttp對象會處理http相關內容,獲取到用戶id和行爲傳遞給多Ram對象依次處理,然後根據Ram結果處理,一般RAM對象如果處理了請求會設置ram參數爲處理者,例如acl處理的請求,獲得ram參數的值就是acl。
ram需要兩個參數用戶id和action,用戶id由認證體系提供的UID獲得,action參數由路過提供的靜態值
例如路由指定的action參數爲Get
app.GetFunc("/* action=Get", func(ctx eudore.Context){})
或者控制器指定的路由參數,例如website控制器使用的action參數,由包名稱、控制器名稱、控制器方法組成。
// GetRouteParam 方法添加路由參數信息。
func (ctl *ControllerWebsite) GetRouteParam(pkg, name, method string) string {
pos := strings.LastIndexByte(pkg, '/') + 1
if pos != 0 {
pkg = pkg[pos:]
}
if strings.HasSuffix(name, "Controller") {
name = name[:len(name)-len("Controller")]
}
return fmt.Sprintf("action=%s:%s:%s", pkg, name, method)
例如一條路由註冊日誌
github.com/eudore/website/handlers/auth.PolicyController.GetIdById就是處理函數,對應的包是auth、控制器名稱是Policy(移除應用控制器的Controller後綴)、控制器方法是GetIdById,組合的action爲 auth:Policy:GetIdById
{"time":"2019-10-02 18:57:23","level":"INFO","message":"RegisterHandler: GET /api/v1/auth/policy/id/:id prefix=/api/v1/auth action=auth:Policy:GetIdById [github.com/eudore/website/handlers/auth.PolicyController.GetIdById]"}
數據庫設計
website使用pgsql數據庫,然後建立user_info、user_permisson、user_role、user_policy表,記錄用戶基本信息和綁定的權限、角(jue)色、策略信息,對應是是acl、rbac、pbac三種鑑權數據。
數據庫唯一約束未添加
-- 用戶信息表
CREATE SEQUENCE seq_auth_user_info_id INCREMENT by 1 MINVALUE 1 START 1;
CREATE TABLE tb_auth_user_info(
"id" INTEGER PRIMARY KEY DEFAULT nextval('seq_auth_user_info_id'),
"name" VARCHAR(32) NOT NULL,
"status" INTEGER DEFAULT 0,
"level" INTEGER DEFAULT 0,
"mail" VARCHAR(48),
"tel" VARCHAR(16),
"icon" INTEGER DEFAULT 0,
"loginip" INTEGER DEFAULT 0,
"logintime" TIMESTAMP,
"sigintime" TIMESTAMP DEFAULT (now())
);
-- 用戶綁定權限列表
CREATE TABLE tb_auth_user_permission(
"userid" INTEGER,
"permissionid" INTEGER,
"effect" bool,
"time" TIMESTAMP DEFAULT (now()),
PRIMARY KEY("userid", "permissionid")
);
COMMENT ON TABLE "public"."tb_auth_user_permission" IS 'ACL用戶綁定權限列表';
COMMENT ON COLUMN "tb_auth_user_permission"."userid" IS '用戶id';
COMMENT ON COLUMN "tb_auth_user_permission"."permissionid" IS '權限id';
-- 用戶綁定角色關係
CREATE TABLE tb_auth_user_role(
"userid" INTEGER,
"roleid" INTEGER,
"time" TIMESTAMP DEFAULT (now()),
PRIMARY KEY("userid", "roleid")
);
COMMENT ON TABLE "public"."tb_auth_user_role" IS 'RBAC用戶綁定角色關係';
COMMENT ON COLUMN "tb_auth_user_role"."userid" IS '用戶id';
COMMENT ON COLUMN "tb_auth_user_role"."roleid" IS '角色id';
-- 用戶綁定策略
CREATE TABLE tb_auth_user_policy(
"userid" INTEGER,
"policyid" INTEGER,
"index" INTEGER DEFAULT 0,
"time" TIMESTAMP DEFAULT (now()),
PRIMARY KEY("userid", "policyid")
);
COMMENT ON TABLE "public"."tb_auth_user_policy" IS 'PBAC用戶綁定策略';
COMMENT ON COLUMN "tb_auth_user_policy"."userid" IS 'User ID';
COMMENT ON COLUMN "tb_auth_user_policy"."policyid" IS 'Polic ID';
COMMENT ON COLUMN "tb_auth_user_policy"."index" IS '策略優先級';
然後創建權限、角色、策略相關的表,創建permission、role、policy三種權限對象的信息,和role綁定的權限信息。
-- 資源權限列表
CREATE SEQUENCE seq_auth_permission_id INCREMENT by 1 MINVALUE 1 START 1;
CREATE TABLE tb_auth_permission(
"id" INTEGER PRIMARY KEY DEFAULT nextval('seq_auth_permission_id'),
"name" VARCHAR(64) NOT NULL,
"description" VARCHAR(512),
"time" TIMESTAMP DEFAULT (now())
);
COMMENT ON TABLE "public"."tb_auth_permission" IS '資源權限列表';
COMMENT ON COLUMN "tb_auth_permission"."id" IS '權限id';
COMMENT ON COLUMN "tb_auth_permission"."name" IS '權限行爲';
-- 角色信息表
CREATE SEQUENCE seq_auth_role_id INCREMENT by 1 MINVALUE 1 START 1;
CREATE TABLE tb_auth_role(
"id" INTEGER PRIMARY KEY DEFAULT nextval('seq_auth_role_id'),
"name" VARCHAR(32),
"description" VARCHAR(64),
"time" TIMESTAMP DEFAULT (now())
);
COMMENT ON TABLE "public"."tb_auth_role" IS 'RBAC角色信息表';
COMMENT ON COLUMN "tb_auth_role"."id" IS '角色id';
COMMENT ON COLUMN "tb_auth_role"."name" IS '角色名稱';
-- 角色綁定權限
CREATE TABLE tb_auth_role_permission(
"roleid" INTEGER,
"permissionid" INTEGER,
"time" TIMESTAMP DEFAULT (now()),
PRIMARY KEY("roleid", "permissionid")
);
COMMENT ON TABLE "public"."tb_auth_role_permission" IS 'RBAC角色綁定權限';
COMMENT ON COLUMN "tb_auth_role_permission"."roleid" IS '角色id';
COMMENT ON COLUMN "tb_auth_role_permission"."permissionid" IS '權限id';
-- PBAC策略信息表
CREATE SEQUENCE seq_auth_policy_id INCREMENT by 1 MINVALUE 1 START 1;
CREATE TABLE tb_auth_policy(
"id" INTEGER PRIMARY KEY DEFAULT nextval('seq_auth_policy_id'),
"name" VARCHAR(64),
"description" VARCHAR(512),
"policy" VARCHAR(4096),
"time" TIMESTAMP DEFAULT (now())
);
COMMENT ON TABLE "public"."tb_auth_policy" IS 'PBAC策略信息表';
COMMENT ON COLUMN "tb_auth_policy"."id" IS 'Polic ID';
COMMENT ON COLUMN "tb_auth_policy"."policy" IS '策略內容';
Acl
acl(access control list)訪問控制列表,記錄用戶對某個權限是允許和拒絕。
Permissions記錄權限對應的id,對應tb_auth_permission表。
AllowBinds和DenyBinds記錄用戶綁定的信息(爲何不定義成map[int]map[int]bool忘記了),對應tb_auth_user_permission表。
RamHandle先使用權限行爲轉換成權限id,然後map查找用戶id和權限id對應的結果,如果查找到就返回結果。
type Acl struct {
AllowBinds map[int]map[int]struct{}
DenyBinds map[int]map[int]struct{}
Permissions map[string]int
}
// RamHandle 方法實現ram.RamHandler接口,匹配一個請求。
func (acl *Acl) RamHandle(id int, perm string, ctx eudore.Context) (bool, bool) {
permid, ok := acl.Permissions[perm]
// 存在這個權限
if ok {
// 綁定Allow
_, ok = acl.AllowBinds[id][permid]
if ok {
ctx.SetParam(eudore.ParamRAM, "acl")
return true, true
}
_, ok = acl.DenyBinds[id][permid]
if ok {
ctx.SetParam(eudore.ParamRAM, "acl")
return false, true
}
}
return false, false
}
RBAC
基於角色的權限訪問控制(Role-Based Access Control),判斷一個用戶的角色是否擁有對應的權限,由於用戶綁定角色、角色綁定權限,所以只需要遍歷用戶的全部角色的全部權限判斷即可。
三表對應關係:
RoleBinds => tb_auth_user_role
PermissionBinds => tb_auth_role_permission
Permissions => tb_auth_permission
RamHandle方法先轉換權限成id,然後遍歷用戶id對應的全部角色,再遍歷角色對應的全部權限id,檢查用戶是否擁有這個角色,如果某個角色擁有這個權限id,那麼就是用戶綁定的擁有這個權限(優化:未使用二分,匹配性能可提升4倍)。
type (
// Rbac 定義rbac對象。
Rbac struct {
RoleBinds map[int][]int
PermissionBinds map[int][]int
Permissions map[string]int
}
)
// RamHandle 方法實現ram.RamHandler接口。
func (r *Rbac) RamHandle(id int, name string, ctx eudore.Context) (bool, bool) {
permid, ok := r.Permissions[name]
if !ok {
return false, false
}
// 遍歷角色
for _, roles := range r.RoleBinds[id] {
// 遍歷權限
for _, perm := range r.PermissionBinds[roles] {
// 匹配權限
if perm == permid {
ctx.SetParam(eudore.ParamRAM, "rbac")
return true, true
}
}
}
return false, false
}
PBAC
PBAC基於策略的權限控制,一個用戶有多個策略,依次判斷策略匹配結果,pbac也是eudore-website主要使用的鑑權方式。
type (
// Pbac 定義PBAC鑑權對象。
Pbac struct {
PolicyBinds map[int][]int `json:"-" key:"-"`
Policys map[int]*Policy `json:"-" key:"-"`
}
)
// RamHandle 方法實現ram.RamHandler接口,匹配一個請求。
func (p *Pbac) RamHandle(id int, action string, ctx eudore.Context) (bool, bool) {
// 獲得資源resource
resource := getResource(ctx)
bs, ok := p.PolicyBinds[id]
if ok {
// 遍歷全部策略
for _, b := range bs {
// 檢查策略id是否存在
ps, ok := p.Policys[b]
if !ok {
continue
}
// 匹配策略描述
for _, s := range ps.Statement {
if s.MatchAction(action) && s.MatchResource(resource) && s.MatchCondition(ctx) {
ctx.SetParam(eudore.ParamRAM, "pbac")
return s.Effect, true
}
}
}
}
return false, false
}
// getResource 函數未更新
func getResource(ctx eudore.Context) string {
path := ctx.Path()
prefix := ctx.GetParam("prefix")
if prefix != "" {
path = path[len(prefix):]
}
ctx.SetParam("resource", path)
return path
}
策略
eudore pbac的策略對象會綁定多個描述對象,每個描述對象具有鑑權結果(effect)、行爲、資源和多項條件。
例如一個策略:
定義了一個描述對象,如果行爲是auth和status任意對象的的Get方法就會通過,同時限制了請求時間是2021年前和http請求方法是GET方法(browser限制ua未實現)。
{
"version": "1",
"description": "全部文檔只讀權限",
"statement": [
{
"effect": true,
"action": [
"auth:*:Get*",
"status:*:Get*"
],
"resource": [
"*"
],
"conditions": {
"time": {
"befor": "2020-12-31"
},
"method": [
"GET"
],
"browser": [
"Chrome/60+",
"Chromium/0-90",
"Firefox"
]
}
}
]
}
go定義的Policy對象,其中Conditions作爲接口,允許擴展多種條件限制,當前允許or、and、sourceip、time、method這些條件。
type (
// Policy 定義一個策略。
Policy struct {
Description string `json:"description"`
Version string `json:"version"`
Statement []Statement `json:"statement"`
}
// Statement 定義一條策略內容。
Statement struct {
Effect bool
Action []string
Resource []string
Conditions *Conditions `json:"conditions,omitempty"`
}
// Conditions 定義PBAC使用的條件對象。
Conditions struct {
Conditions []Condition
}
// Condition 定義策略條件
Condition interface {
Name() string
Match(ctx eudore.Context) bool
}
ConditionOr struct {
Conditions []Condition
}
ConditionAnd struct {
Conditions []Condition
}
ConditionSourceIp struct {
SourceIp []*net.IPNet
}
ConditionTime struct {
Befor time.Time `json:"befor"`
After time.Time `json:"after"`
}
ConditionMethod struct {
Methods []string
}
)
PBAC存在問題:
RegisterCondition函數忘記寫了
獲取resource對象未更新
browser限制ua未實現
Website Ram 封裝
website需要對ram數據與數據庫同步,沒有直接使用eudore-RAM,而是進行了簡單封裝。
RAM
website-ram重新實現了eudore.RamHttp對象,同時額外添加用戶訪問自己資源通過,如果路由參數中具有username和userid就是訪問屬於用戶自己的資源。
init系列函數是初始化7張權限表數據到ram對象
import eram "github.com/eudore/eudore/middleware/ram"
type Ram struct {
Acl *eram.Acl
Rbac *eram.Rbac
Pbac *eram.Pbac
}
func NewRam(app *eudore.App) *Ram {
db, ok := app.Config.Get("keys.db").(*sql.DB)
if !ok {
panic("init middleware check config 'keys.db' not find database.")
}
ram := &Ram{
Acl: eram.NewAcl(),
Rbac: eram.NewRbac(),
Pbac: eram.NewPbac(),
}
errs := eudore.NewErrors()
// 初始化: 權限、策略
// TODO: 數據修改併發問題
errs.HandleError(ram.InitPermissionInfo(db))
errs.HandleError(ram.InitPolicyInfo(db))
// 初始化: 用戶綁定權限、用戶綁定教師、角色綁定權限、用戶綁定策略
errs.HandleError(ram.InitUserBindPermission(db))
errs.HandleError(ram.InitUserBindRole(db))
errs.HandleError(ram.InitRoleBindPermission(db))
errs.HandleError(ram.InitUserBindPolicy(db))
if errs.GetError() != nil {
panic(errs.GetError())
}
// 傳遞ram對象
app.Set("keys.ram", ram)
return ram
}
func (ram *Ram) NewRamFunc() eudore.HandlerFunc {
handler := eram.NewRamAny(
ram.Acl,
ram.Rbac,
ram.Pbac,
eram.DenyHander,
).RamHandle
return func(ctx eudore.Context) {
// 如果請求用戶資源是用戶本身的直接通過,UID、UNAME由用戶信息中間件加載,userid、username由路由參數加載。
if ctx.GetParam("userid") == ctx.GetParam("UID") && ctx.GetParam("userid") != "" {
return
}
if ctx.GetParam("username") == ctx.GetParam("UNAME") && ctx.GetParam("username") != "" {
return
}
// 執行ram鑑權邏輯
action := ctx.GetParam("action")
if len(action) > 0 && !eram.HandleDefaultRam(eudore.GetInt(ctx.GetParam("UID")), action, ctx, handler) {
ctx.WriteHeader(403)
ctx.Render(map[string]interface{}{
eudore.ParamRAM: ctx.GetParam("ram"),
eudore.ParamAction: action,
})
ctx.End()
}
}
}
Init
InitPermissionInfo方法就從數據庫查詢權限信息,然後賦值給ACL和RBAC對象。
其他init函數類似。
func (ram *Ram) InitPermissionInfo(db *sql.DB) error {
rows, err := db.Query("SELECT id,name FROM tb_auth_permission")
if err != nil {
return err
}
defer rows.Close()
var Permissions = make(map[string]int)
var id int
var name string
for rows.Next() {
err = rows.Scan(&id, &name)
if err != nil {
return err
}
Permissions[name] = id
}
// 共享權限信息
ram.Acl.Permissions = Permissions
ram.Rbac.Permissions = Permissions
return nil
}
Controller
例如策略控制器賦值策略的管理,實現策略CURD和RAM信息同步,其他User、Permission、Role三個控制器行爲類型。
type PolicyController
func NewPolicyController(db *sql.DB, ram *middleware.Ram) *PolicyController
func (ctl *PolicyController) DeleteIdById() (err error)
func (ctl *PolicyController) DeleteNameByName() (err error)
func (ctl *PolicyController) GetCount() interface{}
func (ctl *PolicyController) GetIdById() (interface{}, error)
func (ctl *PolicyController) GetIndex() (interface{}, error)
func (ctl *PolicyController) GetList() (interface{}, error)
func (ctl *PolicyController) GetNameByName() (interface{}, error)
func (ctl *PolicyController) GetSearchByKey() (interface{}, error)
func (ctl *PolicyController) GetUserIdById() (interface{}, error)
func (ctl *PolicyController) GetUserNameByName() (interface{}, error)
func (ctl *PolicyController) PostIdById() (err error)
func (ctl *PolicyController) PostNameByName() (err error)
func (ctl *PolicyController) PutNew() (err error)
func (ctl *PolicyController) Release() error
如果請求方法是POST、PUT、DELETE就是對策略信息有所修改,就調用RAM重新初始化策略數據,實現鑑權信息同步。
type PolicyController struct {
controller.ControllerWebsite
Ram *middleware.Ram
}
// Release 方法用於刷新ram策略信息。
func (ctl *PolicyController) Release() error {
// 如果修改策略信息成功,則刷新ram策略信息。
if ctl.Response().Status() == 200 && (ctl.Method() == "POST" || ctl.Method() == "PUT" || ctl.Method() == "DELETE") {
ctl.Ram.InitPolicyInfo(ctl.DB)
}
return nil
}
type UserController struct {
controller.ControllerWebsite
Ram *middleware.Ram
}
// Release 方法刷新用戶綁定ram資源信息。
func (ctl *UserController) Release() error {
// 如果修改策略信息成功,則刷新ram策略信息。
if ctl.Response().Status() == 200 && ctl.GetParam("bind") != "" {
switch ctl.GetParam("bind") {
case "permission":
ctl.Ram.InitUserBindPermission(ctl.DB)
case "role":
ctl.Ram.InitUserBindRole(ctl.DB)
case "policy":
ctl.Ram.InitUserBindPolicy(ctl.DB)
}
}
return nil
}
// GetRouteParam 方法額外添加bind路由參數信息,用於Release刷新ram。
func (ctl *UserController) GetRouteParam(pkg, name, method string) string {
params := ctl.ControllerWebsite.GetRouteParam(pkg, name, method)
// 添加bind參數
if strings.HasPrefix(method, "PutBind") || strings.HasPrefix(method, "DeleteBind") {
method = strings.TrimPrefix(method, "PutBind")
method = strings.TrimPrefix(method, "DeleteBind")
switch method[0:4] {
case "Perm":
method = "permission"
case "Poli":
method = "policy"
case "Role":
method = "role"
}
params += fmt.Sprintf(" bind=%s", method)
}
return params
}
/*
用戶策略
*/
// GetPolicyNameByName 方法根據策略id獲得綁定的用戶。
func (ctl *UserController) GetPolicyIdById() (interface{}, error) {
return ctl.QueryRows("SELECT * FROM tb_auth_user_policy AS u JOIN tb_auth_policy AS p ON u.policyid = p.id WHERE userid=$1", ctl.GetParam("id"))
}
// GetPolicyNameByName 方法根據策略name獲得綁定的用戶。
func (ctl *UserController) GetPolicyNameByName() (interface{}, error) {
return ctl.QueryRows("SELECT * FROM tb_auth_user_policy AS u JOIN tb_auth_policy AS p ON u.policyid = p.id WHERE userid=(SELECT id FROM tb_auth_user_info WHERE name=$1)", ctl.GetParam("name"))
}
// PutBindPolicyById 方法給用戶批量綁定多條策略。
//
// body: [{"id":4},{"id":6}]
func (ctl *UserController) PutBindPolicyById() error {
err := ctl.ExecBodyWithJSON(fmt.Sprintf("INSERT INTO tb_auth_user_policy(userid,policyid) VALUES(%d,$1);", ctl.GetParamInt("id")), "id")
return err
}
// PutBindPolicyByUidById 方法給指定用戶綁定指定權限。
func (ctl *UserController) PutBindPolicyByUidById() (err error) {
_, err = ctl.Exec("INSERT INTO tb_auth_user_policy(userid,policyid) VALUES($1,$2)", ctl.GetParam("uid"), ctl.GetParam("id"))
return
}
// DeleteBindPolicyById 方法給用戶批量刪除多條策略。
//
// body: [{"id":4},{"id":6}]
func (ctl *UserController) DeleteBindPolicyById() error {
err := ctl.ExecBodyWithJSON(fmt.Sprintf("DELETE FROM tb_auth_user_policy WHERE userid=%s AND policyid=$1", ctl.GetParamInt("id")), "id")
return err
}
// DeleteBindPolicyByUidById 方法給指定用戶刪除指定權限。
func (ctl *UserController) DeleteBindPolicyByUidById() (err error) {
_, err = ctl.Exec("DELETE FROM tb_auth_user_policy WHERE userid=$1 AND policyid=$2", ctl.GetParam("uid"), ctl.GetParam("id"))
return
}
訪問日誌
訪問日誌記錄了請求信息,可以清晰的看到權限相關的行爲。
{"time":"2019-10-02 19:27:14","level":"INFO","fields":{"time":"1.066777ms","route":"/auth/","method":"GET","path":"/auth/","proto":"HTTP/1.1","status":200,"remote":"59.63.178.92","host":"47.52.173.119:8082","size":582,"x-request-id":"294459f1b000000"}}
{"time":"2019-10-02 19:27:14","level":"INFO","fields":{"ram":"ram-pbac","remote":"59.63.178.92","proto":"HTTP/1.1","action":"auth:Permission:GetCount","resource":"/permission/count","x-request-id":"294459f67c00000","method":"GET","status":200,"route":"/api/v1/auth/permission/count","size":36,"x-parent-id":"294459f1b000000","path":"/api/v1/auth/permission/count","host":"47.52.173.119:8082","time":"1.337828ms"}}
{"time":"2019-10-02 19:27:14","level":"INFO","fields":{"remote":"59.63.178.92","proto":"HTTP/1.1","time":"1.695997ms","x-parent-id":"294459f1b000000","method":"GET","action":"auth:Permission:GetIndex","ram":"ram-pbac","x-request-id":"294459f68000000","host":"47.52.173.119:8082","status":200,"route":"/api/v1/auth/permission/index","path":"/api/v1/auth/permission/index","size":225,"resource":"/permission/index"}}
{"time":"2019-10-02 19:27:14","level":"INFO","fields":{"method":"GET","path":"/api/v1/auth/user/icon/name/root","action":"auth:User:GetIconNameByName","route":"/api/v1/auth/user/icon/name/:name","x-request-id":"294459f76c00000","remote":"59.63.178.92","proto":"HTTP/1.1","host":"47.52.173.119:8082","status":200,"time":"1.029588ms","size":12164,"ram":"ram-acl"}}
例如第二條格式化結果:
{
"time": "2019-10-02 19:27:14",
"level": "INFO",
"fields": {
"ram": "ram-pbac",
"remote": "59.63.178.92",
"proto": "HTTP/1.1",
"action": "auth:Permission:GetCount",
"resource": "/permission/count",
"x-request-id": "294459f67c00000",
"method": "GET",
"status": 200,
"route": "/api/v1/auth/permission/count",
"size": 36,
"x-parent-id": "294459f1b000000",
"path": "/api/v1/auth/permission/count",
"host": "47.52.173.119:8082",
"time": "1.337828ms"
}
}
其中部分參數含義:
參數 | 值 | 含義 |
---|---|---|
path | /api/v1/auth/permission/count | http請求路徑 |
route | /api/v1/auth/permission/count | 路由匹配規則 |
action | auth:Permission:GetCount | 處理行爲 |
ram | ram-pbac | ram執行者,如果status非403執行結果爲通過 |
resoure | /permission/count | 資源值,僅pbac存在 |