场景
我们应用开发经常会遇到认证和授权问题,比如:ERP系统、OA系统、CRM系统等等,这些系统都需要用户登录后才能访问,如何实现用户登录和权限认证呢?
我们来看下对应的解决方案:
Python的装饰器模式
熟悉Python的大小朋友可能都知道Flask-JWT这个flask的extension,如何使用的?
@jwt_required()
@app.route('/user/list')
def user_list():return users
COPY
Java中认证授权
Java的认证和授权主要有以下3中方式:
- 放到应用Gateway去做
- 自定义注解,类似于Python的Decorator
- 自定义拦截器
.NET的方式
[Authorize(Roles = "Admin")]
public class UserController : Controller
{[Route("api/users")]public IActionResult Get(){return Ok(new { message = "Hello World" });}
}
COPY
问题
- 可能会忘记使用注解或者把他们放错位置
- 阅读业务代码需要理解注解的意思
- 找不到注解的定义
- 控制流程被隐藏
解决方案
原生Go Http Server
Go的解决方案也比较简单,
首先定义一个认证包装器
func Authentication(h http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 校验if !IsAuthenticated(r) {http.Error(w, "Not authenticated", http.StatusForbidden)return}h.ServeHTTP(w, r)}
}
COPY
然后使用它:
Authentication(http.HandlerFunc(w http.ResponseWriter,r *http.Response){// 处理请求
})
COPY
最后注册路由:
serve := http.NewServeMux()// 需要验证的路由
serve.Handle("/",Authentication(http.HandlerFunc(w http.ResponseWriter,r *http.Response){
}))// 不需要验证的路由
serve.Handle("/login",http.HandlerFunc(w http.ResponseWriter,r *http.Response){
})
COPY
基于Gin的认证
也是基于中间件的认证,和上面一样,只是使用gin框架。
也是首先定义一个Authentication中间件,然后注册路由的时候使用AuthMiddleware中间件。
func AuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {tokenStr := c.GetHeader("Authorization")if tokenStr == "" {c.JSON(http.StatusUnauthorized, common.CommonResp{Message: "Unauthorized",})c.Abort()return}token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])}return []byte("secret"), nil})if err != nil {c.JSON(http.StatusUnauthorized, common.CommonResp{Message: "Unauthorized",})c.Abort()}if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {// 添加claims到上下文c.Set("claims", claims)c.Next()} else {c.JSON(http.StatusUnauthorized, common.CommonResp{Message: "Unauthorized",})c.Abort()}}
}
COPY
然后定义一个Public和protect group来分别处理公共接口和需要登录的接口。
v1 := r.Group("/api/v1")publicGroup := v1.Group("/")protectGroup := v1.Group("/")
protectGroup.Use(user.AuthMiddleware())user.InitRouter(publicGroup, protectGroup)
COPY
总结
处理认证和授权是业务系统场景的问题,不论你是在云上还是哪儿,同事各种编程语言和框架都会提供相应的解决方案,如Python的Flask-JWT, Java的自定义注解或拦截器,.NET的Attribute等。
无论使用哪种方法,都需要对相关的技术有深入的理解,并且在编程时要时刻注意,防止忘记使用注解,或者把注解放错位置。
阅读包含大量注解的代码也会变得困难,找不到注解的定义,或者感觉控制流程被隐藏起来了。
Go语言提供了更简洁清晰的解决方案。Go允许我们直接定义一个认证处理器并将其附加到注册路由上。我们可以自定义这个认证处理器,使其在进入handler之前进行预处理,校验请求是否已经过认证,以此实现权限控制。这样的设计使得控制流程变得清晰,且可以根据需要自由组合handler,具有很高的扩展性。
基于Gin的认证是另一种Go语言下的解决方案,借助于Gin框架,我们可以通过定义中间件的方式来处理认证问题。
参考
Go 认证与授权(Authentication) – 小厂程序员