Web应用程序中的Go身份验证逻辑模式

huangapple go评论90阅读模式
英文:

Go- authentication logic pattern in web app

问题

我想确定一个在使用golang编写的Web应用程序中用于用户身份验证的简单而有用的模式。

我想到了两种模式。第一种模式是使程序员能够将自己的函数与身份验证逻辑分开,并且在main()中具有更清晰的HandleFunc部分,只需查看main()即可了解哪些部分受身份验证控制。

第二种模式是使程序员在处理需要身份验证的URL的每个函数中包含一个决策。一个if语句通过在其他地方定义的authp()函数进行检查。

哪种模式对于这种需求来说更好?

有哪些更好的模式适用于这个任务?

如果不能将具有与func urlFunc (ResponseWriter, *Request)不同签名的函数传递给http.HandleFunc,例如func urlFunc (successFunc, failFunc)func urlFunc (ResponseWriter, *Request, successFunc, failFunc),是否有适当的解决方法?

//第一种方式
package main

func authGateKeeper(successFunc, failFunc) {
    if (authp()) {
        successFunc
    } else {
        failFunc
    }
}

func authp() boolean {
    //身份验证逻辑,数据库查询或会话检查等
}

//在main中的使用
http.HandleFunc("/", authGateKeeper)



//第二种方式;其他替代方案,在每个函数中进行检查
func f(w, r) {
    if (authp()) {
    //函数的处理过程
    } else {
    //失败情况的函数或处理过程
    }
}

func authp() boolean {
    //身份验证逻辑,数据库查询或会话检查等
}

//在main中的使用
http.HandleFunc("/", f)
英文:

I want to determine a simple and useful pattern for user authentication in a web app being written in golang.

I have come up with two patterns. First one is enabling the programmer to have his functions separate form the authentication logic, and has cleaner HandleFunc parts in main() that one can see only by loking main() to see what parts are under authentication control.

Second one is making programmer include a decision in every function deal with authentication required urls. An if statement checks by a authp() function defined else where.

Which one is better pattern for such necessity?

What are the better patterns for this job?

Is it even possible to pass a function to http.HandleFunc that has signature other than func urlFunc (ResponseWriter, *Request) bu like func urlFunc (successFunc, failFunc) or func urlFunc (ResponseWriter, *Request, successFunc, failFunc) as in authenticationGateKeeper function of First Way below, if not a suitable workaround for that?

//First Way
package main

func authGateKeeper(successFunc, failFunc) {
	if (authp()) {
		successFunc
	} else {
		failFunc
	}
}

func authp() boolean {
	//authentication logic, db query, or session check etc.
}

//usage in main
http.HandleFunc("/", authGateKeeper)



//Second Way; other alternative, in each function check pattern
func f(w, r) {
	if (authp()) {
	//function's processes
	} else {
	//the fail case function or processes
	}
}

func authp() boolean {
	//authentication logic, db query, or session check etc.
}

//usage in main
http.HandleFunc("/", f)

答案1

得分: 4

有很多方法可以解决这个问题,是否有一种方法明显“更好”是有争议的。我强烈建议编写一些中间件来包装你的路由,并强制进行检查,只有在成功时才调用包装的处理程序。

请注意,由于你没有告诉我们你是如何管理会话(使用cookie?服务器端?)以及你可能需要的授权类型,我将做出一些假设。

// 中间件 - 一个位于请求处理“中间”的函数。
func RequireAuth(h http.Handler) http.Handler) {
    fn := func(w http.ResponseWriter, r *http.Request) {
        // 假设使用gorilla/sessions
        session, err := store.Get("name", r)
        if err != nil {
            // 抛出HTTP 500错误
            return
        }
        
        // 假设你在其他地方登录时将userID存储在cookie|服务器会话中。
        id := session.Values["userID"]
        // 可能返回一个*yourapp.User
        user, err := db.GetUser(id)
        if err != nil {
            // 抛出HTTP 500错误
            return
        }

        if user == nil {
            http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
            // 不要忘记这些“裸”的返回语句 - 如果你漏掉一个,你的处理程序将继续处理错误之外的内容,导致意外的副作用
            return
        }
        
        // 进行进一步的检查 - 比如检查user.Active == true等等。
        
        // userID与数据库中的一个匹配,所以让我们继续
        h.ServeHTTP(w, r)
    }
    
    return http.HandlerFunc(fn)
}

// 在你的路由器中使用 - 假设只使用原始的net/http
http.Handle("/", RequireAuth(yourHandlerFunc))
http.Handle("/", RequireAuth(someOtherHandler))
// 请注意,使用gorilla/mux或goji可以帮助你获得“子路由器”,这样你就不必为每个路由都包装中间件(混乱,容易出错)

我还建议你阅读一些关于Go中间件1和组合2的文章,这将有助于你将来的开发。

如果你想调用自定义的错误页面,只需编写一个处理程序 - 例如UnauthorizedHandler,它满足http.Handler接口,然后在途中调用UnauthorizedHandler.ServeHTTP(w, r),而不是使用http.Error

英文:

There are many ways to spin this, and it's arguable whether one is outright "better". I'd strongly suggest writing some middleware that wraps your routes and enforces the check, calling the wrapped handler only on success.

Note that I'm going to make a few assumptions here as you haven't told us how you're managing sessions (cookies? server-side?) and/or what kind of authorization you might need on top of authentication.

// Middleware - a function that sits in the 'middle' of your request processing.
func RequireAuth(h http.Handler) http.Handler) {
    fn := func(w http.ResponseWriter, r *http.Request) {
        // Assuming gorilla/sessions
        session, err := store.Get("name", r)
        if err != nil {
            // Raise HTTP 500
            return
        }
        
        // We'll assume you're storing the userID in the cookie|server session
        // upon login elsewhere.
        id := session.Values["userID"]
        // Probably returns a *yourapp.User
        user, err := db.GetUser(id)
        if err != nil {
            // Raise HTTP 500
            return
        }

        if user == nil {
            http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
            // Don't forget these 'naked' returns - if you miss one, your 
            // handler will keep processing beyond the error and result in
            // unintended side effects
            return
        }
        
        // Further checks here - i.e. checking user.Active == true, etc.
        
        // The userID matches one in the DB, so let's proceed
        h.ServeHTTP(w, r)
    }
    
    return http.HandlerFunc(fn)
}

// And in your router - assuming just vanilla net/http
http.Handle("/", RequireAuth(yourHandlerFunc))
http.Handle("/", RequireAuth(someOtherHandler))
// Note that using gorilla/mux or goji can help give you "subrouters" so you
// don't have to wrap every single route with your middleware (messy, error prone)

I'd also suggest some reading on Go middleware1 composition2 which will help you in the future.

If you want to call a custom error page, just write a handler - e.g. UnauthorizedHandler that satisfies http.Handler and just call UnauthorizedHandler.ServeHTTP(w, r) instead of http.Error along the way.

huangapple
  • 本文由 发表于 2015年6月7日 02:53:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/30686458.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定