[TOC]
### 實現原理:
~~~
前后臺實現session共享,實現一次登錄現在所有子系統
~~~
~~~
后臺:每個系統是在不同服務器上的,因此每個服務器上都會有有一個不同的session信息。實現單點登錄只需登錄一次將不同服務器中的session同步即可。
前端:每個系統登錄之后會將session信息設置到cooke中,實現單點登錄只需登錄一次將cooke跨域傳遞給不同域實現共享cooke即可
~~~
### 前端實現過程
#### 1. 登錄成功后設置session信息到cooke中
~~~
// 用戶登錄
async userLogin({ commit }, { loginname, loginpwd }) {
const key = await getRsaKey();
// rsa加密
const encrypt = new JSEncrypt();
encrypt.setPublicKey(key);
const rsapwd = encrypt.encrypt(loginpwd);
const data = await loginRsa({
loginName: loginname,
loginPwd: rsapwd
});
const { userCode, ownRegion } = data;
commit(SET_USER_INFO, data);
commit(SET_USER_SESSION, userCode); // todo
setSession(userCode);
return data;
},
······························································
// 設置session值
export function setSession(session = "ADE8DBC9-BD3F-49CD-BDD4-B712BB1C36B0") {
return Cookies.set(SESSION_KEY, session, { expires: 0.5 });
}
~~~
#### 2. 路由攔截判斷是否有記錄的用戶信息(沒有信息獲取信息),獲取后將cooke共享
~~~
router.beforeEach(async (to, from, next) => {
// TODO添加session
if (isPermission()) {
const hasSession = getSession();
if (hasSession) {
if (String(to.path).toLowerCase() === "/login") {
next({ path: "/" });
} else {
if (hasPermissionInfo() && hasUserInfo()) {
// 有權限信息進行正常路由跳轉
next();
} else {
try {
await store.dispatch("getUserInfo");
// 無權限信息先獲取權限信息
const permission = await store.dispatch("userGetPermission");
if (
permission &&
Array.isArray(permission) &&
permission.length > 0 &&
hasUserInfo()
) {
// 如果權限信息獲取成功,動態添加路由并繼續原來的路由跳轉
const accessRoutes = await store.dispatch(
"generateRoutes",
permission
);
router.addRoutes(accessRoutes);
// 同步到optios.routes對象中,方便在業務組件內部獲取
router.options.routes.push(...accessRoutes);
loginLog();
postCrossInfo();
// hack method to ensure that addRoutes is complete
// set the replace: true, so the navigation will not leave a history record
next({ ...to, replace: true });
} else {
// 如果權限信息獲取不成功,拋出錯誤,進行錯誤捕獲處理
const errorMsg = "未獲取到用戶權限";
iview.Message.error(errorMsg);
throw new Error(errorMsg);
}
} catch (error) {
// 用戶權限獲取失敗后 清空session,重新登錄
await store.dispatch("resetSession");
setRedirect(to.fullPath);
next(`/login`);
}
}
}
} else {
// 無session信息則跳轉到登錄頁面
if (whiteList.indexOf(String(to.path).toLowerCase()) !== -1) {
next();
} else {
setRedirect(to.fullPath);
next(`/login`);
}
}
} else {
// fix: Duplicate named routes definition
// 開發環境默認在外部導入全部路由,不異步導入
// router.addRoutes(asyncRoutes);
if (to.path === "/login") {
next({ path: "/" });
} else {
next();
}
}
});
function postCrossInfo() {
const { config } = store.getters;
const { CROSSDOMAINS } = config;
if (CROSSDOMAINS) {
if (Array.isArray(CROSSDOMAINS)) {
CROSSDOMAINS.forEach(domain => {
shareSession(document.cookie, domain);
});
} else if (
Object.prototype.toString.call(CROSSDOMAINS) === "[object String]"
) {
shareSession(document.cookie, CROSSDOMAINS);
}
}
}
·································································
import Url from "url-parse";
const FRAMMEID = "sessionshare";
const createIframe = url => {
const iframe = document.createElement("iframe");
iframe.setAttribute("id", FRAMMEID);
iframe.style.display = "none";
iframe.src = url;
document.body.appendChild(iframe);
return iframe;
};
const deleteIframe = iframe => {
if (iframe) {
iframe.parentNode.removeChild(iframe);
}
};
const shareSession = (data, url) => {
const iframe = createIframe(url);
const iframeCont = iframe.contentWindow;
const { host } = new Url(url);
iframe.onload = () => {
window.addEventListener("message", e => {
if (e.data === "ok") {
deleteIframe(iframe);
}
});
iframeCont.postMessage(data, `http://${host}`);
};
};
export default shareSession;
~~~
#### 3. 不同域獲取cooke信息并存入cooke中
~~~
<!-- 監聽設置cookie -->
<script>
window.addEventListener('message', function(e){
typeof(e.data)=='string' && e.data.split(";").forEach(function (cookie) {
document.cookie = cookie;
})
}, false);
</script>
~~~