如何在ember-simple-auth的beforeModel钩子实现差异化认证?
实现差异化路由认证逻辑
要在Ember应用中实现你描述的差异化认证需求,核心思路是利用路由的beforeModel钩子判断当前目标路由,然后触发对应的认证流程。结合你已经自定义的JWT认证器,我们可以这样一步步实现:
1. 在父路由(Application Route)中添加路由判断逻辑
因为所有路由都会继承application路由的钩子,我们在这里统一处理认证逻辑,避免重复代码:
// app/routes/application.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class ApplicationRoute extends Route { @service session; beforeModel(transition) { const targetRoute = transition.targetName; const isAuthenticated = this.session.isAuthenticated; // 已认证状态直接放行 if (isAuthenticated) return; // 区分路由处理不同认证方式 if (targetRoute === 'mobile_messages') { // 处理mobile_messages页面的刷新令牌认证 return this._authenticateWithRefreshToken(); } else { // 其他页面引导到登录页,让用户输入邮箱密码 return this.transitionTo('login'); // 如果需要自动认证(比如记住密码),可以替换为: // return this._authenticateWithEmailPassword(storedEmail, storedPassword); } } // 私有方法:刷新令牌认证逻辑 _authenticateWithRefreshToken() { const refreshToken = localStorage.getItem('refresh_token'); // 没有刷新令牌则跳转到登录页 if (!refreshToken) { return this.transitionTo('login'); } // 调用自定义认证器,传入刷新令牌 return this.session.authenticate('authenticator:jwt', { refresh_token: refreshToken }) .catch((error) => { // 刷新令牌无效,清除存储并跳转登录 localStorage.removeItem('refresh_token'); this.transitionTo('login'); console.error('刷新令牌认证失败:', error); }); } // 可选:邮箱密码自动认证逻辑(比如记住密码场景) _authenticateWithEmailPassword(email, password) { return this.session.authenticate('authenticator:jwt', { email, password }) .catch(() => this.transitionTo('login')); } }
2. 调整自定义JWT认证器的authenticate方法
你的认证器需要根据传入的凭证类型(邮箱密码/刷新令牌)调用不同的后端接口,所以修改authenticate方法如下:
// app/authenticators/jwt.js import JWT from 'ember-simple-auth/authenticators/jwt'; export default class CustomJWTAuthenticator extends JWT { authenticate(credentials, headers) { return new Ember.RSVP.Promise((resolve, reject) => { // 根据凭证判断调用哪个接口 const authUrl = credentials.refresh_token ? '/auth/mobile_token' : '/auth/login'; this.makeRequest(authUrl, credentials, headers) .then((response) => { Ember.run(() => { try { const sessionData = this.handleAuthResponse(response); // 如果是刷新令牌认证,记得更新本地存储的刷新令牌 if (credentials.refresh_token && response.refresh_token) { localStorage.setItem('refresh_token', response.refresh_token); } resolve(sessionData); } catch (error) { reject(error); } }); }) .catch((error) => { Ember.run(() => reject(error)); }); }); } }
3. 完善登录页的认证流程
在登录页,用户提交邮箱密码后,调用认证器并存储返回的刷新令牌:
// app/routes/login.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; export default class LoginRoute extends Route { @service session; @tracked email; @tracked password; @tracked errorMessage; // 登录提交方法 submitLogin() { this.session.authenticate('authenticator:jwt', { email: this.email, password: this.password }) .then(() => { // 存储刷新令牌供后续mobile_messages页面使用 const newRefreshToken = this.session.data.authenticated.refresh_token; if (newRefreshToken) { localStorage.setItem('refresh_token', newRefreshToken); } // 认证成功跳转到默认页面 this.transitionTo('dashboard'); }) .catch((error) => { this.errorMessage = `登录失败:${error.message}`; }); } }
4. 处理会话失效场景
在application路由中监听会话失效事件,清除刷新令牌并引导用户到登录页:
// app/routes/application.js // ... 之前的代码 ... init() { super.init(...arguments); this.session.on('invalidated', () => { localStorage.removeItem('refresh_token'); this.transitionTo('login'); }); }
这样就完成了差异化认证的实现:除mobile_messages外的所有页面需要用户先通过邮箱密码登录,而mobile_messages页面会自动尝试用刷新令牌完成认证,失败则引导到登录页。
内容的提问来源于stack exchange,提问作者Nick Shcherba
相关产品推荐
相关产品推荐

