后端Go框架:
这是我参与【第五届青训营】伴学笔记创作活动的第10天。
12.Cookie的缺点
- 不安全,明文
- 增加带宽消耗
- 可以被禁用
- cookie有上限
13.Sessions
主要功能是:
- 简单的API:将其用作设置签名(以及可选的加密)cookie的简便方法。
- 内置的后端可将session存储在cookie或文件系统中。
- Flash消息:一直持续读取的session值。
- 切换session持久性(又称“记住我”)和设置其他属性的便捷方法。
- 旋转身份验证和加密密钥的机制。
- 每个请求有多个session,即使使用不同的后端也是如此。
- 自定义session后端的接口和基础结构:可以使用通用API检索并批量保存来自不同商店的session。
// 初始化一个cookie存储对象
// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func main() {
http.HandleFunc("/save", SaveSession)
http.HandleFunc("/get", GetSession)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("HTTP server failed,err:", err)
return
}
}
func SaveSession(w http.ResponseWriter, r *http.Request) {
// Get a session. We're ignoring the error resulted from decoding an
// existing session: Get() always returns a session, even if empty.
// 获取一个session对象,session-name是session的名字
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 在session中存储值
session.Values["foo"] = "bar"
session.Values[42] = 43
// 保存更改
session.Save(r, w)
}
func GetSession(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, "session-name")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
foo := session.Values["foo"]
fmt.Println(foo)
}
14.结构体自定义验证
/*
对绑定解析到结构体上的参数,自定义验证功能
比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法 binding 现成的方法
需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions)
这里需要下载引入下 gopkg.in/go-playground/validator.v8
*/
type Person struct {
Age int `form:"age" binding:"required,gt=10"`
// 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称
Name string `form:"name" binding:"NotNullAndAdmin"`
Address string `form:"address" binding:"required"`
}
// 1、自定义的校验方法
func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
if value, ok := field.Interface().(string); ok {
// 字段不能为空,并且不等于 admin
return value != "" && !("5lmh" == value)
}
return true
}
func main() {
r := gin.Default()
// 3、将我们自定义的校验方法注册到 validator中
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
// 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key
v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin)
}
/*
curl -X GET "http://127.0.0.1:8080/testing?name=&age=12&address=beijing"
curl -X GET "http://127.0.0.1:8080/testing?name=lmh&age=12&address=beijing"
curl -X GET "http://127.0.0.1:8080/testing?name=adz&age=12&address=beijing"
*/
r.GET("/5lmh", func(c *gin.Context) {
var person Person
if e := c.ShouldBind(&person); e == nil {
c.String(http.StatusOK, "%v", person)
} else {
c.String(http.StatusOK, "person bind err:%v", e.Error())
}
})
15.日志文件
func main() {
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// 如果需要同时将日志写入文件和控制台,请使用以下代码。
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run()
}
16.生成解析token
//自定义一个字符串
var jwtkey = []byte("www.topgoer.com")
var str string
type Claims struct {
UserId uint
jwt.StandardClaims
}
func main() {
r := gin.Default()
r.GET("/set", setting)
r.GET("/get", getting)
//监听端口默认为8080
r.Run(":8080")
}
//颁发token
func setting(ctx *gin.Context) {
expireTime := time.Now().Add(7 * 24 * time.Hour)
claims := &Claims{
UserId: 2,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireTime.Unix(), //过期时间
IssuedAt: time.Now().Unix(),
Issuer: "127.0.0.1", // 签名颁发者
Subject: "user token", //签名主题
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// fmt.Println(token)
tokenString, err := token.SignedString(jwtkey)
if err != nil {
fmt.Println(err)
}
str = tokenString
ctx.JSON(200, gin.H{"token": tokenString})
}
//解析token
func getting(ctx *gin.Context) {
tokenString := ctx.GetHeader("Authorization")
//vcalidate token formate
if tokenString == "" {
ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
ctx.Abort()
return
}
token, claims, err := ParseToken(tokenString)
if err != nil || !token.Valid {
ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
ctx.Abort()
return
}
fmt.Println(111)
fmt.Println(claims.UserId)
}
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
Claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, Claims, func(token *jwt.Token) (i interface{}, err error) {
return jwtkey, nil
})
return token, Claims, err
}
17.权限管理
- Casbin是用于Golang项目的功能强大且高效的开源访问控制库。
特征:
Casbin的作用:
- 以经典{subject, object, action}形式或您定义的自定义形式实施策略,同时支持允许和拒绝授权。
- 处理访问控制模型及其策略的存储。
- 管理角色用户映射和角色角色映射(RBAC中的角色层次结构)。
- 支持内置的超级用户,例如root或administrator。超级用户可以在没有显式权限的情况下执行任何操作。
- 多个内置运算符支持规则匹配。例如,keyMatch可以将资源键映射/foo/bar到模式
/foo*。
Casbin不执行的操作:
- 身份验证(又名验证username以及password用户登录时)
- 管理用户或角色列表。我相信项目本身管理这些实体会更方便。用户通常具有其密码,而Casbin并非设计为密码容器。但是,Casbin存储RBAC方案的用户角色映射。