如何修改Cypress MSAL登录命令以支持多用户登录
Cypress MSAL 多用户登录逻辑改造方案
核心改造思路为参数化认证入参+用户级缓存隔离,在保留现有令牌缓存、免UI登录提效能力的基础上,支持任意凭据传入,同时避免多用户测试时的登录态串号问题,改造全程不需要改动原有令牌请求、解码、localStorage注入的核心逻辑。
1. 配置层改造(authSettings.json)
拆分全局固定配置和多用户配置,避免把租户、客户端ID这类通用参数和用户凭据混写:
{ "azureAd": { "tenantId": "测试环境租户ID", "clientId": "测试应用客户端ID", "authority": "https://login.microsoftonline.com/对应租户ID", "scopes": ["openid", "profile", "业务接口所需权限范围"] }, "testUsers": { "admin": { "username": "admin@yourtenant.onmicrosoft.com", "password": "管理员账号密码", "tag": "全局管理员" }, "regular": { "username": "user@yourtenant.onmicrosoft.com", "password": "普通用户账号密码", "tag": "普通业务用户" }, "guest": { "username": "guest@externaldomain.com", "password": "访客账号密码", "tag": "外部访客" } } }
敏感凭据不要直接提交到代码仓库,正式环境可把密码字段替换为Cypress环境变量占位符,本地开发用加入
.gitignore的cypress.env.json存真实密码,CI环境从流水线密钥注入即可。
2. 核心认证逻辑改造(auth.js)
把原来硬编码读取单用户配置的逻辑改为支持动态入参,同时给缓存增加用户维度的隔离标识,避免不同用户的令牌互相覆盖:
import authSettings from "./authSettings.json"; // 生成用户维度的缓存唯一key const getUserCacheKey = (username) => `msal_cache_${btoa(username)}`; // 改造原有令牌获取方法,新增用户参数 export const fetchAndCacheMsalToken = async (userOptions) => { let credentials = {}; // 兼容两种传参模式 if (typeof userOptions === "string") { // 传入预定义用户key,从配置读取凭据 if (!authSettings.testUsers[userOptions]) throw new Error(`未找到预定义测试用户:${userOptions}`); credentials = authSettings.testUsers[userOptions]; } else if (typeof userOptions === "object" && userOptions.username && userOptions.password) { // 传入自定义凭据对象,直接使用 credentials = userOptions; } else { throw new Error("登录参数非法,请传入预定义用户key,或包含username/password的凭据对象"); } // 优先读取当前用户的有效缓存 const cacheKey = getUserCacheKey(credentials.username); const cachedToken = Cypress.env(cacheKey); if (cachedToken && Date.now() < cachedToken.expiresOn) return cachedToken; // 缓存失效/不存在时,走你原有的令牌请求、解码逻辑 const tokenRes = await /* 保留你原来的MSAL令牌请求代码 */; const decodedToken = /* 保留你原来的JWT解码代码 */; const tokenData = { idToken: tokenRes.idToken, accessToken: tokenRes.accessToken, expiresOn: decodedToken.exp * 1000, homeAccountId: `${decodedToken.oid}.${decodedToken.tid}` }; // 仅写入当前用户的缓存,不覆盖其他用户的缓存数据 Cypress.env(cacheKey, tokenData); return tokenData; }; // localStorage注入逻辑不需要改动,直接复用原有代码即可 export const injectTokenToStorage = (tokenData) => { /* 保留你原来的localStorage写入逻辑 */ };
3. 自定义命令改造(commands.js)
给LoginWithMsal命令增加可选入参,设置默认登录用户降低无特殊需求用例的代码量,推荐配合Cypress原生cy.session()做会话隔离,比自定义缓存更稳定:
import { fetchAndCacheMsalToken, injectTokenToStorage } from "./auth"; Cypress.Commands.add("loginWithMsal", (userOptions = "regular") => { cy.session(`msal_${typeof userOptions === "string" ? userOptions : userOptions.username}`, () => { return fetchAndCacheMsalToken(userOptions).then((tokenData) => { cy.window().then((win) => { win.localStorage.clear(); injectTokenToStorage.call(win, tokenData); }); }); }, { // 简单校验登录态有效性,避免缓存失效导致用例失败 validate() { cy.visit("/"); cy.get("[data-testid='user-avatar']").should("exist"); } }); });
4. 测试用例调用方式
在beforeEach或单个用例中按需传入参数即可,不需要修改其他测试逻辑:
describe("权限场景测试", () => { it("管理员可访问用户管理页面", () => { // 传入预定义用户key登录 cy.loginWithMsal("admin"); cy.visit("/admin/users"); cy.get("[data-testid='user-list']").should("be.visible"); }); it("临时测试账号可正常登录", () => { // 传入自定义凭据登录,不需要提前写入配置 cy.loginWithMsal({ username: "temptest@yourtenant.onmicrosoft.com", password: "testpass123" }); cy.visit("/profile"); cy.contains("temptest").should("be.visible"); }); it("访客无管理页面访问权限", () => { cy.loginWithMsal("guest"); cy.visit("/admin/users", { failOnStatusCode: false }); cy.contains("无权限访问").should("be.visible"); }); });
落地注意事项
- 不同用户的缓存、会话完全隔离,不会出现A用户操作后串到B用户登录态的问题
- 令牌过期后会自动触发重新请求,不需要手动处理缓存失效逻辑
- 如需测试账号锁定、密码错误等异常场景,直接传入对应异常凭据即可,不需要修改核心认证逻辑
内容的提问来源于stack exchange,提问作者E. E. Ozkan
相关产品推荐
相关产品推荐

