1 Star 0 Fork 0

dpnogo/iam

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
auth.go 7.88 KB
一键复制 编辑 原始数据 按行查看 历史
dpnogo 提交于 2022-03-07 06:22 . 修改部分内容
package apiserver
import (
"encoding/base64"
"gitee.com/GomiTo/iam/pkg/code"
"gitee.com/GomiTo/iam/pkg/errors"
metav1 "gitee.com/GomiTo/iam/pkg/marmotedu/meta/v1"
"strings"
"context"
"gitee.com/GomiTo/iam/internal/apiserver/store"
"gitee.com/GomiTo/iam/pkg/log"
v1 "gitee.com/GomiTo/iam/pkg/marmotedu/api/apiserver/v1"
"gitee.com/GomiTo/iam/pkg/middleware"
"gitee.com/GomiTo/iam/pkg/middleware/auth"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"net/http"
"time"
jwt "github.com/appleboy/gin-jwt/v2"
)
// 调用 middleware 中的接口 进行使用
const (
APIServerIssuer = "iam-server"
APIServerAudience = "iam-client"
authStrCount = 2
)
type loginInfo struct {
Username string `form:"username" json:"username" ` //binding:"required,username"
Password string `form:"password" json:"password" ` //binding:"required,password"
}
// 返回接口 (Basic和jwt)
func newAuto()middleware.AuthStrategy {
return auth.NewAutoStrategy(newBasic().(auth.BasicStrategy),newJWT().(auth.JwtStrategy))
}
// basic
func newBasic() middleware.AuthStrategy {
return auth.NewBasicStrategy(verification)
}
// 从mysql中验证用户名和密码是否正确
func verification (username string, password string) bool {
uinfer:=store.Client().Users()
// 然后从其中得到密码
user,err:=uinfer.Get(context.Background(),username,metav1.GetOptions{})
if err != nil {
log.Errf("verification user get err %v",err)
return false
}
// 验证用户名是否相等
if err := user.Compare(password); err != nil {
return false
}
return true
}
// jwt 验证
func newJWT()middleware.AuthStrategy {
// 使用 jwt-go 中间件 viper中的参数在NewApp中得到
jwtdata,_:=jwt.New(&jwt.GinJWTMiddleware{
Realm: viper.GetString("jwt.Realm"), // 从配置文件获取参数 可以理解成该中间件的名称,用于展示,默认值为gin jwt
SigningAlgorithm: "HS256", //签名算法,默认值为HS256
Key: []byte(viper.GetString("jwt.key")), // key的值 --> 加密为对称加密
Timeout: viper.GetDuration("jwt.timeout"), // token过期时间,默认值为time.Hour
MaxRefresh: viper.GetDuration("jwt.max-refresh"), // 最大刷新时间
Authenticator: authenticator(), // 校验用户名和密码是否合法 --> Basic 认证 base64那个
LoginResponse: loginResponse(), // 完成登录后返回的信息,用户可自定义返回数据
LogoutResponse: func(c *gin.Context, code int) { // 注销时如何进行返回
c.JSON(http.StatusOK, nil)
},
RefreshResponse: refreshResponse(), // 重置token后返回的response
PayloadFunc: payloadFunc(), // 登录期间的回调的函数 赋值载荷相关内容 验证时候需要其中负载中的内容(identity)
IdentityHandler: func(c *gin.Context) interface{} { // 解析claims值 得到 identity 值
claims := jwt.ExtractClaims(c) // 需要存在这个用户名才说明是正常的 验证jwt时候使用 (获取)
return claims[jwt.IdentityKey] // 返回用户名
},
IdentityKey: auth.UsernameKey,
Authorizator: authorizator(), // 判断jwt.IdentityKey值是否存在 存在则说明此时能够拿到username正常
Unauthorized: func(c *gin.Context, code int, message string) { //处理不进行授权的逻辑
c.JSON(code, gin.H{
"message": message,
})
},
TokenLookup: "header: Authorization, query: token, cookie: jwt", //token检索模式,用于提取token,默认值为header:Authorization。
TokenHeadName: "Bearer", //token在请求头时的名称,默认值为Bearer
SendCookie: true,
TimeFunc: time.Now,
})
return auth.NewJwtStrategy(*jwtdata)
}
// 验证用户账户密码是否正确
func authenticator()func(c *gin.Context) (interface{}, error) {
return func(c *gin.Context) (interface{}, error){
// 使用 c 返回 loginInfo 判断是否满足要求
var login loginInfo
var err error
// 从头部 根据情况获取
if c.Request.Header.Get("Authorization") != "" { // head 登录
login, err = parseWithHeader(c)
} else {
//curl -s -XPOST -H'Content-Type: application/json' -d'{"username":"admin","password":"Admin@2021"}' http://127.0.0.1:8080/login
// # 用户名和密码在HTTP Body中传递,因为密码是明文,所以这里不建议实际开发中,使用这种方式。
// post 登录
login, err = parseWithBody(c)
}
// log.Infof("%s",login)
userInfo,err:=store.Client().Users().Get(context.Background(),login.Username,metav1.GetOptions{})
if err != nil {
log.Errf("get user information failed: %s", err.Error())
return "", jwt.ErrFailedAuthentication
}
log.Infof("%v",err)
// 验证二者用户密码是否正确
if err = userInfo.Compare(login.Password);err != nil {
return "", jwt.ErrFailedAuthentication
}
return userInfo,nil
}
}
// 登录成功后 返回响应
func loginResponse()func(c *gin.Context, code int, message string, time time.Time) {
return func(c *gin.Context, code int, token string, expire time.Time){
c.JSON(http.StatusOK, gin.H{
"token": token,
"expire": expire.Format(time.RFC3339),
})
}
}
// 重置 jwt 后返回的响应
func refreshResponse()func(c *gin.Context, code int, message string, time time.Time) {
return func(c *gin.Context, code int, token string, expire time.Time){
c.JSON(http.StatusOK, gin.H{
"token": token,
"expire": expire.Format(time.RFC3339),
})
}
}
// 登录后 range mw.PayloadFunc(data) 赋值给-> claims[key] 赋值载荷
// 得到载荷
func payloadFunc() func(data interface{}) jwt.MapClaims {
return func(data interface{}) jwt.MapClaims {
claims := jwt.MapClaims{ // Payload(载荷) 中的内容
"iss": APIServerIssuer, // jwt 的签发者
"aud": APIServerAudience, // 接收 jwt的一方
}
// 从数据库中返回的user信息
if u, ok := data.(*v1.User); ok { // 这里需要注意 ****** 因为 当时v1包地址不一样所以导致这里(自己iam和老师的iam)无法赋值
claims[jwt.IdentityKey] = u.Name //claims["identity"] = "用户名" identity(身份)
claims["sub"] = u.Name // sub 主题 用来鉴别一个用户
}
return claims
}
}
// 验证时候调用 若c中无"identity"(实际用户名)参数则说明验证失败
func authorizator() func(data interface{}, c *gin.Context) bool {
return func(data interface{}, c *gin.Context) bool {
if v, ok := data.(string); ok { // 这里的v为实际用户名
// c.Set(log.KeyUsername, v)
// c.Set(log.KeyRequestID, v) // 打印日志中带有 context中的 username和requestID的值
log.L(c).Infof("user `%s` is authenticated.", v)
return true
}
return false
}
}
// head loginInfo
func parseWithHeader(c *gin.Context)(loginInfo,error) {
authStr:=c.Request.Header.Get("Authorization")
strs:=strings.Split(authStr," ")
if len(strs)!=authStrCount||strs[0]!="Basic"{ // 若验证参数不符合条件
log.Errf("get basic string from Authorization header failed")
return loginInfo{},errors.WithCode(code.ErrInvalidBasic, "Authorization Basic format is wrong.")
}
// 因为传来的信息为base64编码过的,所以需要先进行解码
payload, err := base64.StdEncoding.DecodeString(strs[1])
if err != nil {
log.Errf("decode basic string: %s", err.Error())
return loginInfo{}, jwt.ErrFailedAuthentication
}
// 长度不符合条件 || 用户或者密码错误
userinfo := strings.SplitN(string(payload), ":", 2) //然后得到 账号:密码 [账号,密码]
if len(userinfo) != 2 { //若
log.Errf("parse payload failed")
return loginInfo{}, jwt.ErrFailedAuthentication
}
return loginInfo{
Username: userinfo[0],
Password: userinfo[1],
}, nil
}
// body
func parseWithBody(c *gin.Context)(loginInfo,error) {
userInfo:=loginInfo{}
err:=c.ShouldBind(&userInfo) // shouldBind 表单(从表单绑定) ShouldBindJSON 消息体参数
if err != nil {
log.Errf("parse login parameters: %s", err.Error())
return loginInfo{}, jwt.ErrFailedAuthentication
}
return userInfo,nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/GomiTo/iam.git
git@gitee.com:GomiTo/iam.git
GomiTo
iam
iam
455e706b39fb

搜索帮助