gin-jwt
gin-jwt copied to clipboard
different Authorizator depending on Router Groups
Hi,
(thx for having merged so quickly my PR on Context injection ^^)
I'm facing an issue, and I think it may be a functional / logical problem in Authorization implementation and / or limitations cause by lack of informations about groups in gin.Context.
For example, here are my groups definitions for admin/users :
admin := r.Group("/admin")
admin.Use(authMiddleware.MiddlewareFunc())
{
users := admin.Group("/users")
users.GET("/:username", user.ByUsernameHandler)
users.POST("", registration.RegisterHandler)
users.GET("", user.AllUsersHandler)
}
By using your Authorizator callback, I have to be aware of two informations :
- roles of user (stored in my DB)
- which route matches to the request and in which group(s) it belongs
Each leads me to an issue :
- User roles can be accessed only by querying for the second time (it has already been queried in Authenticator Handler). I've been trying to set user in context with c.Set("User", user) in Authenticator and access it in Authorizator, but unsuccessfully, c.Get return false for "exists" return param…
- gin.Context does not contain Group… Just handler name…
Should I implement my own solution like writing a specific Middleware, with an string argument telling me what group is called ? Or is there any more elegant way to fix this ?
maybe you can use belown method
func xxAuthorizator func(userID string, c *gin.Context) bool {
claims := jwt.ExtractClaims(c)
//.... get token and do your things
}
Hi @philippecarle, or anyone else, did you find an elegant solution to this?
I also would like to pass a different Authorizator
function for different groups, ideally without creating multiple middlewares.
Thanks!
@arrwhidev you can create a single middleware and switch between its parameter:
func GroupAuthorizator(group string) gin.HandlerFunc {
return func(c *gin.Context) {
switch group {
case "product":
usr, _ := c.Get("id")
fmt.Println(usr.(*models.User).UserName)
c.Next()
return
default:
jwt.Unauthorized(c, http.StatusForbidden, jwt.HTTPStatusMessageFunc(jwt.ErrForbidden, c))
}
}
}
And then, create your gin group router like this:
t := &ProductRouter{}
tR := r.Group("/product")
tR.Use(jwt.MiddlewareFunc(), GroupAuthorizator("product"))
tR.GET("/", t.GetProduct)
@alcmoraes, could you be more detailed ? how to distinguish normal user, admin user, guest user?
usr, _ := c.Get("id")
fmt.Println(usr.(*models.User).UserName)
c.Next()
return
Could you explain these lines ?
Currently I hacked in the gin-jwt source code, like this, to by pass the authenticator function for normal users, so I achieved 3 roles, normal user, admin, and guest without login
// NormalMiddlewareFunc makes GinJWTMiddleware implement the Middleware interface by pass the admin identity check
func (mw *GinJWTMiddleware) NormalMiddlewareFunc() gin.HandlerFunc {
return func(c *gin.Context) {
mw.normalMiddlewareImpl(c)
}
}
func (mw *GinJWTMiddleware) normalMiddlewareImpl(c *gin.Context) {
claims, err := mw.GetClaimsFromJWT(c)
if err != nil {
mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
return
}
if claims["exp"] == nil {
mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrMissingExpField, c))
return
}
if _, ok := claims["exp"].(float64); !ok {
mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrWrongFormatOfExp, c))
return
}
if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() {
mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrExpiredToken, c))
return
}
c.Set("JWT_PAYLOAD", claims)
identity := mw.IdentityHandler(c)
if identity != nil {
c.Set(mw.IdentityKey, identity)
}
c.Next()
}
Then I used such middleware function like,
knowledgeRoutes := router.Group("/data")
{
knowledgeRoutes.Use(jwtMiddleware.NormalMiddlewareFunc())
{
//these fetch functions record statistics
knowledgeRoutes.GET("/getknowledgelist", getKnowledgeListHandler)
knowledgeRoutes.GET("/getallknowledge", getAllKnowledgeHandler)
knowledgeRoutes.GET("/getknowledge/:name", getKnowledgeByNameHandler)
knowledgeRoutes.GET("/getknowledgelogo/:name", getKnowledgeLogoHandler)
knowledgeRoutes.POST("/searchknowledge/:keyword", searchKnowledgeKeywordHandler)
}
}