preface

Almost all projects require login. No matter the requirements of permission restriction, personalized customization, information security, etc., user information must be obtained through login system so as to provide follow-up services.

A company may have several different projects, each of which share the same user system at the back end, and there will be a need for a common login.

There are many ways to implement a common login, but let’s just look at the front-end implementation.

Project subdomains are different, but share one parent domain

By setting the domain attribute of a cookie, the content carried in the cookie can be shared between the parent and child domain names.

According to this feature, after login, the token is saved in the cookie, and all subprojects can share the token.

The login system will be put out as a separate project, and all other projects will be redirected to the independent login system without login. After login, it will jump to the corresponding page according to the source. The simple implementation is as follows:

// If the subitem is not logged in, it will jump to the corresponding login item and bring the current URL as a parameter to the login system
location.replace('https://login.abc.com?redirectUrl' + window.location.href)

// Login system after login, according to the redirectUrl jump back to the corresponding project
location.replace(redirectUrl)
Copy the code

This approach is the simplest, and since the login is a separate project, you can also put personalized customization into the project, just need to jump to other projects in addition to redirectUrl, multiple project type parameters (parameter name can be arbitrary) can be customized for different subsystems of the personalized login interface.

Same domain, but differentiated by gateway

The implementation effect is the same as above, but because it is in the same domain, so the operational place is more, token is not limited to cookies, any localStorage can be used, such as sessionStorage, localStorage and other local cache.

Generally, this mode is used on the PC, which is highly customized. However, more resources are required to log in to the project at the same time, which affects the loading speed.

NPM

The login components, interfaces, and logic are all packaged into an NPM package, and the used projects can be imported as needed, and then the unified login method is invoked.

Just like writing component business, writing the login as a separate business component has the disadvantage that when the login business is upgraded, all related projects need to be rebuilt and released.

CDN SDK

On the first front end of the advanced inside has been talked about, SDK unified login scheme, here to take out in detail, by the way, with part of the code to explain.

In fact, in general, no difficulty, is the entire login business encapsulation, do more general.

First of all, the login business needs to be broken down into the following four parts:

  1. Logging in to DOM Rendering
  2. Request module
  3. Event module used for login
  4. Callbacks after login events (success, failure, and so on)

Log in to the DOM rendering module

Pre-write the static HTML for the login. The template is then saved as a template string and the styles are written inline.

this.domTpl = `<div style="position: fixed; top: 0; left: 0; background: #fff; width: 100%; height: 100%; z-index: 9999; font-family: 'PingFangSC-Regular'">The ${this.close ? `<div id="closeIcon" style="position: absolute; right: 10px; top: 10px"><p style="height: 20px; width: 20px;" >X</p></div>` : ' '}
    The ${this.imgUrl.loginImgStart ? `<div class="logo" style="text-align: center; padding-top: 60px;" > <img src=The ${this.imgUrl.loginImgUrl}Style = "width: 36.6 vw; Height: 36.6 vw "/ > < / div > ` : ' '}< div style = "width: 78.6 vw; margin: 0 auto; margin-top: 16px;" > < img SRC =" 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: 1 px solid rgba (232232232, 1); padding-bottom: 10px;" "> <div style="width: 78.6vw; margin: 0 auto; display: flex;" > < span 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: 1 px solid rgba (232232232, 1); padding-bottom: 10px;" /> <p class="Obtain" style="width: 84px; Border: 1 px solid rgba (42112254, 1); font-size: 12px; padding: 5px 12px; text-align: center; margin: 20px 0 0px 0; color: #2A70FE; border-radius:8px;" "> <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 (203205209, 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 (255255255, 1); line-height:40px; margin-block-start: 0; margin-block-end: 0;" > < div style = "text-align: center;The ${this.agreement.start ? ` < div style = "width: 78.6 vw; margin: 0 auto; margin-top: 12px;" > <div id="notes" style="display: flex; align-content: center;" > <i id="regulations" style="display: block; background: url(The ${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;" > Read and agree <a href=The ${this.agreement.serverUrl}style="color: #2A70FE; text-decoration:none;" > User Service Agreement </a> and <a href=The ${this.agreement.privacyUrl}style="color: #2A70FE; text-decoration:none;" "></ a></p> </div> </div> ' : ' '}
</div>`;
Copy the code

Unified login interface, you can add some modules in advance customization, such as login logo, background picture, etc., will be more general.

In addition, in order to ensure the volume and loading speed of SDK, large image materials should be used as little as possible, small materials should be directly introduced in Base64, and large background images, such as relatively large resources, should be introduced in CDN.

Request module

To ensure high compatibility and the size of the SDK, native XHR requests are used directly, without additional Ajax request libraries and fetch.

// Send the Ajax request
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[`The ${this.dataStorage.storage}Even`](data.data.token); // Store the cache in the specified location according to the configured cache method
          if (this.success) this.success(data.data.token); // The direct successful callback passes the token to the caller
        }
        returndata; }}; }Copy the code

Event module used for login

The built-in events are as follows:

  1. Verification code sending
  2. Verify mobile phone, account number and verification code
  3. The login request
  4. Page is closed
  5. Prompt interaction
  6. Some optional additional features (e.g. whether to check protocol validation, etc.)
// Log in to related events
bindAction() {
// The mobile phone number is regular
let checkPhone = (phone) = > {
  if(! (/^1(3|4|5|6|7|8|9)\d{9}$/.test(phone))) {
    return false;
  } else {
    return true; }};/ / window
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'; }};// The verification code is related
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 = 'Get the verification code';
      clearInterval(interval);
      document.getElementsByClassName('ObtainStart') [0].className = 'Obtain';
      let Obtain = document.getElementsByClassName('Obtain') [0];
      Obtain.style.borderColor = '#2A70FE';
      Obtain.style.background = '#fff'; }},1000)};// Verify code event
document.getElementsByClassName('Obtain') [0].onclick = () = > {
  let phone = document.getElementById('phone').value;
  if(! checkPhone(phone)) { tipModel.show('Please enter the correct mobile number');
    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 event
if (this.close) {
  document.getElementById('closeIcon').onclick = () = > {
    this.hide();
  };
}

// Check whether the verification code exists
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'; }};// Landing events
document.getElementsByClassName('loginButton') [0].onclick = () = > {
  if (!document.getElementById('phone').value || !document.getElementById('code').value) {
    return tipModel.show('Please enter the correct mobile phone number and verification code');
  }
  if (this.agreement.start && document.getElementById('regulations').style.backgroundImage ! = =`url("The ${this.regulationsStart}") `) {
    return tipModel.show('Please read the User policy');
  }
  this.createXMLHttpRequest(this.dataStorage.url, tipModel.show)();
};

// User regulation event
if (this.agreement.start) {
  document.getElementById('notes').addEventListener('click'.() = > {
    let regulations = document.getElementById('regulations');
    let regulationsBackground = regulations.style.backgroundImage;
    if (regulationsBackground === `url("The ${this.regulations}") `) {
      regulations.style.backgroundImage = `url("The ${this.regulationsStart}") `;
    } else {
      regulations.style.backgroundImage = `url(The ${this.regulations}) `; }},false)}}Copy the code

Callbacks after login events (success, failure, and so on)

During initialization, you can pass in the required callback method, and then execute the corresponding callback event in the corresponding scenario.

As mentioned above, a simple and generic login SDK has been completed, which can be directly introduced in the project:

<! DOCTYPEhtml>
<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>The login</title>
  </head>
  <body style="margin: 0;"></body>
  <script type="text/javascript" src="./js/login.js"></script>
  <script>
    Login.init({
      imgUrl: {
        loginImgStart: true.loginImgUrl: "https://mirror-gold-cdn.xitu.io/168e088524247c4bcc7? imageView2/1/w/180/h/180/q/85/format/webp/interlace/1".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>
Copy the code

The effect is as follows:

As above, a generic login SDK was developed, with an overall compressed size of around 9KB. If that’s not enough, you can use the ES5 syntax to develop, and the volume can be reduced.

Point can be optimized

  1. After initializing the SDK, you can automatically or manually determine the login mode and process the login service according to your own needs
  2. According to their own project requirements, the general SDK is further customized

Write in the last

These are some simple solutions for independent development and deployment after stripping the login business. If there is a better solution or optimization point, please explore.

The project sample code will be uploaded to Github tomorrow. If you are interested, you can download and play with it and customize one yourself.