这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记。基于上课所讲内容,查阅官方文档,通过b站课程进行了简单的gin框架学习以进行抖音项目开发。
日志和日志格式 为什么要使用日志?
记录参数信息 猜测用户行为 复现系统 bug 并修复 Gin 中自带日志写入的中间件,但是自定义比较麻烦,不推荐使用。
第三方日志工具:
go-logging logrus zap 日志切割:
自行根据时间在写入时进行切割日志 借助现成的日志包:go-file-rotatelogs、file-rotatelogs
jwt-go 什么是 jwt?
全称 JSON WEB TOKEN 一种后台不做存储的前端身份验证的工具 分为三部分 Header Claims Signature 也有的地方分成:Header Paload Signature
引入 jwt-go 第三方库:
import "github.com/dgrijalva/jwt-go
创建一个 JWT 通常使用 NewWithClaims,因为我们可以通过匿名结构体来实现 Claims 接口,从而携带自己的参数。
源码中的 StandardClaims 结构体:
// Structured version of Claims Section, as referenced at
// https://tools.ietf.org/html/rfc7519#section-4.1
// See examples for how to use this with your own claim types
type StandardClaims struct {
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}
其中最重要的三个参数:NoteBefore 生效时间、ExpiresAt 过期时间、Issuer 签发者
两种常用的 Claims 的实现方式:
- 自定义结构体嵌入接口
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
创建一个 Token:
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c) // 创建
s, err := t.SignedString(mySigningKey) // 签发
解析一个 JWT
主要通过以下方法实现:
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error)
其中 keyFunc 是个特殊的回调函数,固定接受 *Token 类型指针,返回一个 i 和 err,i 就是我们的密钥
示例:创建和解析 JWT
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
func main() {
// 创建一个 JWT
mySigningKey := []byte("woxiangbianqiang!")
c := MyClaims{
Username: "qimiao",
StandardClaims: jwt.StandardClaims{
NotBefore: time.Now().Unix() - 60, // 生效时间 1 分钟
ExpiresAt: time.Now().Unix() + 2*60*60, // 失效时间 2 小时
Issuer: "lzy", // 签发者
},
}
t := jwt.NewWithClaims(jwt.SigningMethodHS256, c) // 创建 Token
s, _ := t.SignedString(mySigningKey) // 签发 Token
fmt.Println(s) // token 的字符串
// 解析 JWT
token, err := jwt.ParseWithClaims(s, &MyClaims{},
func(t *jwt.Token) (interface{}, error) {
return mySigningKey, nil
})
if err != nil {
fmt.Println(err)
}
// 对 Claims 进行断言并使用
fmt.Println(token.Claims.(*MyClaims))
}
Casbin 模型基础 Casbin 工作原理
在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。
PERM 模式由四个基础组成,描述资源与用户之间的关系:
Request 请求,定义了我们应该提供访问控制匹配功能的参数名称和顺序。
例如:r = {sub, obj, act}
Policy 政策,在政策规则文件中定义字段的名称和顺序。
例如:p = {sub, obj, act} 或 p = {sub, obj, act, eft}
注:如果未定义 eft,则策略文件中的结果字段将不会被读取,匹配的策略结果将默认被允许
Matcher 匹配规则,匹配请求和政策的规则。
例如:m = r.sub == p.sub && r.act == p.act && r.obj == p.obj
Effect 影响,对匹配结果再次作出逻辑组合判断。
例如:e = some (where (p.eft == allow))
Casbin 中最基本、最简单的 model 是 ACL。ACL 中的 model 配置为:
# Request definition
[request_definition]
r = sub, obj, act
# Policy definition
[policy_definition]
p = sub, obj, act
# Policy effect
[policy_effect]
e = some(where (p.eft == allow))
# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
ACL 模型的一个示例策略类似:
p, alice, data1, read
p, bob, data2, write
它意味着:
- alice 可以读取 data1
- bob 可以读取 data2
RBAC 在模型中使用 RBAC 角色:基于角色的控制访问
在 编辑器 中选择 RBAC 模型。
实战模型 以角色为基础:
[request_definition]
r = sub, obj, act
# 请求入参(实体,资源,方法)
[policy_definition]
p = sub, obj, act
# 策略(实体,资源,方法)
[role_definition]
g = _, _
# 这个情况下 g 写啥都行,因为 match 里没有涉及到 g
# 不过规范一点,按照角色权限,这里 g 接收两个参数
# g = 用户, 角色
[policy_effect]
e = some(where(p.eft==allow))
# 看看经过下面那些匹配规则后的返回值
# 是否有一条等于里面那个 allow
[matchers]
m = r.sub == p.sub && ParamsMatch(r.obj, p.obj) && r.act == p.act
# 进来的 实体、资源、方法 能不能在 权限表(p) 里面找到一个一模一样的
多租户模型:
参考:域内 RBAC
r = sub, dom, obj, act
# 请求入参(实体,域【商户】,资源,方法)
[policy_definition]
p = sub, dom, obj, act
# 权限模型(实体,域【商户】,资源,方法)
[role_definition]
g = _, _, _
# 域匹配规则,后面g会说,这里意思是g接受3个参数
[policy_effect]
e = some(where(p.eft==allow))
# 看看经过下面那些匹配规则后的返回值
# 是否有一条等于里面那个 allow
[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
# 先列出来一个权限定义里面的东西 g, qm, teacher, classOne
# 然后用g做一些解析动作
# 一般我们会收到前端的入参, 大概长这样 "qm", "classOne", "/base/api", "get"
# 上面我们定义的g就是我们需要的模型