douxiaomang5640 2017-02-08 09:56
浏览 90
已采纳

带有LDAP的golang webapp

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条回答 默认 最新

  • duanqu9279 2017-02-08 15:13
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。