authhandler.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package handler
  2. import (
  3. "bufio"
  4. "context"
  5. "errors"
  6. "net"
  7. "net/http"
  8. "net/http/httputil"
  9. "git.i2edu.net/i2/go-zero/core/logx"
  10. "git.i2edu.net/i2/go-zero/rest/token"
  11. "github.com/dgrijalva/jwt-go"
  12. )
  13. const (
  14. jwtAudience = "aud"
  15. jwtExpire = "exp"
  16. jwtId = "jti"
  17. jwtIssueAt = "iat"
  18. jwtIssuer = "iss"
  19. jwtNotBefore = "nbf"
  20. jwtSubject = "sub"
  21. noDetailReason = "no detail reason"
  22. )
  23. var (
  24. errInvalidToken = errors.New("invalid auth token")
  25. errNoClaims = errors.New("no auth params")
  26. )
  27. type (
  28. // A AuthorizeOptions is authorize options.
  29. AuthorizeOptions struct {
  30. PrevSecret string
  31. Callback UnauthorizedCallback
  32. }
  33. // UnauthorizedCallback defines the method of unauthorized callback.
  34. UnauthorizedCallback func(w http.ResponseWriter, r *http.Request, err error)
  35. // AuthorizeOption defines the method to customize an AuthorizeOptions.
  36. AuthorizeOption func(opts *AuthorizeOptions)
  37. )
  38. // Authorize returns an authorize middleware.
  39. func Authorize(secret string, opts ...AuthorizeOption) func(http.Handler) http.Handler {
  40. var authOpts AuthorizeOptions
  41. for _, opt := range opts {
  42. opt(&authOpts)
  43. }
  44. parser := token.NewTokenParser()
  45. return func(next http.Handler) http.Handler {
  46. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  47. tok, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
  48. if err != nil {
  49. unauthorized(w, r, err, authOpts.Callback)
  50. return
  51. }
  52. if !tok.Valid {
  53. unauthorized(w, r, errInvalidToken, authOpts.Callback)
  54. return
  55. }
  56. claims, ok := tok.Claims.(jwt.MapClaims)
  57. if !ok {
  58. unauthorized(w, r, errNoClaims, authOpts.Callback)
  59. return
  60. }
  61. ctx := r.Context()
  62. for k, v := range claims {
  63. switch k {
  64. case jwtAudience, jwtExpire, jwtId, jwtIssueAt, jwtIssuer, jwtNotBefore, jwtSubject:
  65. // ignore the standard claims
  66. default:
  67. ctx = context.WithValue(ctx, k, v)
  68. }
  69. }
  70. next.ServeHTTP(w, r.WithContext(ctx))
  71. })
  72. }
  73. }
  74. // WithPrevSecret returns an AuthorizeOption with setting previous secret.
  75. func WithPrevSecret(secret string) AuthorizeOption {
  76. return func(opts *AuthorizeOptions) {
  77. opts.PrevSecret = secret
  78. }
  79. }
  80. // WithUnauthorizedCallback returns an AuthorizeOption with setting unauthorized callback.
  81. func WithUnauthorizedCallback(callback UnauthorizedCallback) AuthorizeOption {
  82. return func(opts *AuthorizeOptions) {
  83. opts.Callback = callback
  84. }
  85. }
  86. func detailAuthLog(r *http.Request, reason string) {
  87. // discard dump error, only for debug purpose
  88. details, _ := httputil.DumpRequest(r, true)
  89. logx.Errorf("authorize failed: %s\n=> %+v", reason, string(details))
  90. }
  91. func unauthorized(w http.ResponseWriter, r *http.Request, err error, callback UnauthorizedCallback) {
  92. writer := newGuardedResponseWriter(w)
  93. if err != nil {
  94. detailAuthLog(r, err.Error())
  95. } else {
  96. detailAuthLog(r, noDetailReason)
  97. }
  98. if callback != nil {
  99. callback(writer, r, err)
  100. }
  101. writer.WriteHeader(http.StatusUnauthorized)
  102. }
  103. type guardedResponseWriter struct {
  104. writer http.ResponseWriter
  105. wroteHeader bool
  106. }
  107. func newGuardedResponseWriter(w http.ResponseWriter) *guardedResponseWriter {
  108. return &guardedResponseWriter{
  109. writer: w,
  110. }
  111. }
  112. func (grw *guardedResponseWriter) Flush() {
  113. if flusher, ok := grw.writer.(http.Flusher); ok {
  114. flusher.Flush()
  115. }
  116. }
  117. func (grw *guardedResponseWriter) Header() http.Header {
  118. return grw.writer.Header()
  119. }
  120. // Hijack implements the http.Hijacker interface.
  121. // This expands the Response to fulfill http.Hijacker if the underlying http.ResponseWriter supports it.
  122. func (grw *guardedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  123. if hijacked, ok := grw.writer.(http.Hijacker); ok {
  124. return hijacked.Hijack()
  125. }
  126. return nil, nil, errors.New("server doesn't support hijacking")
  127. }
  128. func (grw *guardedResponseWriter) Write(body []byte) (int, error) {
  129. return grw.writer.Write(body)
  130. }
  131. func (grw *guardedResponseWriter) WriteHeader(statusCode int) {
  132. if grw.wroteHeader {
  133. return
  134. }
  135. grw.wroteHeader = true
  136. grw.writer.WriteHeader(statusCode)
  137. }