我这边技术栈是react+ts 如果你是vue,直接将tsx文件改成jsx就可以或者不该也没问题
上篇文章介绍了msal 的弹框登录,先介绍下重定向登录这个相对弹框登录要烦很多。。。中国内网看我查询的资料很少,只有微软系的公司才会有相对应的需求。此处自己研究了2天并实现了功能现分享给大家。
思路我们实现重定向登录。顾名思义就是我先在msal服务登录成功,并成功通过msal api拿到登陆后信息accounts,isAuthenticated。根据accounts获取令牌(token),再将令牌与我们自己后端api去认证改用户是否可以登录此系统(欧莱雅一个公司几万人不是所有的人都可以登录所以加了这个接口认证)。重定向登录一定要组件化并要包裹layout 否者无法实现,这点给弹框登录的最大区别。
点击按钮代码以及调用登录方法handleLogin('redirect');就不做解释了,这个很简单
1、环境配置文件相关参数全局新建 authConfig.js
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { LogLevel } from '@azure/msal-browser';
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL.js configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
*/
export const msalConfig = {
auth: {
clientId: '277e3d78-7012-4c89-8e32-', // 公司在msal客户端编号 ,给微信小程序的id 一样
authority:
'https://login.microsoftonline.com/e4e1abd9-eac7-4a71-ab52-', //由应用程序的身份提供者实例和登录受众组成,可能还有租户 ID
redirectUri: process.env.REDIRECT_URL, // 重定向的地址就是自己前端发布的访问地址,比如dev环境 http://localhost:8002
tenantId: 'e4e1abd9-eac7-4a71-ab52-',
scope: 'api://277e3d78-7012-4c89-8e32-/default',
postLogoutRedirectUri: 'https://pto-test.lindemobile.cn/#/logout',
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: 'localStorage', // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
default:
return;
}
},
},
},
};
/**
* Scopes you add here will be prompted for user consent during sign-in.
* By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
* For more information about OIDC scopes, visit:
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
export const loginRequest = {
scopes: [msalConfig?.auth.scope],
};
/**
* Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
*/
export const graphConfig = {
graphMeEndpoint: 'Enter_the_Graph_Endpoint_Herev1.0/me', //e.g. https://graph.microsoft.com/v1.0/me
};
export const tokenRequest = {
scopes: [msalConfig?.auth.scope],
forceRefresh: false, // Set this to "true" to skip a cached token and go to the server to get a new token
};
2、layouts文件
import { theme } from '@/app/config/theme';
import useAuthService, { AuthService } from '@/modules/User/useAuthService';
import useFilesService from '@/modules/File/useFileService';
import useFormatLanguageService, {
FormatLanguageService,
} from '@/tools/formatLanguage';
import { UseRequestProvider } from 'ahooks';
import 'antd/dist/antd.less';
import axios from 'axios';
import React, { useState } from 'react';
import { createGlobalStyle, ThemeProvider } from 'styled-components';
import { IRouteComponentProps } from 'umi';
import './layouts.less';
import bg from '@/assets/images/bg.jpg';
import heardBG from '@/assets/images/heardBG.png';
import background from '../../public/images/background.jpg';
import { getToken } from '@/uitls/auth';
import GetAuth from './AADLogin';
export default function Layout({
children,
location,
route,
history,
match,
}: IRouteComponentProps) {
const [loginSuccess, setLoginSuccess] = useState(false);
const formatLanguageService = useFormatLanguageService();
const fileService = useFilesService();
let token = getToken();
const authService = useAuthService({
mode: token ? '' : 'normal',
formatLanguageService,
defaultRoute: '/login',
token,
});
return (
// 注入主题样
<ThemeProvider theme={theme}>
<GetAuth //必须作为父盒子
onLoginSuccess={() => setLoginSuccess(true)}
loginSuccess={loginSuccess}
>
<UseRequestProvider
value={{
requestMethod: (param) =>
axios(param).then((r) => {
return r.data;
}),
}}
>
{children}
</UseRequestProvider>
</GetAuth>
</ThemeProvider>
);
}
3、给layout 文件同级新建AADLogin.tsx
import React, { useState } from 'react';
import { useEffect } from 'react';
import {
InteractionStatus,
PublicClientApplication,
} from '@azure/msal-browser';
import { useIsAuthenticated, useMsal, MsalProvider } from '@azure/msal-react';
import { message } from 'antd';
import { history } from 'umi';
import { loginRequest, msalConfig } from '@/authConfig';
import Loading from '@/components/Loading';
import { GetUserInfo, ThirdLogin } from '@/app/request/requestApi';
import { setMenus, setRefershToken, setToken } from '@/uitls/auth';
export interface LoginProps {
onLoginSuccess?: () => void;
loginSuccess?: boolean;
children?: any;
}
// 一定要放在最外层
//下面的所有组件MsalProvider都可以通过上下文访问PublicClientApplication实例,以及@azure/msal-react.
const msalInstance = new PublicClientApplication(msalConfig);
function GetAuthSub(props: LoginProps) {
console.log(props, '--------');
const [loading, setLoading] = useState<boolean>(false);
const { instance: msalObject, accounts, inProgress } = useMsal();
const isAuthenticated = useIsAuthenticated(); //判断用户是否通过身份验证
useEffect(() => {
msalInstance
.handleRedirectPromise()
.then((s) => {
console.log(s);
if (s !== null && s.account != null)
// currentAccount.setCurrentAccount(s.account.username, s.account.homeAccountId, s.account.tenantId);
console.log('success');
})
.catch((a: any) => {
console.log('err');
console.log(a);
});
}, []);
useEffect(() => {
if (sessionStorage.getItem('loginType') !== 'Gool') {
props.onLoginSuccess();
console.log(accounts, inProgress, isAuthenticated, '--------');
if (inProgress === InteractionStatus.Logout) {
return;
}
//登录 如果要初始化就调用Az登录放开此处
if (inProgress === InteractionStatus.None && !isAuthenticated) {
// msalInstance.loginRedirect(loginRequest);
}
if (
inProgress === InteractionStatus.None &&
accounts.length > 0 &&
isAuthenticated &&
!sessionStorage.getItem('ADToken')
) {
const curAccount = msalInstance.getActiveAccount() || accounts[0];
const tokenRequest = {
account: curAccount,
scopes: [...loginRequest.scopes],
};
//获取访问令牌 token
msalInstance.acquireTokenSilent(tokenRequest).then((response) => {
sessionStorage.setItem('ADToken', response?.accessToken);
// 调用自己后端认证msal token
onThirdLogin(response?.accessToken);
});
}
}
//accounts登录信息 isAuthenticated是否授权
}, [accounts, isAuthenticated, inProgress]);
return <>{props.children}</>;
}
// 获取token
const onThirdLogin = (accessToken) => {
Loading.show();
const params = {
accessToken,
type: 7,
code: 'code',
clientType: 0,
};
ThirdLogin(params)
.then((res) => {
if (res.success) {
message.success('Login succeeded');
sessionStorage.setItem('loginType', 'AD');
setToken(res.data.accessToken);
setRefershToken(res.data.refreshToken);
Loading.hide();
onGetUserInfo('/LOREAL/allItems/items');
} else {
Loading.hide();
message.error(res.msg);
history.replace('/');
}
})
.catch((err) => {
Loading.hide();
message.error(err.msg);
});
};
// 用户信息
const onGetUserInfo = (homeRoute) => {
GetUserInfo({}).then((res) => {
if (res.success) {
setMenus(JSON.stringify(res.data.menus));
sessionStorage.setItem('realName', res.data.account.realName);
sessionStorage.setItem('roles', res.data.account.roles[0]);
history.replace(homeRoute);
} else {
message.error(res?.msg);
}
});
};
// 登录
export const handleLogin = (loginType) => {
if (loginType === 'popup') {
//弹框登录
msalInstance.loginPopup(loginRequest).catch((e) => {
console.log(e);
});
} else if (loginType === 'redirect') {
//重定向登录
msalInstance.loginRedirect(loginRequest);
}
};
// 登出
export const signOut = () => {
const logoutRequest = {
postLogoutRedirectUri: msalConfig.auth.redirectUri,
mainWindowRedirectUri: msalConfig.auth.redirectUri,
};
msalInstance.logoutRedirect(logoutRequest);
};
// 组件化 MsalProvider 获取msal授权流
const GetAuth = (props: LoginProps) => {
return (
<MsalProvider instance={msalInstance}>
<GetAuthSub
onLoginSuccess={props.onLoginSuccess}
loginSuccess={props.loginSuccess}
>
{props.loginSuccess ? props.children : null}
</GetAuthSub>
</MsalProvider>
);
};
export default GetAuth;
登录成功。。。
国内资料很少。如有疑问请留言。推荐用梯子去查资料