mirror of
https://github.com/go-admin-team/go-admin.git
synced 2026-09-20 17:57:54 +00:00
The warning on Authorizator matters to anyone keeping a copy of that file, not to one particular consumer, and it reads better addressed to all of them: check what reads those context keys before taking this change.
198 lines
6.5 KiB
Go
198 lines
6.5 KiB
Go
package handler
|
||
|
||
import (
|
||
"go-admin/common"
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/go-admin-team/go-admin-core/v2/captcha"
|
||
jwt "github.com/go-admin-team/go-admin-core/v2/jwtauth"
|
||
"github.com/go-admin-team/go-admin-core/v2/jwtauth/user"
|
||
"github.com/go-admin-team/go-admin-core/v2/response"
|
||
"github.com/go-admin-team/go-admin-core/v2/sdk"
|
||
"github.com/go-admin-team/go-admin-core/v2/sdk/api"
|
||
"github.com/go-admin-team/go-admin-core/v2/sdk/config"
|
||
"github.com/go-admin-team/go-admin-core/v2/sdk/pkg"
|
||
"github.com/mssola/user_agent"
|
||
"go-admin/common/global"
|
||
)
|
||
|
||
func PayloadFunc(data interface{}) jwt.MapClaims {
|
||
if v, ok := data.(map[string]interface{}); ok {
|
||
u, _ := v["user"].(SysUser)
|
||
r, _ := v["role"].(SysRole)
|
||
return jwt.MapClaims{
|
||
jwt.IdentityKey: u.UserId,
|
||
jwt.RoleIdKey: r.RoleId,
|
||
jwt.RoleKey: r.RoleKey,
|
||
jwt.NiceKey: u.Username,
|
||
jwt.DataScopeKey: r.DataScope,
|
||
jwt.RoleNameKey: r.RoleName,
|
||
// deptid completes what the data-permission scope is decided by,
|
||
// so it can be read from the token instead of joined for on every
|
||
// request. core's user.GetDeptId has always read this claim.
|
||
"deptid": u.DeptId,
|
||
}
|
||
}
|
||
return jwt.MapClaims{}
|
||
}
|
||
|
||
func IdentityHandler(c *gin.Context) interface{} {
|
||
claims := jwt.ExtractClaims(c)
|
||
return map[string]interface{}{
|
||
"IdentityKey": claims["identity"],
|
||
"UserName": claims["nice"],
|
||
"RoleKey": claims["rolekey"],
|
||
"UserId": claims["identity"],
|
||
"RoleIds": claims["roleid"],
|
||
"DataScope": claims["datascope"],
|
||
}
|
||
}
|
||
|
||
// Authenticator 获取token
|
||
// @Summary 登陆
|
||
// @Description 获取token
|
||
// @Description LoginHandler can be used by clients to get a jwt token.
|
||
// @Description Payload needs to be json in the form of {"username": "USERNAME", "password": "PASSWORD"}.
|
||
// @Description Reply will be of the form {"token": "TOKEN"}.
|
||
// @Description dev mode:It should be noted that all fields cannot be empty, and a value of 0 can be passed in addition to the account password
|
||
// @Description 注意:开发模式:需要注意全部字段不能为空,账号密码外可以传入0值
|
||
// @Tags 登陆
|
||
// @Accept application/json
|
||
// @Product application/json
|
||
// @Param account body Login true "account"
|
||
// @Success 200 {string} string "{"code": 200, "expire": "2019-08-07T12:45:48+08:00", "token": ".eyJleHAiOjE1NjUxNTMxNDgsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU2NTE0OTU0OH0.-zvzHvbg0A" }"
|
||
// @Router /api/v1/login [post]
|
||
func Authenticator(c *gin.Context) (interface{}, error) {
|
||
log := api.GetRequestLogger(c)
|
||
db, err := pkg.GetOrm(c)
|
||
if err != nil {
|
||
log.Errorf("get db error, %s", err.Error())
|
||
response.Error(c, 500, err, "数据库连接获取失败")
|
||
return nil, jwt.ErrFailedAuthentication
|
||
}
|
||
|
||
var loginVals Login
|
||
var status = "2"
|
||
var msg = "登录成功"
|
||
var username = ""
|
||
defer func() {
|
||
LoginLogToDB(c, status, msg, username)
|
||
}()
|
||
|
||
if err = c.ShouldBind(&loginVals); err != nil {
|
||
username = loginVals.Username
|
||
msg = "数据解析失败"
|
||
status = "1"
|
||
|
||
return nil, jwt.ErrMissingLoginValues
|
||
}
|
||
if config.ApplicationConfig.Mode != "dev" {
|
||
if !captcha.Verify(loginVals.UUID, loginVals.Code, true) {
|
||
username = loginVals.Username
|
||
msg = "验证码错误"
|
||
status = "1"
|
||
|
||
return nil, jwt.ErrInvalidVerificationode
|
||
}
|
||
}
|
||
sysUser, role, e := loginVals.GetUser(db)
|
||
if e == nil {
|
||
username = loginVals.Username
|
||
|
||
return map[string]interface{}{"user": sysUser, "role": role}, nil
|
||
} else {
|
||
msg = "登录失败"
|
||
status = "1"
|
||
log.Warnf("%s login failed!", loginVals.Username)
|
||
}
|
||
return nil, jwt.ErrFailedAuthentication
|
||
}
|
||
|
||
// LoginLogToDB Write log to database
|
||
func LoginLogToDB(c *gin.Context, status string, msg string, username string) {
|
||
if !config.LoggerConfig.EnabledDB {
|
||
return
|
||
}
|
||
log := api.GetRequestLogger(c)
|
||
l := make(map[string]interface{})
|
||
|
||
ua := user_agent.New(c.Request.UserAgent())
|
||
l["ipaddr"] = common.GetClientIP(c)
|
||
l["loginLocation"] = "" // pkg.GetLocation(common.GetClientIP(c),gaConfig.ExtConfig.AMap.Key)
|
||
l["loginTime"] = pkg.GetCurrentTime()
|
||
l["status"] = status
|
||
l["remark"] = c.Request.UserAgent()
|
||
browserName, browserVersion := ua.Browser()
|
||
l["browser"] = browserName + " " + browserVersion
|
||
l["os"] = ua.OS()
|
||
l["platform"] = ua.Platform()
|
||
l["username"] = username
|
||
l["msg"] = msg
|
||
|
||
q := sdk.Runtime.GetQueuePrefix(c.Request.Host)
|
||
message, err := sdk.Runtime.GetStreamMessage("", global.LoginLog, l)
|
||
if err != nil {
|
||
log.Errorf("GetStreamMessage error, %s", err.Error())
|
||
//日志报错错误,不中断请求
|
||
} else {
|
||
err = q.Append(message)
|
||
if err != nil {
|
||
log.Errorf("Append message error, %s", err.Error())
|
||
}
|
||
}
|
||
}
|
||
|
||
// LogOut
|
||
// @Summary 退出登录
|
||
// @Description 获取token
|
||
// LoginHandler can be used by clients to get a jwt token.
|
||
// Reply will be of the form {"token": "TOKEN"}.
|
||
// @Accept application/json
|
||
// @Product application/json
|
||
// @Success 200 {string} string "{"code": 200, "msg": "成功退出系统" }"
|
||
// @Router /logout [post]
|
||
// @Security Bearer
|
||
func LogOut(c *gin.Context) {
|
||
LoginLogToDB(c, "2", "退出成功", user.GetUserName(c))
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 200,
|
||
"msg": "退出成功",
|
||
})
|
||
|
||
}
|
||
|
||
// Authorizator decides whether a parsed identity may proceed. It authorizes
|
||
// every identity IdentityHandler was able to build, which is what it has always
|
||
// done.
|
||
//
|
||
// It used to also assert data["user"] and data["role"] into app/admin/models
|
||
// types and copy five fields onto the context. Those two keys are not in the
|
||
// map: IdentityHandler builds it from the token claims and puts in
|
||
// IdentityKey / UserName / RoleKey / UserId / RoleIds / DataScope. Both
|
||
// assertions therefore failed on every request, and because the ok result was
|
||
// discarded, the five c.Set calls stored zero values and the function returned
|
||
// true regardless.
|
||
//
|
||
// Nothing in this repository or in go-admin-core reads role / roleIds /
|
||
// userId / userName / dataScope off the context - the open-source data
|
||
// permission path reads the JWT claims through
|
||
// common/actions.Permission -> user.GetUserIdStr(c). Dropping the block
|
||
// therefore removes five zero values nobody read, and with them the last
|
||
// import of app/admin from a contract package.
|
||
//
|
||
// Anything maintaining its own copy of this file must check its own consumers
|
||
// before taking this change: a codebase that does read those keys off the
|
||
// context needs Authorizator to keep setting them.
|
||
func Authorizator(data interface{}, c *gin.Context) bool {
|
||
_, ok := data.(map[string]interface{})
|
||
return ok
|
||
}
|
||
|
||
func Unauthorized(c *gin.Context, code int, message string) {
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": code,
|
||
"msg": message,
|
||
})
|
||
}
|