Azure应用注册与配置
1. 进入应用注册页面
登录 Microsoft Entra 管理中心(或 Azure 门户),在左侧菜单中依次选择 “标识” (或 “Microsoft Entra ID”)→ “应用程序” → “应用注册” ,然后点击 “+ 新建注册” 。
2. 填写应用注册信息
在 “注册应用程序” 页面中:
- 名称:输入一个有意义的应用名称(例如
vue-azure-login),用户可能会看到此名称。 - 支持的帐户类型:根据你的需求选择。如果你只允许自己组织内的账户登录,选择 “仅此组织目录中的帐户(单租户)” ;如果希望任何组织的账户都能登录,选择 “任何组织目录中的帐户(多租户)” 。
- 重定向 URI:这一步对后续登录跳转至关重要。在平台下拉框中选择 “单页应用程序(SPA)” ,然后输入你的 Vue 应用地址,例如本地开发环境为
http://localhost:5173。 - 点击 “注册” 按钮完成创建。
3. 记录必要信息
注册完成后,页面会自动跳转到应用 “概述” 页。请记录下以下两个关键值,后续需要填入 auth.js 配置中:
- 应用程序(客户端)ID:唯一标识此应用的 ID。
- 目录(租户)ID:你的租户 ID。
前端项目集成
npm install @azure/msal-browser
配置.env
以本地测试为例
VITE_AZURE_CLIENT_ID=应用程序(客户端) ID
VITE_AZURE_TENANT_ID=目录(租户) ID
VITE_AZURE_REDIRECT_URI=http://localhost:5173
VITE_AZURE_POST_LOGOUT_REDIRECT_URI=http://localhost:5173
再随便写几行用于auth的工具(/src/utils/auth.js)
import {
PublicClientApplication,
InteractionRequiredAuthError,
LogLevel,
} from '@azure/msal-browser'
// ============ MSAL 配置 ============
const msalConfig = {
auth: {
clientId: import.meta.env.VITE_AZURE_CLIENT_ID,
authority: `https://login.microsoftonline.com/${import.meta.env.VITE_AZURE_TENANT_ID}`,
// 登录后重定向回应用的地址,必须与 Azure 应用注册中的重定向 URI 一致
redirectUri: import.meta.env.VITE_AZURE_REDIRECT_URI || window.location.origin,
postLogoutRedirectUri: import.meta.env.VITE_AZURE_POST_LOGOUT_REDIRECT_URI || window.location.origin,
},
cache: {
cacheLocation: 'localStorage', // 持久化缓存,刷新页面保持登录
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback(level, message, containsPii) {
if (containsPii) return
switch (level) {
case LogLevel.Error: console.error(message); break
case LogLevel.Warning: console.warn(message); break
case LogLevel.Info: console.info(message); break
case LogLevel.Verbose: console.debug(message); break
}
},
logLevel: LogLevel.Warning,
},
},
}
// 请求的权限范围(登录获取用户基本信息)
const loginRequest = {
scopes: ['openid', 'profile', 'User.Read'],
}
// 创建 MSAL 实例
const msalInstance = new PublicClientApplication(msalConfig)
// ============ 初始化(必须在使用前调用一次) ============
let isInitialized = false
export async function initAuth() {
if (isInitialized) return
await msalInstance.initialize()
// 处理重定向返回的结果(从微软登录页跳回来时会执行)
try {
const response = await msalInstance.handleRedirectPromise()
if (response) {
// 登录成功,设置当前活跃账户
msalInstance.setActiveAccount(response.account)
console.info('[Auth] 重定向登录成功:', response.account.username)
} else {
// 非重定向返回,尝试从缓存恢复活跃账户
const accounts = msalInstance.getAllAccounts()
if (accounts.length > 0) {
msalInstance.setActiveAccount(accounts[0])
}
}
} catch (error) {
console.error('[Auth] 处理重定向响应失败:', error)
}
isInitialized = true
}
// ============ 获取当前活跃账户 ============
export function getActiveAccount() {
return msalInstance.getActiveAccount()
}
// ============ 是否已登录 ============
export function isAuthenticated() {
return msalInstance.getAllAccounts().length > 0
}
// ============ 登录(重定向到微软登录页) ============
export async function login() {
// 如果已有账户,先尝试静默获取令牌
const accounts = msalInstance.getAllAccounts()
if (accounts.length > 0) {
msalInstance.setActiveAccount(accounts[0])
try {
const result = await msalInstance.acquireTokenSilent({
...loginRequest,
account: accounts[0],
})
return result
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
// 静默失败,走重定向登录
await msalInstance.loginRedirect(loginRequest)
} else {
throw error
}
}
} else {
// 无账户,直接重定向到微软登录页
await msalInstance.loginRedirect(loginRequest)
}
}
// ============ 登出 ============
export async function logout() {
const account = msalInstance.getActiveAccount()
await msalInstance.logoutRedirect({
account,
postLogoutRedirectUri: msalConfig.auth.postLogoutRedirectUri,
})
}
// ============ 获取访问令牌(用于调用受保护的 API) ============
export async function getAccessToken(scopes = ['User.Read']) {
const account = msalInstance.getActiveAccount()
if (!account) {
throw new Error('用户未登录')
}
try {
const result = await msalInstance.acquireTokenSilent({
scopes,
account,
})
return result.accessToken
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
// 需要用户交互(如令牌过期),走重定向
await msalInstance.acquireTokenRedirect({ scopes, account })
} else {
throw error
}
}
}
// ============ 导出 msalInstance(供需要时直接使用) ============
export { msalInstance, loginRequest }
在main.js中初始化一下msal实例
import { initAuth } from './utils/auth'
await initAuth()
最后在路由守卫中加上必要的鉴权逻辑,进行登录跳转即可
import { isAuthenticated, login } from '../utils/auth'
// 全局前置守卫,这里可以加入用户登录判断
router.beforeEach(async (to, from, next) => {
const message = window.message
if (import.meta.env.VITE_SECURE === 'true') {
// 检查是否有token
if (isAuthenticated()) {
// 如果有token,正常导航
next()
} else {
// 如果没有token,导航到登录页面
message.info("身份校验失败,SSO登录中...")
await login()
}
}else{
// 继续前进 next()
// 返回 false 以取消导航
next()
}
})
这时打开页面会进行登录页面的跳转
大功告成