前言
在现代 Web 开发中,无论是传统后端渲染的应用,还是新型的单页面应用(SPA),都需要用到身份验证和授权。JSON Web Token(JWT)是一种用于描述安全令牌的 JSON 格式规范,广泛应用于 RESTful API 和 SPA 架构的身份验证中。AngularJS 是一个优秀的 SPA 框架,它提供了许多方便的工具和设施来集成 JWT,使开发人员能够更轻松地进行身份验证和授权。
本文将介绍如何在 AngularJS 单页应用中集成 JWT,包括身份验证、授权、刷新令牌等方面,以及一些最佳实践和值得关注的注意事项。代码示例将基于 AngularJS 1.7.9 版本和 AngularJS 的官方 JWT 模块。
身份验证和授权
身份验证是指确认用户身份的过程,通常依靠用户名和密码进行。在 SPA 应用中,一般采用表单方式输入用户名和密码,通过 AJAX 请求服务器端 API 进行认证。如果认证成功,服务器会返回 JWT 令牌,包含了用户信息和过期时间等等。之后,客户端会保存这个令牌,以备将来使用。
授权是指对已经登录的用户进行访问权限的控制,例如允许用户编辑自己的数据,但是不允许用户查看其他用户数据。在 SPA 应用中,通常采用 JWT 来实现授权,服务端返回一个有限的包含用户角色与权限的 JWT 令牌,客户端进行访问时需要将这个令牌带到请求头中,服务端会根据令牌中的内容来处理请求。
AngularJS 的 JWT 模块
AngularJS 官方提供了一个非常方便的 jwt 模块,用于集成 JWT 到 AngularJS 程序中。我们可以在 index.html 中加载这个模块:
-- -------------------- ---- ------- --------- ----- ----- --------------- ------ ----- ---------------- ---------------- --- ------------ ------- -------------------------------------------------------------------------------- ------- -------------------------------------------------------------------------------------- ------- ---------------------- ------- ----- ------------------------- -- -------- ---------------------------- -- -------- ------------------------------ ---- ----------------------- ------ -- ------------- --- ------ ------- -------
这段代码中,我们加载了 AngularJS 核心模块和 AngularJS 官方的 jwt 模块,并在 app.js 中定义了控制器 AuthCtrl。这个控制器管理用户登录状态和用户信息,并暴露了 login(),logout() 和 isLoggedIn() 方法。
-- -------------------- ---- ------- ----------------------- ---------------- ----------------------- -------- -------- ------ -------- ---------- - ----------- - --- ---------- - --- ------------ - -------- -- - ----------------------- ------------ -------------- ---------- - ---------- - -------------------- ----------------------------------- ------------ --- -- ------------- - -------- -- - --------------------------------------- ---------- - --- -- ----------------- - -------- -- - --- ----- - ------------------------------------ ------ ----- -- --------------------------------- -- ---
这个控制器中,我们通过 $http 服务进行与服务器交互,将用户的登录数据 $scope.user 发送到服务器端的 /api/auth 地址,服务器会返回一个 JWT 令牌作为响应。客户端使用 $window.localStorage 将这个令牌保存起来,使用 jwtHelper 的 isTokenExpired() 方法来检测令牌是否已过期,从而判断用户是否已经登录。
JWT 的保护和刷新
JWT 令牌保存在客户端,可能会面临盗用和窃听的风险。为了减少风险,我们需要在客户端保护令牌,使用 https 协议来保护通信安全,设置短期过期时间和定期更新令牌。
我们可以使用 $http 的拦截器来访问令牌,并在请求头中添加 Authorization 字段,将令牌发送给服务器。
-- -------------------- ---- ------- ----------------------- --------------------------- -------- ------------ --- -------- - ------ - -------- -------- -------- - -------------- - -------------- -- --- -- ------------------------------------- - ---------------------------- - ------- - - ------------------------------------ - ------ ------- -- -------------- -------- ---------- - -- ---------------- --- ---- - -------------------------------------- - ------ -------------------- - -- -- ---------------- --------------- - --------------------------------------------------- ---
这里我们定义了一个名为 authInterceptor 的 factory,它将从本地存储中获取 JWT 令牌,并将其加入到请求头 Authorization 字段中。
同样,我们应该定期更新令牌以提高安全性。如果我们设定 JWT 令牌的过期时间为 30 分钟,那么我们可以在变成后 25 分钟左右向服务器发送请求,让服务器为我们生成一个新的、更新过的 JWT 令牌。
-- -------------------- ---- ------- ----------------------- ---------------------- -------- ------- ---------- -------- ---------- - -------- -------------- - --- ----- - ------------------------------------ -- ------ -- --------------------------------- - ------------------------------- - ------ ----- -- -------------- ---------- - ----------------------------------- --------------------- --- - - ----------------------- -- - -- - ------ ---
这里我们定义了 jwtRefresh,它会在每个周期(25 分钟)内请求服务器来更新令牌,并且将更新后的令牌保存到本地存储当中。
最佳实践和注意事项
在集成 JWT 时,我们需要注意以下几个最佳实践和注意事项:
- 下载 AngularJS 官方提供的 jwt 模块,避免重复造轮子。
- JWT 令牌保存在本地存储中,尽量减少数据量和敏感信息。
- 使用拦截器来保护 JWT 令牌,避免被攻击者盗用。
- 设定较短的 JWT 过期时间,可以增加安全性。
- 尽量使用 https 协议,保护 JWT 的传输过程。
- 根据需要定期更新 JWT 令牌,增强安全性。
- 在服务端对 JWT 进行签名,避免伪造和篡改。
- 不要在 JWT 中包含敏感信息,例如密码和秘钥。
结论
在 AngularJS SPA 应用中集成 JWT 可以帮助我们轻松地进行身份验证和授权,并提高应用的安全性。通过模块化编程、定义拦截器和定时更新令牌等最佳实践,我们可以最大化发挥 JWT 的优势,保护我们的应用不受攻击者的伤害。该实践内容详细,并包含示例代码,希望可以帮助到前端开发人员进行学习并指导实践。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6731d10e0bc820c5823a9392