golang用户权限管理与安全认证插件库permissions的使用

Golang用户权限管理与安全认证插件库permissions的使用

Permissions是一个用于跟踪用户、登录状态和权限的中间件库,是permissions2的延续版本。

功能特点与限制

  • 使用安全cookie并将用户信息存储在Redis数据库中
  • 适合运行本地Redis服务器、注册/确认用户和管理公共/用户/管理员页面
  • 支持连接到远程Redis服务器
  • 不支持SQL数据库(如需MariaDB/MySQL支持可使用permissionsql)
  • 支持注册和通过生成的确认码进行确认
  • 默认支持public、user和admin三种权限级别
  • 支持Chi、Negroni、Martini、Gin、Goji和原生net/http框架

要求

  • Redis >= 2.6.12(或开源Redis克隆)
  • Go >= 1.21

完整示例代码

使用Chi框架的示例

package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"

    "github.com/go-chi/chi/v5"
    "github.com/xyproto/permissions"
)

func main() {
    m := chi.NewRouter()

    // 新建权限中间件
    perm, err := permissions.New2()
    if err != nil {
        log.Fatalln(err)
    }

    // 获取userstate,用于下面的处理程序
    userstate := perm.UserState()

    // 为Chi设置中间件处理程序
    m.Use(perm.Middleware)

    m.Get("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
        fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
        fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
        fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.Username(req))
        fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
        fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
        fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
    })

    m.Get("/register", func(w http.ResponseWriter, r *http.Request) {
        userstate.AddUser("bob", "hunter1", "bob@zombo.com")
        fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
    })

    m.Get("/confirm", func(w http.ResponseWriter, r *http.Request) {
        userstate.MarkConfirmed("bob")
        fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
    })

    m.Get("/remove", func(w http.ResponseWriter, r *http.Request) {
        userstate.RemoveUser("bob")
        fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
    })

    m.Get("/login", func(w http.ResponseWriter, r *http.Request) {
        userstate.Login(w, "bob")
        fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
    })

    m.Get("/logout", func(w http.ResponseWriter, r *http.Request) {
        userstate.Logout("bob")
        fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
    })

    m.Get("/makeadmin", func(w http.ResponseWriter, r *http.Request) {
        userstate.SetAdminStatus("bob")
        fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
    })

    m.Get("/clear", func(w http.ResponseWriter, r *http.Request) {
        userstate.ClearCookie(w)
        fmt.Fprintf(w, "Clearing cookie")
    })

    m.Get("/data", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "user page that only logged in users must see!")
    })

    m.Get("/admin", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
        if usernames, err := userstate.AllUsernames(); err == nil {
            fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
        }
    })

    // 权限被拒绝时的自定义处理程序
    perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
        http.Error(w, "Permission denied!", http.StatusForbidden)
    })

    // 启动服务
    http.ListenAndServe(":3000", m)
}

使用原生net/http的示例

package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"
    "time"

    "github.com/xyproto/permissions"
    "github.com/xyproto/pinterface"
)

type permissionHandler struct {
    // perm是一个Permissions结构,可用于拒绝请求和获取UserState
    perm pinterface.IPermissions

    // HTTP多路复用器
    mux *http.ServeMux
}

// 实现ServeHTTP方法使permissionHandler成为http.Handler
func (ph *permissionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    // 检查用户是否具有正确的admin/user权限
    if ph.perm.Rejected(w, req) {
        // 通过调用自定义"permission denied"函数通知用户
        ph.perm.DenyFunction()(w, req)
        // 拒绝请求
        return
    }
    // 如果权限被授予,则提供请求的页面
    ph.mux.ServeHTTP(w, req)
}

func main() {
    mux := http.NewServeMux()

    // 新建权限中间件
    perm, err := permissions.New2()
    if err != nil {
        log.Fatalln(err)
    }

    // 获取userstate,用于下面的处理程序
    userstate := perm.UserState()

    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
        fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
        fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
        fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.Username(req))
        fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
        fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
        fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
    })

    mux.HandleFunc("/register", func(w http.ResponseWriter, req *http.Request) {
        userstate.AddUser("bob", "hunter1", "bob@zombo.com")
        fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
    })

    mux.HandleFunc("/confirm", func(w http.ResponseWriter, req *http.Request) {
        userstate.MarkConfirmed("bob")
        fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
    })

    mux.HandleFunc("/remove", func(w http.ResponseWriter, req *http.Request) {
        userstate.RemoveUser("bob")
        fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
    })

    mux.HandleFunc("/login", func(w http.ResponseWriter, req *http.Request) {
        userstate.Login(w, "bob")
        fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
    })

    mux.HandleFunc("/logout", func(w http.ResponseWriter, req *http.Request) {
        userstate.Logout("bob")
        fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
    })

    mux.HandleFunc("/makeadmin", func(w http.ResponseWriter, req *http.Request) {
        userstate.SetAdminStatus("bob")
        fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
    })

    mux.HandleFunc("/clear", func(w http.ResponseWriter, req *http.Request) {
        userstate.ClearCookie(w)
        fmt.Fprintf(w, "Clearing cookie")
    })

    mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "user page that only logged in users must see!")
    })

    mux.HandleFunc("/admin", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
        if usernames, err := userstate.AllUsernames(); err == nil {
            fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
        }
    })

    // 权限被拒绝时的自定义处理程序
    perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
        http.Error(w, "Permission denied!", http.StatusForbidden)
    })

    // 配置HTTP服务器和permissionHandler结构
    s := &http.Server{
        Addr:           ":3000",
        Handler:        &permissionHandler{perm, mux},
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }

    log.Println("Listening for requests on port 3000")

    // 开始监听
    log.Fatal(s.ListenAndServe())
}

默认权限

  • 访问/admin路径前缀要求用户具有管理员权限
  • 这些路径前缀要求用户登录:/repo/data
  • 这些路径前缀默认公开://login/register/style/img/js/favicon.ico/robots.txt/sitemap_index.xml

密码哈希

  • 默认使用bcrypt进行密码哈希,也支持sha256
  • 默认所有新密码都将使用bcrypt进行哈希处理
  • 为了向后兼容,长度与sha256哈希相同的旧密码哈希将使用sha256进行检查

用户属性设置与获取

设置属性:

username := "bob"
propertyName := "clever"
propertyValue := "yes"

userstate.Users().Set(username, propertyName, propertyValue)

获取属性:

username := "bob"
propertyName := "clever"
propertyValue, err := userstate.Users().Get(username, propertyName)
if err != nil {
    log.Print(err)
    return err
}
fmt.Printf("%s is %s: %s\n", username, propertyName, propertyValue)

版本信息

  • 版本:1.0.0
  • 许可证:BSD-3

更多关于golang用户权限管理与安全认证插件库permissions的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang用户权限管理与安全认证插件库permissions的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 权限管理与安全认证插件库 Permissions 使用指南

概述

Permissions 是一个用于 Golang 的轻量级权限管理和安全认证库,它提供了简单易用的 API 来实现基于角色的访问控制(RBAC)和其他安全功能。

安装

go get github.com/xyproto/permissions2

基本使用

初始化权限管理器

package main

import (
    "github.com/xyproto/permissions2"
    "net/http"
)

func main() {
    // 创建新的权限管理器
    perm := permissions.New()
    
    // 设置数据库路径(用于存储用户和权限数据)
    perm.SetDatabasePath("/tmp/permissions.db")
    
    // 初始化数据库
    err := perm.Init()
    if err != nil {
        panic(err)
    }
}

用户管理

// 添加新用户
err := perm.AddUser("alice", "password123", "user@example.com")
if err != nil {
    // 处理错误
}

// 验证用户凭据
valid, err := perm.UserState().CorrectPassword("alice", "password123")
if err != nil {
    // 处理错误
}
if valid {
    // 用户认证成功
}

// 删除用户
err = perm.RemoveUser("alice")
if err != nil {
    // 处理错误
}

角色和权限管理

// 添加新角色
err := perm.AddRole("admin")
if err != nil {
    // 处理错误
}

// 为用户分配角色
err = perm.SetUserRole("alice", "admin")
if err != nil {
    // 处理错误
}

// 检查用户是否具有特定角色
isAdmin, err := perm.UserHasRole("alice", "admin")
if err != nil {
    // 处理错误
}
if isAdmin {
    // 用户是管理员
}

// 创建自定义权限
err = perm.AddPermission("edit_content")
if err != nil {
    // 处理错误
}

// 为角色分配权限
err = perm.SetRolePermission("admin", "edit_content", true)
if err != nil {
    // 处理错误
}

// 检查用户是否具有特定权限
canEdit, err := perm.UserHasPermission("alice", "edit_content")
if err != nil {
    // 处理错误
}
if canEdit {
    // 用户可以编辑内容
}

与 HTTP 集成

func adminHandler(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Admin area")
}

func main() {
    mux := http.NewServeMux()
    
    // 普通路由
    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "Public area")
    })
    
    // 受保护的路由 - 需要登录
    mux.HandleFunc("/user", perm.UserWrap(func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "User area")
    }))
    
    // 受保护的路由 - 需要特定角色
    mux.HandleFunc("/admin", perm.AdminWrap(adminHandler))
    
    // 启动服务器
    http.ListenAndServe(":8080", mux)
}

高级功能

自定义权限检查中间件

func requirePermission(perm *permissions.Permissions, permission string) http.HandlerFunc {
    return func(w http.ResponseWriter, req *http.Request) {
        username := perm.Username(req)
        if username == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        
        hasPermission, err := perm.UserHasPermission(username, permission)
        if err != nil {
            http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            return
        }
        
        if !hasPermission {
            http.Error(w, "Forbidden", http.StatusForbidden)
            return
        }
        
        // 继续处理请求
        next.ServeHTTP(w, req)
    }
}

// 使用自定义中间件
mux.HandleFunc("/edit", requirePermission(perm, "edit_content")(editHandler))

会话管理

// 用户登录
func loginHandler(w http.ResponseWriter, req *http.Request) {
    username := req.FormValue("username")
    password := req.FormValue("password")
    
    valid, err := perm.UserState().CorrectPassword(username, password)
    if err != nil || !valid {
        http.Error(w, "Invalid credentials", http.StatusUnauthorized)
        return
    }
    
    // 创建会话
    err = perm.UserState().SetUsernameCookie(w, username)
    if err != nil {
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    
    http.Redirect(w, req, "/dashboard", http.StatusFound)
}

// 用户登出
func logoutHandler(w http.ResponseWriter, req *http.Request) {
    // 清除会话
    perm.UserState().ClearCookie(w)
    http.Redirect(w, req, "/", http.StatusFound)
}

最佳实践

  1. 密码安全:确保使用强密码哈希算法(Permissions 默认使用 bcrypt)
  2. 最小权限原则:只授予用户完成工作所需的最小权限
  3. 定期审计:定期检查用户权限分配情况
  4. HTTPS:在生产环境中始终使用 HTTPS
  5. CSRF 保护:为表单操作添加 CSRF 保护

Permissions 库提供了灵活而强大的工具来管理用户权限和安全认证,通过合理的设计和配置,可以为您的 Golang 应用程序构建坚实的安全基础。

回到顶部