123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- package main
- import (
- "errors"
- "fmt"
- "log"
- "net/http"
- "strings"
- "time"
- "github.com/go-martini/martini"
- jwt "github.com/dgrijalva/jwt-go"
- )
- const (
- //DefaultContextKey jwt
- DefaultContextKey = "jwt"
- )
- // errorHandler error callback
- type errorHandler func(http.ResponseWriter, string)
- // TokenExtractor TokenExtractor
- type TokenExtractor func(*http.Request) (string, error)
- // Config config
- type Config struct {
- ValidationKeyGetter jwt.Keyfunc
- ContextKey string
- ErrorHandler errorHandler
- CredentialsOptional bool
- Extractor TokenExtractor
- // Default: true
- Debug bool
- // Default: false
- EnableAuthOnOptions bool
- // Default: nil
- SigningMethod jwt.SigningMethod
- // Default: false
- Expiration bool
- }
- // Middleware the middleware for JSON Web tokens authentication method
- type Middleware struct {
- Config Config
- }
- // OnError default error handler
- func OnError(res http.ResponseWriter, err string) {
- http.Error(res, err, http.StatusUnauthorized)
- }
- // New constructs a new Secure instance with supplied options.
- func New(cfg ...Config) *Middleware {
- var c Config
- if len(cfg) == 0 {
- c = Config{}
- } else {
- c = cfg[0]
- }
- if c.ContextKey == "" {
- c.ContextKey = DefaultContextKey
- }
- if c.ErrorHandler == nil {
- c.ErrorHandler = OnError
- }
- if c.Extractor == nil {
- c.Extractor = FromAuthHeader
- }
- return &Middleware{Config: c}
- }
- func (m *Middleware) logf(format string, args ...interface{}) {
- if m.Config.Debug {
- log.Printf(format, args...)
- }
- }
- // Get returns the user (&token) information for this client/request
- // func (m *Middleware) Get(req *http.Request) *jwt.Token {
- // return req.Header.Get(m.Config.ContextKey).(*jwt.Token)
- // }
- // Serve the middleware's action
- func (m *Middleware) Serve(ctx martini.Context, res http.ResponseWriter, req *http.Request) {
- if err := m.CheckJWT(req, res, ctx); err != nil {
- return
- }
- // If everything ok then call next.
- ctx.Next()
- }
- // FromAuthHeader is a "TokenExtractor" that takes a give context and extracts
- // the JWT token from the Authorization header.
- func FromAuthHeader(req *http.Request) (string, error) {
- authHeader := req.Header.Get("Authorization")
- if authHeader == "" {
- return "", nil // No error, just no token
- }
- // TODO: Make this a bit more robust, parsing-wise
- authHeaderParts := strings.Split(authHeader, " ")
- if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
- return "", fmt.Errorf("Authorization header format must be Bearer {token}")
- }
- return authHeaderParts[1], nil
- }
- // CheckJWT the main functionality, checks for token
- func (m *Middleware) CheckJWT(req *http.Request, res http.ResponseWriter, ctx martini.Context) error {
- if !m.Config.EnableAuthOnOptions {
- if req.Method == http.MethodOptions {
- return nil
- }
- }
- // Use the specified token extractor to extract a token from the request
- token, err := m.Config.Extractor(req)
- // If debugging is turned on, log the outcome
- if err != nil {
- m.logf("Error extracting JWT: %v", err)
- } else {
- m.logf("Token extracted: %s", token)
- }
- // If an error occurs, call the error handler and return an error
- if err != nil {
- m.Config.ErrorHandler(res, err.Error())
- return fmt.Errorf("Error extracting token: %v", err)
- }
- // If the token is empty...
- if token == "" {
- // Check if it was required
- if m.Config.CredentialsOptional {
- m.logf(" No credentials found (CredentialsOptional=true)")
- // No error, just no token (and that is ok given that CredentialsOptional is true)
- return nil
- }
- // If we get here, the required token is missing
- errorMsg := "Required authorization token not found"
- m.Config.ErrorHandler(res, errorMsg)
- m.logf(" Error: No credentials found (CredentialsOptional=false)")
- return fmt.Errorf(errorMsg)
- }
- // Now parse the token
- parsedToken, err := jwt.Parse(token, m.Config.ValidationKeyGetter)
- // Check if there was an error in parsing...
- if err != nil {
- m.logf("Error parsing token: %v", err)
- m.Config.ErrorHandler(res, err.Error())
- return fmt.Errorf("Error parsing token: %v", err)
- }
- if m.Config.SigningMethod != nil && m.Config.SigningMethod.Alg() != parsedToken.Header["alg"] {
- message := fmt.Sprintf("Expected %s signing method but token specified %s",
- m.Config.SigningMethod.Alg(),
- parsedToken.Header["alg"])
- m.logf("Error validating token algorithm: %s", message)
- m.Config.ErrorHandler(res, errors.New(message).Error())
- return fmt.Errorf("Error validating token algorithm: %s", message)
- }
- // Check if the parsed token is valid...
- if !parsedToken.Valid {
- m.logf("Token is invalid")
- m.Config.ErrorHandler(res, "The token isn't valid")
- return fmt.Errorf("Token is invalid")
- }
- if m.Config.Expiration {
- if claims, ok := parsedToken.Claims.(jwt.MapClaims); ok {
- if expired := claims.VerifyExpiresAt(time.Now().Unix(), true); !expired {
- return fmt.Errorf("Token is expired")
- }
- }
- }
- m.logf("JWT: %v", parsedToken)
- // If we get here, everything worked and we can set the
- // user property in context.
- ctx.Map(parseToken(parsedToken))
- return nil
- }
- // 把token解析成一个usertoken对象
- func parseToken(token *jwt.Token) *UserToken {
- claims := token.Claims.(jwt.MapClaims)
- ut := &UserToken{
- UserID: uint(claims["UserID"].(float64)),
- UserName: claims["UserName"].(string),
- UserType: claims["UserType"].(string),
- RoleCode: int(claims["RoleCode"].(float64)),
- }
- return ut
- }
|