另客网go项目公用的代码库

auth.go 2.9KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package gin
  5. import (
  6. "crypto/subtle"
  7. "encoding/base64"
  8. "net/http"
  9. "strconv"
  10. )
  11. // AuthUserKey is the cookie name for user credential in basic auth.
  12. const AuthUserKey = "user"
  13. // Accounts defines a key/value for user/pass list of authorized logins.
  14. type Accounts map[string]string
  15. type authPair struct {
  16. value string
  17. user string
  18. }
  19. type authPairs []authPair
  20. func (a authPairs) searchCredential(authValue string) (string, bool) {
  21. if authValue == "" {
  22. return "", false
  23. }
  24. for _, pair := range a {
  25. if pair.value == authValue {
  26. return pair.user, true
  27. }
  28. }
  29. return "", false
  30. }
  31. // BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
  32. // the key is the user name and the value is the password, as well as the name of the Realm.
  33. // If the realm is empty, "Authorization Required" will be used by default.
  34. // (see http://tools.ietf.org/html/rfc2617#section-1.2)
  35. func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
  36. if realm == "" {
  37. realm = "Authorization Required"
  38. }
  39. realm = "Basic realm=" + strconv.Quote(realm)
  40. pairs := processAccounts(accounts)
  41. return func(c *Context) {
  42. // Search user in the slice of allowed credentials
  43. user, found := pairs.searchCredential(c.requestHeader("Authorization"))
  44. if !found {
  45. // Credentials doesn't match, we return 401 and abort handlers chain.
  46. c.Header("WWW-Authenticate", realm)
  47. c.AbortWithStatus(http.StatusUnauthorized)
  48. return
  49. }
  50. // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
  51. // c.MustGet(gin.AuthUserKey).
  52. c.Set(AuthUserKey, user)
  53. }
  54. }
  55. // BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
  56. // the key is the user name and the value is the password.
  57. func BasicAuth(accounts Accounts) HandlerFunc {
  58. return BasicAuthForRealm(accounts, "")
  59. }
  60. func processAccounts(accounts Accounts) authPairs {
  61. assert1(len(accounts) > 0, "Empty list of authorized credentials")
  62. pairs := make(authPairs, 0, len(accounts))
  63. for user, password := range accounts {
  64. assert1(user != "", "User can not be empty")
  65. value := authorizationHeader(user, password)
  66. pairs = append(pairs, authPair{
  67. value: value,
  68. user: user,
  69. })
  70. }
  71. return pairs
  72. }
  73. func authorizationHeader(user, password string) string {
  74. base := user + ":" + password
  75. return "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
  76. }
  77. func secureCompare(given, actual string) bool {
  78. if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
  79. return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
  80. }
  81. // Securely compare actual to itself to keep constant time, but always return false.
  82. return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
  83. }