由于文章的篇幅有限,無法將全部的代碼貼上來,如想要看完整案例,請在公眾號文章中留言(其他平臺很少看…畢竟最近印度同事的UI組件庫搞得我好煩)
1.關于SSO
單點登錄又稱之為SSO,全稱為 Single Sign On ,一般在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。
比如,當我們使用一個騰訊旗下的產品時,我們一般會接入QQ登陸,此時就可以認為QQ登陸為我們的SSO。只要我們登錄了QQ,就可以根據憑證獲取到你在QQ中的信息,并登錄該平臺。
2.SSO流程
無論使用第三方或者是自己的sso,下面將以一個開發人員的角度,以QQ作為例子講解一個SSO的過程,
1.當用戶第一次登錄平臺A的時候,由于該平臺使用了QQ的服務去獲取用戶信息,該平臺會自動調用QQ的服務(比如跳轉QQ登錄),此時我們會設置一個RedirectURL的參數到qq的login服務上。
2.當用戶從qq登錄后,qq的后臺服務會根據我們的 RedirectURL 參數,將一個token傳到我們的Redirect的地址。
3.當我們的服務接收到了來自QQ服務器的一個重定向請求,且該請求還會帶一個token,我們可以根據QQ文檔去調用Api獲取我們想要的信息,比如獲取用戶信息等。
4.當我們獲取到用戶的信息之后,我們用 JsonWebToken 的形式重新去設置我們的token,并且使用其作為前后端通訊的token。
在上述的過程中,我們使用第三方的SSO,是基于以下幾個原因
- qq本質上提供了用戶的信息給我們,并且提供了一個便捷的,獲取用戶信息的api。
- QQ作為一個維護多年的平臺,對于權限以及用戶管理等模塊已經很完善了
- 我們自己開發的話,我們需要花費大量的時間和精力,還不能保證一定沒問題
3.關于Azure
上面的過程中,已經知道SSO的流程,想必大家都已經對SSO有了初步的認知,而選擇一個SSO是要根據市場以及客戶所用的SSO有關,比如對于外企,我們會選擇Azure作為SSO,而不是選擇很少外國人用的QQ。
Microsoft Azure 作為微軟云計算,大公司背書.
最主要是我們的客戶選擇使用它,所以下面的例子會以其作為例子
4.使用SSO
不同平臺的sso參數思路‘大差不差’,都是用 AppID + AppScrect 這一套,所以下面的例子也按照這個套路來介紹。
1.準備各類參數,其中最主要的參數是CLIENT_ID,TENANT_ID,CLIENT_SECRET。
//當前域名
LOGIN_REDIRECT=https://xxx.com
//重定向地址
OAUTH2_REDIRECT_URL=https://xxx.com/user/login_callback
//client_id,在app的詳情中查看,由管理員給的
OAUTH2_CLIENT_ID=6aaaaaae-7aaa-4aaa-baaa-aaaaaaaaad89
//tent_id,可以理解為密鑰。由管理員給的
OAUTH2_TENANT_ID=4266ec6c-fe9f-4893-82e9-996189e0b81b
//在Azure上生成的,驗證機器是否允許登錄
OAUTH2_CLIENT_SECRET=mvaaa~.qLgH8aaaaaaaaaTpnWaLD9Em-H3Z6gb_T
2.準備我們的登陸接口重定向到auzre的登陸接口
當用戶調用我們的登陸接口時,我們會馬上調用到Azure的服務去登陸。
@Get('user/login')login(@Response() res) {res.redirect(this.userServie.processLogin());}
?
此時,瀏覽器的彈窗如下:
3.登陸成功后獲取到用戶的憑證
@Get('user/login_callback')async loginCallback(@Request() req, @Response() res) {let code = '';if (req.query.code) {code = req.query.code;const tokenInfo = await this.userServie.getAccessTokenByCode(code, req.log);// if redirect error, check cookie has refresh_tokenif (tokenInfo.error) {req.log.error(`user login callback error will redirect to login`);res.redirect('/login');} else {const { claimsInfo, user, groups } = this.userServie.processAccessToken(tokenInfo.access_token);
?if (!groups.includes(environmentConfig.azure.adGroupName)) {req.log.error('User not in AD group');res.status(400).json({ message: 'User not in AD group' });}
?
?req.log.info(`login user name is ${user.id}`);const redirectUrl = `${environmentConfig.cx.frontend_url}?t=${claimsInfo}`;res.redirect(redirectUrl);}} else {req.log.error('ADFS grant code not found');res.status(400).json({ message: 'ADFS grant code not found' });}}
?
上述代碼中,流程在于獲取到了azure的token之后,調用api獲取用戶信息,并生成新的token并給到前端。
4.根據憑證獲取到用戶的信息。
在上述代碼中,我們完成了整個流程,但是最主要的核心代碼如下
processAccessToken(azureToken) {const auzraUserInfo = JWT.decode(azureToken);const {onPremisesSamAccountName = '',cn = '',name = '',family_name = '',given_name = '',username = '',groups = []} = auzraUserInfo;let adKeyWord = '';let userName = '';adKeyWord = name;userName = `${given_name} ${family_name}`;const jwtToken = JWT.sign({cn: adKeyWord,sAMAccountName: adKeyWord,username: userName,auth: 'saml',thumbnail: ''},environmentConfig.cx.jwt_token_secret,// { expiresIn: 7 * 24 * 60 * 60 }{ expiresIn: 1 * 24 * 60 * 60 });return { claimsInfo: jwtToken, user: { id: userName }, groups: groups };}
至此,一個流程就結束了,我們將生成的 token 放到前端就可以了。
需要注意的是,我們的憑證是有expiry date的。
多謝關注~ 公眾號求關注~
公眾號文章