[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>
~~~
