golang webapp with LDAP

时间:2017-02-08 09:56:51

标签: go active-directory 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
}

1 个答案:

答案 0 :(得分: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) {
    //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"
}

User结构实现了ServeHTTP功能,有效地实现了http.Handler interface,这开启了将其添加到多路复用器的选项,而多路复用器又有助于维护每个请求的用户名和密码。虽然方法接收指针类型,但您可以更改它以更好地满足您的需求。