使用LDAP的Golang Web应用程序

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

golang webapp with LDAP

问题

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

main.go

  1. func main() {
  2. http.HandleFunc("/", auth.BasicAuth(mainHandler))
  3. http.ListenAndServe(":8080", nil)
  4. }
  5. func mainHandler(w http.ResponseWriter, r *http.Request) {
  6. tmpl, err := template.ParseFiles("templates/main.html")
  7. if err == nil {
  8. tmpl.Execute(w, nil)
  9. }
  10. }

auth.go

  1. type Handler func(w http.ResponseWriter, r *http.Request)
  2. // BasicAuth - handler wrapper for authentication
  3. func BasicAuth(pass Handler) Handler {
  4. return func(w http.ResponseWriter, r *http.Request) {
  5. username, password, ok := r.BasicAuth()
  6. err := ValidateAD(username, password)
  7. if err != nil || !ok {
  8. realm := "Please enter your corporate key and password"
  9. w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
  10. // w.WriteHeader(401)
  11. http.Error(w, "authorization failed", http.StatusUnauthorized)
  12. return
  13. }
  14. pass(w, r)
  15. }
  16. }
  17. var ErrEmptyUserOrPass = errors.New("Username or password cannot be empty")
  18. var conn *ldap.Conn
  19. // ValidateAD validation based on Active Directory
  20. func ValidateAD(user, passwd string) error {
  21. if user == "" || passwd == "" {
  22. return ErrEmptyUserOrPass
  23. }
  24. tlsConfig := &tls.Config{InsecureSkipVerify: true}
  25. var err error
  26. conn, err = ldap.DialTLS("tcp", "ad.something.com:636", tlsConfig)
  27. if err != nil {
  28. return err
  29. }
  30. defer conn.Close()
  31. domainPrefix := "ad\\"
  32. err = conn.Bind(domainPrefix+user, passwd)
  33. if err != nil {
  34. return err
  35. }
  36. return nil
  37. }
  38. // GetLDAPEmail returns email address for given username and password
  39. func GetLDAPEmail(user, password string) (string, error) {
  40. if err := ValidateAD(user, password); err != nil {
  41. return "", err
  42. }
  43. searchBase := "CN=" + user + ",OU=OU1,OU=OU2,OU=OU3,DC=ad,DC=something,DC=com"
  44. searchFilter := "(&(samAccountName=" + user + "))"
  45. searchRequest := ldap.NewSearchRequest(
  46. searchBase, // The base dn to search
  47. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  48. searchFilter, // The filter to apply
  49. []string{"mail"}, // A list attributes to retrieve
  50. nil,
  51. )
  52. sr, err := conn.Search(searchRequest)
  53. if err != nil {
  54. return "", err
  55. }
  56. email := sr.Entries[0].GetAttributeValue("mail")
  57. return strings.ToLower(email), nil
  58. }

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

  1. func mainHandler(w http.ResponseWriter, r *http.Request) {
  2. // 获取用户名和密码
  3. username, password, _ := r.BasicAuth()
  4. // 调用GetLDAPEmail()函数获取电子邮件地址
  5. email, err := GetLDAPEmail(username, password)
  6. if err != nil {
  7. // 处理错误
  8. // ...
  9. }
  10. // 在模板中使用电子邮件地址
  11. data := struct {
  12. Email string
  13. }{
  14. Email: email,
  15. }
  16. tmpl, err := template.ParseFiles("templates/main.html")
  17. if err == nil {
  18. tmpl.Execute(w, data)
  19. }
  20. }

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

英文:

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

  1. func main() {
  2. http.HandleFunc("/", auth.BasicAuth(mainHandler))
  3. http.ListenAndServe(":8080", nil)
  4. }
  5. func mainHandler(w http.ResponseWriter, r *http.Request) {
  6. tmpl, err := template.ParseFiles("templates/main.html")
  7. if err == nil {
  8. tmpl.Execute(w, nil)
  9. }
  10. }

auth.go

  1. type Handler func(w http.ResponseWriter, r *http.Request)
  2. // BasicAuth - handler wrapper for authentication
  3. func BasicAuth(pass Handler) Handler {
  4. return func(w http.ResponseWriter, r *http.Request) {
  5. username, password, ok := r.BasicAuth()
  6. err := ValidateAD(username, password)
  7. if err != nil || !ok {
  8. realm := "Please enter your corporate key and password"
  9. w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
  10. // w.WriteHeader(401)
  11. http.Error(w, "authorization failed", http.StatusUnauthorized)
  12. return
  13. }
  14. pass(w, r)
  15. }
  16. }
  17. var ErrEmptyUserOrPass = errors.New("Username or password cannot be empty")
  18. var conn *ldap.Conn
  19. // ValidateAD validation based on Active Directory
  20. func ValidateAD(user, passwd string) error {
  21. if user == "" || passwd == "" {
  22. return ErrEmptyUserOrPass
  23. }
  24. tlsConfig := &tls.Config{InsecureSkipVerify: true}
  25. var err error
  26. conn, err = ldap.DialTLS("tcp", "ad.something.com:636", tlsConfig)
  27. if err != nil {
  28. return err
  29. }
  30. defer conn.Close()
  31. domainPrefix := "ad\\"
  32. err = conn.Bind(domainPrefix+user, passwd)
  33. if err != nil {
  34. return err
  35. }
  36. return nil
  37. }
  38. // GetLDAPEmail returns email address for given username and password
  39. func GetLDAPEmail(user, password string) (string, error) {
  40. if err := ValidateAD(user, password); err != nil {
  41. return "", err
  42. }
  43. searchBase := "CN=" + user + ",OU=OU1,OU=OU2,OU=OU3,DC=ad,DC=something,DC=com"
  44. searchFilter := "(&(samAccountName=" + user + "))"
  45. searchRequest := ldap.NewSearchRequest(
  46. searchBase, // The base dn to search
  47. ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
  48. searchFilter, // The filter to apply
  49. []string{"mail"}, // A list attributes to retrieve
  50. nil,
  51. )
  52. sr, err := conn.Search(searchRequest)
  53. if err != nil {
  54. return "", err
  55. }
  56. email := sr.Entries[0].GetAttributeValue("mail")
  57. return strings.ToLower(email), nil
  58. }

答案1

得分: 1

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

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

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. type User struct {
  8. Name string
  9. Password string
  10. }
  11. func main() {
  12. mux := http.NewServeMux()
  13. mux.Handle("/", &User{})
  14. s := &http.Server{Addr: "localhost:8080", Handler: mux}
  15. log.Fatal(s.ListenAndServe())
  16. }
  17. func (u *User) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  18. // 提取用户名和密码
  19. u.BasicAuth()
  20. fmt.Println(u.Name)
  21. fmt.Println(u.Password)
  22. // 其余的主处理程序
  23. }
  24. func (u *User) BasicAuth() {
  25. // 这是为了演示传递状态的能力
  26. // 根据您的需求进行修改
  27. u.Name = "user"
  28. u.Password = "pass"
  29. }

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:

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. type User struct {
  8. Name string
  9. Password string
  10. }
  11. func main() {
  12. mux := http.NewServeMux()
  13. mux.Handle("/", &User{})
  14. s := &http.Server{Addr: "localhost:8080", Handler: mux}
  15. log.Fatal(s.ListenAndServe())
  16. }
  17. func (u *User) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  18. //Pull out the username and password
  19. u.BasicAuth()
  20. fmt.Println(u.Name)
  21. fmt.Println(u.Password)
  22. //The rest of your main handler
  23. }
  24. func (u *User) BasicAuth() {
  25. //This is to demonstrate the ability to pass state
  26. //Edit this to fit your needs
  27. u.Name = "user"
  28. u.Password = "pass"
  29. }

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:

确定