使用LDAP的Golang Web应用程序

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

golang webapp with LDAP

问题

我尝试使用Active Directory身份验证构建Web应用程序。我还需要获取用户的电子邮件地址。我有一个可以获取电子邮件地址的函数。在mainHandler()函数中,我应该在哪里以及如何使用该函数来获取电子邮件地址?

main.go

func main() {
    http.HandleFunc("/", auth.BasicAuth(mainHandler))
    http.ListenAndServe(":8080", nil)
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("templates/main.html")
    if err == nil {
        tmpl.Execute(w, nil)
    }
}

auth.go

type Handler func(w http.ResponseWriter, r *http.Request)

// BasicAuth - handler wrapper for authentication
func BasicAuth(pass Handler) Handler {

    return func(w http.ResponseWriter, r *http.Request) {
        username, password, ok := r.BasicAuth()
        err := ValidateAD(username, password)
        if err != nil || !ok {
            realm := "Please enter your corporate key and password"
            w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
            // w.WriteHeader(401)
            http.Error(w, "authorization failed", http.StatusUnauthorized)
            return
        }
        pass(w, r)
    }
}

var ErrEmptyUserOrPass = errors.New("Username or password cannot be empty")
var conn *ldap.Conn


// ValidateAD validation based on Active Directory
func ValidateAD(user, passwd string) error {
    if user == "" || passwd == "" {
        return ErrEmptyUserOrPass
    }
    tlsConfig := &tls.Config{InsecureSkipVerify: true}
    var err error
    conn, err = ldap.DialTLS("tcp", "ad.something.com:636", tlsConfig)
    if err != nil {
        return err
    }
    defer conn.Close()
    domainPrefix := "ad\\"
    err = conn.Bind(domainPrefix+user, passwd)
    if err != nil {
        return err
    }
    return nil
}

// GetLDAPEmail returns email address for given username and password
func GetLDAPEmail(user, password string) (string, error) {
    if err := ValidateAD(user, password); err != nil {
        return "", err
    }
    searchBase := "CN=" + user + ",OU=OU1,OU=OU2,OU=OU3,DC=ad,DC=something,DC=com"
    searchFilter := "(&(samAccountName=" + user + "))"
    searchRequest := ldap.NewSearchRequest(
        searchBase, // The base dn to search
        ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
        searchFilter,     // The filter to apply
        []string{"mail"}, // A list attributes to retrieve
        nil,
    )
    sr, err := conn.Search(searchRequest)
    if err != nil {
        return "", err
    }
    email := sr.Entries[0].GetAttributeValue("mail")
    return strings.ToLower(email), nil
}

你可以在mainHandler()函数中调用GetLDAPEmail()函数来获取电子邮件地址。在调用之前,你需要确保已经进行了身份验证。以下是在mainHandler()函数中使用GetLDAPEmail()函数的示例代码:

func mainHandler(w http.ResponseWriter, r *http.Request) {
    // 获取用户名和密码
    username, password, _ := r.BasicAuth()

    // 调用GetLDAPEmail()函数获取电子邮件地址
    email, err := GetLDAPEmail(username, password)
    if err != nil {
        // 处理错误
        // ...
    }

    // 在模板中使用电子邮件地址
    data := struct {
        Email string
    }{
        Email: email,
    }

    tmpl, err := template.ParseFiles("templates/main.html")
    if err == nil {
        tmpl.Execute(w, data)
    }
}

请注意,你需要根据实际情况进行适当的错误处理和模板处理。

英文:

I try to build web application using Active Directory authentication. I also need to get email address of a user. I have a function that can get email address.
Where and how should I use the function to get email in mainHandler()?

main.go

func main() {
http.HandleFunc("/", auth.BasicAuth(mainHandler))
http.ListenAndServe(":8080", nil)
}
func mainHandler(w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("templates/main.html")
if err == nil {
tmpl.Execute(w, nil)
}
}

auth.go

type Handler func(w http.ResponseWriter, r *http.Request)
// BasicAuth - handler wrapper for authentication
func BasicAuth(pass Handler) Handler {
return func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
err := ValidateAD(username, password)
if err != nil || !ok {
realm := "Please enter your corporate key and password"
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
// w.WriteHeader(401)
http.Error(w, "authorization failed", http.StatusUnauthorized)
return
}
pass(w, r)
}
}
var ErrEmptyUserOrPass = errors.New("Username or password cannot be empty")
var conn *ldap.Conn
// ValidateAD validation based on Active Directory
func ValidateAD(user, passwd string) error {
if user == "" || passwd == "" {
return ErrEmptyUserOrPass
}
tlsConfig := &tls.Config{InsecureSkipVerify: true}
var err error
conn, err = ldap.DialTLS("tcp", "ad.something.com:636", tlsConfig)
if err != nil {
return err
}
defer conn.Close()
domainPrefix := "ad\\"
err = conn.Bind(domainPrefix+user, passwd)
if err != nil {
return err
}
return nil
}
// GetLDAPEmail returns email address for given username and password
func GetLDAPEmail(user, password string) (string, error) {
if err := ValidateAD(user, password); err != nil {
return "", err
}
searchBase := "CN=" + user + ",OU=OU1,OU=OU2,OU=OU3,DC=ad,DC=something,DC=com"
searchFilter := "(&(samAccountName=" + user + "))"
searchRequest := ldap.NewSearchRequest(
searchBase, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
searchFilter,     // The filter to apply
[]string{"mail"}, // A list attributes to retrieve
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return "", err
}
email := sr.Entries[0].GetAttributeValue("mail")
return strings.ToLower(email), nil
}

答案1

得分: 1

您的函数和处理程序的连接方式,我没有看到很多“干净”的选项来从BasicAuth()传递状态回到mainHandler()每个请求。

如果您愿意改变设置处理程序的方式,这里是一个可以根据您的需求进行扩展的框架结构:

package main

import (
	"fmt"
	"log"
	"net/http"
)

type User struct {
	Name     string
	Password string
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", &User{})
	s := &http.Server{Addr: "localhost:8080", Handler: mux}
	log.Fatal(s.ListenAndServe())
}

func (u *User) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// 提取用户名和密码
	u.BasicAuth()
	fmt.Println(u.Name)
	fmt.Println(u.Password)
	// 其余的主处理程序
}

func (u *User) BasicAuth() {
	// 这是为了演示传递状态的能力
	// 根据您的需求进行修改
	u.Name = "user"
	u.Password = "pass"
}

User 结构体实现了 ServeHTTP 函数,有效地实现了 http.Handler 接口,这样可以将其添加到多路复用器中,从而在每个请求中维护用户名和密码。虽然这些方法接收指针类型,但您可以根据需要进行更改。

英文:

The way your functions and handlers are wired, I do not see a whole lot of "clean" options to pass state back from BasicAuth() to the mainHandler() per request.

If you are open to the idea of changing the way you setup your handlers, here is a skeleton structure which you can expand to fit your needs:

package main
import (
"fmt"
"log"
"net/http"
)
type User struct {
Name     string
Password string
}
func main() {
mux := http.NewServeMux()
mux.Handle("/", &User{})
s := &http.Server{Addr: "localhost:8080", Handler: mux}
log.Fatal(s.ListenAndServe())
}
func (u *User) ServeHTTP(w http.ResponseWriter, r *http.Request) {
//Pull out the username and password
u.BasicAuth()
fmt.Println(u.Name)
fmt.Println(u.Password)
//The rest of your main handler
}
func (u *User) BasicAuth() {
//This is to demonstrate the ability to pass state
//Edit this to fit your needs
u.Name = "user"
u.Password = "pass"
}

The User struct implements the ServeHTTP function, effectively implementing the http.Handler interface which opens up the option of adding it to a multiplexer which in turn helps maintain the username and password per request. Although the methods receive a pointer type you could change it to better suit your needs.

huangapple
  • 本文由 发表于 2017年2月8日 17:56:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/42109853.html
匿名

发表评论

匿名网友

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

确定