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)
}
最佳实践
- 密码安全:确保使用强密码哈希算法(Permissions 默认使用 bcrypt)
- 最小权限原则:只授予用户完成工作所需的最小权限
- 定期审计:定期检查用户权限分配情况
- HTTPS:在生产环境中始终使用 HTTPS
- CSRF 保护:为表单操作添加 CSRF 保护
Permissions 库提供了灵活而强大的工具来管理用户权限和安全认证,通过合理的设计和配置,可以为您的 Golang 应用程序构建坚实的安全基础。