предисловие
Почти все проекты требуют входа в систему.Будь то ограничения разрешений, персонализированная настройка, информационная безопасность и другие требования, информация о пользователе должна быть получена через систему входа для предоставления последующих услуг.
У компании может быть несколько разных проектов, и если серверная часть каждого проекта использует один и тот же набор пользовательских систем, неизбежно возникнет необходимость в универсальном входе.
Существует множество способов универсального входа, и ниже мы обсудим только схему реализации фронтенда.
Поддомены проекта разные и имеют один и тот же родительский домен.
установив кукиdomainатрибут, так что контент, переносимый файлом cookie, может совместно использоваться под доменным именем родитель-потомок.
Согласно этой функции, после входа в систему токен сохраняется в файле cookie, и все подпроекты могут совместно использовать токен.
Система входа предлагается отдельно и вынесена в отдельный проект.Все остальные проекты перенаправляются на независимую систему входа без входа в систему.После входа в систему перейдите на соответствующую страницу в соответствии с источником.Простая реализация выглядит следующим образом:
// 子项目在判断未登录的时候,跳转对应的登录项目并将当前的url作为参数带给登录系统
location.replace('https://login.abc.com?redirectUrl' + window.location.href)
// 登录系统在登录之后,根据redirectUrl跳回对应的项目
location.replace(redirectUrl)
Этот способ самый простой, а так как логин является самостоятельным проектом, то в проект также можно внести индивидуальную настройку, нужно только добавить параметры типа проекта (названия параметров необязательны) в дополнение к redirectUrl при переходе на другие проекты. можно настроить персонализированный интерфейс входа в систему для различных подсистем.
Тот же домен, но дифференцируют проекты воротами
Эффект от реализации тот же, что и выше, но поскольку он находится в том же домене, места для работы больше.Токены не ограничиваются файлами cookie.Можно использовать любой метод локального хранения, например, sessionStorage, localStorage и другие локальные кэши.
Как правило, этот метод используется на стороне ПК, который сильно настраивается, но в то же время будет больше ресурсов для входа в проект, что повлияет на скорость загрузки.
NPM
Компоненты, интерфейсы и логика входа упакованы в пакеты npm, а используемые проекты можно импортировать по мере необходимости, после чего можно вызвать единый метод входа.
Как и при написании бизнес-компонента, логин пишется как независимый бизнес-компонент. Недостаток заключается в том, что при обновлении бизнес-логина все связанные проекты необходимо перестроить и выпустить.
CDN SDK
Как упоминалось в основном интерфейсе, расширенном в предыдущей статье, унифицированная схема входа в систему sdk будет подробно описана здесь, и, кстати, будут приложены некоторые пояснения к коду.
На самом деле, в общем-то, нет никакой сложности, просто инкапсулируйте весь бизнес входа в систему и сделайте его более общим.
Прежде всего, для анализа бизнес входа в систему необходимо разделить на следующие 4 части:
- Войти DOM-рендеринг
- модуль запроса
- Войдите в используемый модуль событий
- Обратный вызов после события входа в систему (успех, сбой и т. д.)
Войдите в модуль рендеринга DOM
Заранее напишите статический html логина. Затем сохраните письменный шаблон как строку шаблона, и стильвстроенный стильнаписать.
this.domTpl = `<div style="position: fixed; top: 0; left: 0; background: #fff; width: 100%; height: 100%; z-index: 9999;font-family: 'PingFangSC-Regular'">
${this.close ? `<div id="closeIcon" style="position: absolute; right: 10px; top: 10px"><p style="height: 20px; width: 20px;" >X</p></div>` : ''}
${this.imgUrl.loginImgStart ? `<div class="logo" style="text-align: center; padding-top: 60px;">
<img src=${this.imgUrl.loginImgUrl} style="width: 36.6vw; height: 36.6vw" />
</div>` : ''}
<div style="width: 78.6vw; margin: 0 auto; margin-top: 16px;">
<input id="phone" type="text" name="phone" placeholder="请输入手机号码"
style="width: 100%;font-size: 16px; padding-top: 22px; -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
outline: none;border: none;border-bottom: 1px solid rgba(232,232,232,1);padding-bottom: 10px;" />
</div>
<div style="width: 78.6vw; margin: 0 auto; display: flex;">
<input id="code" type="text" placeholder="请输入验证码"
style="width: calc(100% - 94px); font-size: 16px; padding-top: 22px; -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
outline: none;border: none;border-bottom: 1px solid rgba(232,232,232,1);padding-bottom: 10px;" />
<p class="Obtain" style="width: 84px;border:1px solid rgba(42,112,254,1); font-size: 12px;padding: 5px 12px; text-align: center;margin: 20px 0 0px 0;
color: #2A70FE;border-radius:8px;">获取验证码</p>
</div>
<div style="width: 78.6vw; margin: 0 auto;margin-top: 45px;position: relative;">
<div class="tipModel" style="display: none; position: absolute; top: -24px; left: 0; right: 0; color: #FF495F; font-size: 12px; text-align: center; margin-bottom: 12px;">123</div>
<p class="loginButton" style="font-size: 17px;background:rgba(203,205,209,1);box-shadow:0px 1px 4px 0px rgba(82,88,102,0.2);border-radius:4px; text-align: center;
font-family: 'PingFangSC-Regular';font-weight:400;color:rgba(255,255,255,1);line-height:40px;margin-block-start: 0;margin-block-end: 0;">登录</p>
</div>
${this.agreement.start ? `<div style="width: 78.6vw; margin: 0 auto;margin-top: 12px;">
<div id="notes" style="display: flex;align-content: center;">
<i id="regulations" style="display: block;background: url(${this.regulations}); background-size: cover; width: 16px; height: 16px;margin-right: 5px;"></i>
<p style="color: #7A8599;font-size: 12px;margin-block-start: 0;margin-block-end: 0;">已阅读并同意<a href=${this.agreement.serverUrl} style="color: #2A70FE;text-decoration:none;">《用户服务协议》</a>和<a href=${this.agreement.privacyUrl} style="color: #2A70FE;text-decoration:none;">《隐私政策》</a></p>
</div>
</div>` : ''}
</div>`;
Унифицированный интерфейс входа в систему можно настроить, заранее добавив некоторые модули, такие как логотип входа, фоновое изображение и т. д., которые будут более общими.
Кроме того, чтобы обеспечить размер и скорость загрузки sdk, используйте как можно меньше больших материалов изображений, небольшие материалы импортируются напрямую в base64, а большие ресурсы, такие как фоновые большие изображения, импортируются с помощью CDN.
модуль запроса
чтобы убедиться, чтовысокая совместимость, и размер sdk, поэтому нативный xhr-запрос используется напрямую, без использования дополнительных библиотек ajax-запросов и выборки.
// 发送ajax请求
createXMLHttpRequest(url, errFun) {
let xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", url, false);
xmlHttp.setRequestHeader('content-type', 'application/json');
xmlHttp.send(this.paramsEven());
return xmlHttp.onreadystatechange = () => {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
let data = JSON.parse(xmlHttp.responseText);
if (data.code !== 0) {
return errFun(data.errMsg);
}
if (url === this.dataStorage.url) {
this[`${this.dataStorage.storage}Even`](data.data.token); // 根据配置缓存方法,将缓存存到制定的位置
if (this.success) this.success(data.data.token); // 直接成功回调,把 token 传给调用者
}
return data;
}
};
}
Войдите в используемый модуль событий
Необходимые встроенные события:
- Отправить код подтверждения
- Мобильный телефон, номер счета, подтверждение кода подтверждения
- запрос на вход
- страница закрыть
- оперативное взаимодействие
- Некоторые необязательные дополнительные функции (например: нужно ли вам проверять проверку протокола и т. д.)
// 登陆相关事件
bindAction() {
// 手机号正则
let checkPhone = (phone) => {
if (!(/^1(3|4|5|6|7|8|9)\d{9}$/.test(phone))) {
return false;
} else {
return true;
}
};
// 弹窗
let tipModel = {
show: (tipFont) => {
let tipModel = document.getElementsByClassName('tipModel')[0];
tipModel.innerHTML = tipFont;
tipModel.style.display = 'block';
},
hide: () => {
document.getElementsByClassName('tipModel')[0].style.display = 'none';
}
};
// 验证码相关
let ObtainFun = () => {
let ObtainStart = document.getElementsByClassName('ObtainStart')[0];
let time = 50;
ObtainStart.innerHTML = `${time} S`;
ObtainStart.style.borderColor = 'rgba(245,246,247,1)';
ObtainStart.style.background = 'rgba(245,246,247,1)';
time = time - 1;
let interval = setInterval(() => {
ObtainStart.innerHTML = `${time} S`;
time = time - 1;
if (time < 0) {
ObtainStart.innerHTML = `获取验证码`;
clearInterval(interval);
document.getElementsByClassName('ObtainStart')[0].className = 'Obtain';
let Obtain = document.getElementsByClassName('Obtain')[0];
Obtain.style.borderColor = '#2A70FE';
Obtain.style.background = '#fff';
}
}, 1000)
};
// 验证码事件
document.getElementsByClassName('Obtain')[0].onclick = () => {
let phone = document.getElementById('phone').value;
if (!checkPhone(phone)) {
tipModel.show('请输入正确的手机号码');
return false;
}
let dataInfo = {};
if (document.getElementsByClassName('Obtain')[0]) {
dataInfo = this.createXMLHttpRequest(this.dataStorage.verifyCodeUrl, tipModel.show)();
}
if (dataInfo.code === 0) {
document.getElementsByClassName('Obtain')[0].className = 'ObtainStart';
ObtainFun();
}
};
// closeIcon事件
if (this.close) {
document.getElementById('closeIcon').onclick = () => {
this.hide();
};
}
// 判断验证码是否存在
document.getElementById('code').oninput = () => {
let codeVal = document.getElementById('code').value;
if (codeVal) {
let loginButton = document.getElementsByClassName('loginButton')[0];
loginButton.style.background = '#3D424D';
loginButton.style.color = '#fff';
}
};
// 登陆事件
document.getElementsByClassName('loginButton')[0].onclick = () => {
if (!document.getElementById('phone').value || !document.getElementById('code').value) {
return tipModel.show('请输入正确的手机号码和验证码');
}
if (this.agreement.start && document.getElementById('regulations').style.backgroundImage !== `url("${this.regulationsStart}")`) {
return tipModel.show('请阅读用户相关条例');
}
this.createXMLHttpRequest(this.dataStorage.url, tipModel.show)();
};
// 用户条例事件
if (this.agreement.start) {
document.getElementById('notes').addEventListener('click', () => {
let regulations = document.getElementById('regulations');
let regulationsBackground = regulations.style.backgroundImage;
if (regulationsBackground === `url("${this.regulations}")`) {
regulations.style.backgroundImage = `url("${this.regulationsStart}")`;
} else {
regulations.style.backgroundImage = `url(${this.regulations})`;
}
}, false)
}
}
Обратный вызов после события входа в систему (успех, сбой и т. д.)
При инициализации вы можете передать требуемый метод обратного вызова, а затем выполнить соответствующее событие обратного вызова в соответствующем сценарии.
Как и выше, простой и общий sdk для входа выполнен, в проект его можно напрямую импортировать:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no, shrink-to-fit=no,viewport-fit=cover"/>
<title>登录</title>
</head>
<body style="margin: 0;"></body>
<script type="text/javascript" src="./js/login.js"></script>
<script>
Login.init({
imgUrl: {
loginImgStart: true,
loginImgUrl: "https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/mirror-assets/168e088524247c4bcc7~tplv-t2oaga2asx-image.image",
loginImgStyleWidth: "130px",
loginImgStyleHeight: "130px"
},
agreement: {
start: true,
serverUrl: '',
privacyUrl: ''
},
close: true,
success() {
console.log('success')
},
error() {
console.log('error')
},
dataStorage: {
path: 'https://login.com'
}
})
</script>
</html>
Эффект следующий:
Как и выше, был разработан общий пакет SDK для входа, а общий сжатый размер составляет около 9 КБ. Если вы чувствуете, что этого недостаточно, вы можете использовать синтаксис es5 для разработки, а объем можно сжать еще немного.
Оптимизируемая точка
- После инициализации sdk его можно настроить на автоматическое и ручное определение статуса входа и выполнение бизнес-обработки входа в соответствии с собственными потребностями.
- Дальнейшая настройка общего SDK в соответствии с потребностями вашего проекта
напиши в конце
Выше приведены некоторые простые решения, которые разрабатываются и развертываются независимо после того, как бизнес входа в систему удален.Если есть лучшие решения или точки оптимизации, вы можете их обсудить.
Пример кода проекта будет загружен завтра наGithub, Если вы заинтересованы, вы можете скачать и играть и настроить себя.