GoAdminGroup / go-admin

A golang framework helps gopher to build a data visualization and admin panel in ten minutes
https://www.go-admin.com
Apache License 2.0
8.21k stars 1.34k forks source link

[Question]go-admin如何设置免登录页面 #369

Open kingway126 opened 3 years ago

kingway126 commented 3 years ago

问题描述 [详细地描述问题,让大家都能理解]

我创建了一个免登陆页面,但是系统报错了。
Panic的地方是:GoAdminGroup/go-admin/engine/engine.go:557
报错原因:因为ctx.UserValue["user"]的值为空,导致auth.Auth(ctx)方法中的断言失败。

示例代码 [如果有必要,展示代码,线上示例,或仓库]

eng.HTMLFile("GET", "/admin/hello", "./html/hello.tmpl", map[string]interface{}{
    "msg": "Hello world",
}, true)

hello.tmpl

复现方法

在 https://github.com/GoAdminGroup/example 项目的main.go中设置eng.HTML或者eng.HTMLFile方法的参数noAuth=true 即可

版本信息:

GoAdmin 版本:v1.2.17
golang 版本:v1.14

其他信息[如截图等其他信息可以贴在这里]

具体报错如下:

`2020-11-30T18:01:25.715+0800 ?[31mERROR?[0m engine/engine.go:306 interface conversion: interface {} is nil, not models.UserModel github.com/GoAdminGroup/go-admin/modules/logger.Error C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/logger/logger.go:285 github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1.1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:306 runtime.gopanic E:/Golang/src1.14.4/src/runtime/panic.go:969 runtime.panicdottypeE E:/Golang/src1.14.4/src/runtime/iface.go:260 github.com/GoAdminGroup/go-admin/modules/auth.Auth C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/auth/auth.go:23 github.com/GoAdminGroup/go-admin/engine.(Engine).HTMLFile.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:557 github.com/GoAdminGroup/go-admin/context.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:333 github.com/GoAdminGroup/go-admin/context.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 github.com/GoAdminGroup/go-admin/adapter/gin.(Gin).AddHandler.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/adapter/gin/gin.go:91 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.RecoveryWithWriter.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/recovery.go:83 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.LoggerWithConfig.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/logger.go:241 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.(Engine).handleHTTPRequest C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:409 github.com/gin-gonic/gin.(Engine).ServeHTTP C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:367 net/http.serverHandler.ServeHTTP E:/Golang/src1.14.4/src/net/http/server.go:2807 net/http.(conn).serve E:/Golang/src1.14.4/src/net/http/server.go:1895 2020-11-30T18:01:25.717+0800 ?[31mERROR?[0m engine/engine.go:307 goroutine 51 [running]: runtime/debug.Stack(0xc000506300, 0xe06902, 0x0) E:/Golang/src1.14.4/src/runtime/debug/stack.go:24 +0xa4 github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1.1(0x146da00, 0xc0003111e0, 0xc000312340, 0xc000312640) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:307 +0x3cb panic(0xe06960, 0xc00069aa80) E:/Golang/src1.14.4/src/runtime/panic.go:969 +0x174 github.com/GoAdminGroup/go-admin/modules/auth.Auth(...) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/auth/auth.go:23 github.com/GoAdminGroup/go-admin/engine.(Engine).HTMLFile.func1(0xc000312640) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:557 +0xb77 github.com/GoAdminGroup/go-admin/context.(Context).Next(0xc000312640) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 +0x4b github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1(0xc000312640) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:333 +0x7e github.com/GoAdminGroup/go-admin/context.(Context).Next(0xc000312640) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 +0x4b github.com/GoAdminGroup/go-admin/adapter/gin.(Gin).AddHandler.func1(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/adapter/gin/gin.go:91 +0x2aa github.com/gin-gonic/gin.(Context).Next(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 +0x42 github.com/gin-gonic/gin.RecoveryWithWriter.func1(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/recovery.go:83 +0x67 github.com/gin-gonic/gin.(Context).Next(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 +0x42 github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/logger.go:241 +0xe8 github.com/gin-gonic/gin.(Context).Next(0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 +0x42 github.com/gin-gonic/gin.(Engine).handleHTTPRequest(0xc0002f4dc0, 0xc000152e10) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:409 +0x66d github.com/gin-gonic/gin.(Engine).ServeHTTP(0xc0002f4dc0, 0x1456ec0, 0xc00036c2a0, 0xc0003fc000) C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:367 +0x154 net/http.serverHandler.ServeHTTP(0xc00058e000, 0x1456ec0, 0xc00036c2a0, 0xc0003fc000) E:/Golang/src1.14.4/src/net/http/server.go:2807 +0xaa net/http.(conn).serve(0xc000573220, 0x14597c0, 0xc0003124c0) E:/Golang/src1.14.4/src/net/http/server.go:1895 +0x873 created by net/http.(*Server).Serve E:/Golang/src1.14.4/src/net/http/server.go:2933 +0x363

github.com/GoAdminGroup/go-admin/modules/logger.Error C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/logger/logger.go:285 github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1.1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:307 runtime.gopanic E:/Golang/src1.14.4/src/runtime/panic.go:969 runtime.panicdottypeE E:/Golang/src1.14.4/src/runtime/iface.go:260 github.com/GoAdminGroup/go-admin/modules/auth.Auth C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/auth/auth.go:23 github.com/GoAdminGroup/go-admin/engine.(Engine).HTMLFile.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:557 github.com/GoAdminGroup/go-admin/context.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 github.com/GoAdminGroup/go-admin/engine.(Engine).deferHandler.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:333 github.com/GoAdminGroup/go-admin/context.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 github.com/GoAdminGroup/go-admin/adapter/gin.(Gin).AddHandler.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/adapter/gin/gin.go:91 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.RecoveryWithWriter.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/recovery.go:83 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.LoggerWithConfig.func1 C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/logger.go:241 github.com/gin-gonic/gin.(Context).Next C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 github.com/gin-gonic/gin.(Engine).handleHTTPRequest C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:409 github.com/gin-gonic/gin.(Engine).ServeHTTP C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:367 net/http.serverHandler.ServeHTTP E:/Golang/src1.14.4/src/net/http/server.go:2807 net/http.(conn).serve E:/Golang/src1.14.4/src/net/http/server.go:1895

?[31m2020/11/30 18:01:25 [Recovery] 2020/11/30 - 18:01:25 panic recovered: interface conversion: interface {} is nil, not models.UserModel E:/Golang/src1.14.4/src/runtime/iface.go:260 (0x40a1c8) panicdottypeE: panic(&TypeAssertionError{iface, have, want, ""}) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/auth/auth.go:23 (0xc21dcd) Auth: return ctx.User().(models.UserModel) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:645 (0xc21369) (Engine).errorPanelHTML: user := auth.Auth(ctx) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:330 (0xc22d5b) (Engine).deferHandler.func1.1: eng.errorPanelHTML(ctx, new(bytes.Buffer), errors2.New(errMsg)) E:/Golang/src1.14.4/src/runtime/panic.go:969 (0x4361a3) gopanic: done = runOpenDeferFrame(gp, d) E:/Golang/src1.14.4/src/runtime/iface.go:260 (0x40a1c8) panicdottypeE: panic(&TypeAssertionError{iface, have, want, ""}) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/modules/auth/auth.go:23 (0xc246a6) Auth: return ctx.User().(models.UserModel) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:557 (0xc23d5e) (Engine).HTMLFile.func1: user = auth.Auth(ctx) C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 (0x71cc5a) (Context).Next: ctx.handlersctx.index C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/engine/engine.go:333 (0xc22fdd) (Engine).deferHandler.func1: ctx.Next() C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/context/context.go:101 (0x71cc5a) (Context).Next: ctx.handlersctx.index C:/Users/kingway/Desktop/genshin/vendor/github.com/GoAdminGroup/go-admin/adapter/gin/gin.go:91 (0xc26299) (Gin).AddHandler.func1: ctx.SetHandlers(handlers).Next() C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 (0xb6cc41) (Context).Next: c.handlersc.index C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/recovery.go:83 (0xb809d6) RecoveryWithWriter.func1: c.Next() C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 (0xb6cc41) (Context).Next: c.handlersc.index C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/logger.go:241 (0xb7fae7) LoggerWithConfig.func1: c.Next() C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/context.go:161 (0xb6cc41) (Context).Next: c.handlersc.index C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:409 (0xb76edc) (Engine).handleHTTPRequest: c.Next() C:/Users/kingway/Desktop/genshin/vendor/github.com/gin-gonic/gin/gin.go:367 (0xb765d3) (Engine).ServeHTTP: engine.handleHTTPRequest(c) E:/Golang/src1.14.4/src/net/http/server.go:2807 (0x6d40c9) serverHandler.ServeHTTP: handler.ServeHTTP(rw, req) E:/Golang/src1.14.4/src/net/http/server.go:1895 (0x6cf972) (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req) E:/Golang/src1.14.4/src/runtime/asm_amd64.s:1373 (0x464280) goexit: BYTE $0x90 // NOP ?[0m `

dalianzhu commented 3 years ago

最近我也在魔改这个框架。准备把它接入到公司已有的账户体系,发现这是绝对是一个bug。

目前比较简单的绕过方法是,在数据创建一个叫unknow(叫啥都行)的用户,给一个最小权限。在中间件中,自动用这个用户登录。这样不用伤筋动骨。

因为对项目了解不深,还没想好怎么改。

而且,这个项目在维护吗?

原因: 在HTMLFile方法中,会创建一个handler。无论要不要登录,都调用

        var (
            tmpl, tmplName = template.Default().GetTemplate(ctx.IsPjax())
            user = auth.Auth(ctx) // 既然没登录,auth它干嘛呢?
            buf  = new(bytes.Buffer)
        )

modules/auth/auth.go里面,简单粗暴

func Auth(ctx *context.Context) models.UserModel {
    return ctx.User().(models.UserModel)
}

既然没登录,ctx.User()肯定是nil呀。所以挂了。

kingway126 commented 3 years ago

@dalianzhu 多谢提供解决思路

dalianzhu commented 3 years ago

研究了一下他的用户机制。最核心的在于,他把用户的登录session存在表里。每次登录,会在库里创建这个:

select * from goadmin_session;
+-----+--------------------------------------+---------------+---------------------+---------------------+
| id  | sid                                  | values        | created_at          | updated_at          |
+-----+--------------------------------------+---------------+---------------------+---------------------+
| 634 | 222322d5-6724-447c-9718-8fca6194492c | {"user_id":4} | 2020-12-04 07:32:07 | 2020-12-04 07:32:07 |

sid是随机码,values对应里面的一个用户。也就是说,只要伪造一个session,就能绕过登录。下面就是大显身手的时候啦。

因为goadmin没有提供中间件的写法,所以只能依赖框架的中间件,这里我用的是beego。其它的原理类似,在网络框架中写一个全局filter,运行在所有handler之前。 注意,必须先去go admin创建一个账户,账户信息如代码定义,name=unknow, id是4

    app.Handlers.InsertFilter("*", beego.BeforeExec, func(ctx *context.Context) {
        unknownName := "unknown"
        unknownPassword := "unknown"
        unknownID := 4

        sessionCookieName := "go_admin_session"
        sessionStr := ctx.GetCookie(sessionCookieName)
        if sessionStr == "" {
            // 代表没有cookie,使用Unknow登录
            _, ok := auth.Check(unknownName, unknownPassword, conn)
            if !ok {
                ctx.Redirect(302, "/subadmin/login")
            }

            // 直接暴力插入一条session记录
            tSession := db.WithDriver(models.GetConn()).Table("goadmin_session")
            sid := modules.Uuid()
            tSession.Insert(dialect.H{
                "values": fmt.Sprintf(`{"user_id":%v}`, unknownID),
                "sid":    sid,
            })
            // 把cookie设好,跳转,完美
            ctx.SetCookie("go_admin_session", sid, 3600, "/")
            ctx.Redirect(302, "/subadmin/admin")
        }
    })

完美。