在前后端分离架构中使用 CAS+JWT 认证
原因
- CAS ticket 是一次性的,验证后就失效
- 前后端分离架构中,后续的 API 请求需要一个持续有效的身份凭证
- JWT token 可以包含用户信息,减少数据库查询
- JWT 支持客户端存储,适合前后端分离架构
时序图
简而言之,用户前端跳转到CAS服务器验证登录,登录URL为 http://localhost:8081/cas/login?service=http%3A%2F%2Flocalhost%3A8080%2Fauth%3Fredirect_url%3Dhttps%253A%252F%252Flocalhost%253A5173%252Fmember
登录完成后CAS携带ST令牌回调到service页面,URL为 http://localhost:8080/auth?redirect_url=https%3A%2F%2Flocalhost%3A5173%2Fmember&ticket=ST-1-Ns-YnfPvYpd5jvEkJah7p-hXU7E-aeaa5f03d463
后端service收到请求后向CAS服务器验证ST令牌,令牌授权后准备下发jwt token作为持久性鉴权。一种实现方法是:302跳转至redirect_url,同时跟上路由参数token;前端处理token写入浏览器存储
代码实现
CAS验证处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func AuthHandler(c *gin.Context) { casURL := c.MustGet("casURL").(string) jwtSecret := c.MustGet("jwt_secret").(string) db := c.MustGet("db").(*gorm.DB) redirectURL := c.Query("redirect_url") u, _ := url.Parse(casURL) client := cas.NewClient(&cas.Options{URL: u})
h := client.HandleFunc(func(w http.ResponseWriter, r *http.Request) { if !cas.IsAuthenticated(r) { client.RedirectToLogin(w, r) } else { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{}) tokenString, err := token.SignedString([]byte(jwtSecret)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } else { c.Redirect(http.StatusFound, redirectURL+"?token="+tokenString) } } }) h.ServeHTTP(c.Writer, c.Request) }
|
设置路由
1 2 3
| func SetupAuthRouters(router *gin.Engine) { router.GET("/auth", handlers.AuthHandler) }
|
JWT中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| func JWTMiddleware() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("Authorization") jwt_secret := c.MustGet("jwt_secret").(string) if token != "" && len(token) > 7 { token = token[7:] parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) { return []byte(jwt_secret), nil }) if err != nil { c.AbortWithError(401, err) return } if !parsedToken.Valid { c.AbortWithStatus(401) return } } else { c.AbortWithStatus(401) return } c.Next() } }
|