I. Four authentication methods

At present, we commonly use four kinds of authentication:

  • HTTP Basic Authentication
  • session-cookie
  • Token authentication
  • OAuth(Open License)

HTTP Basic Authentication

This authorization mode is a basic authorization mode for browsers to comply with THE HTTP protocol. The HTTP protocol defines the basic authentication method that allows the HTTP server to authenticate the user ID card of the client during the communication process through HTTP.

Certification process:

1. The client requests data from the server, which may be a web page or an Ajax asynchronous request. Assuming that the client is not authenticated, the client provides the following request to the server:

Get/index. HTTP / 1.0 HTML Host:www.google.comCopy the code

[www-authenticate: Basic realm= “google.com”] [www-authenticate: Basic realm= “google.com”] [www-authenticate: Basic realm= “google.com”]

HTTP/1.0 401 Unauthorised Server: SokEvo/1.0 WWw-Authenticate: Basic realm= "google.com" Content-type: text/html Content-Length: xxxCopy the code

3. When http1.0 or 1.1 compliant clients (IE, FIREFOX) receive the return value 401, a login window will pop up automatically, asking the user to enter a username and password.

4. After the user enters the user name and password, the user name and password are encrypted in BASE64 encryption mode, and the ciphertext is added to the previous request message. The first request message sent by the client is as follows:

  Get /index.html HTTP/1.0 
  Host:www.google.com 
  Authorization: Basic d2FuZzp3YW5n
Copy the code

Note: d2FuZzp3YW5n represents the user name and password after encryption (user name: the password is then encrypted through Base64, the encryption process is the default behavior of the browser, we do not need to manually encrypt, we just need to input the user name and password)

5. After receiving the above request information, the server takes out and decrypts the user information after the Authorization field, and compares the decrypted user name and password with the user database for verification. If the user name and password are correct, the server sends the requested resources to the client according to the request

Effect: When the client is not authenticated, the user name and password input box is displayed. In this case, the request is in the pending state. When the user enters the user name and password, the client sends another request with Authentication header.

Successful certification:

server.js

let express = require("express");
let app = express();
 
  app.use(express.static(__dirname+'/public'));
 
  app.get("/Authentication_base".function(req,res){
    console.log('req.headers.authorization:',req.headers)
    if(! req.headers.authorization){ res.set({'WWW-Authenticate':'Basic realm="wang"'
      });
      res.status(401).end();
    }else{
      let base64 = req.headers.authorization.split("") [1];
      let userPass = new Buffer(base64, 'base64').toString().split(":");
      let user = userPass[0];
      let pass = userPass[1];
      if(user=="wang"&&pass="wang"){
        res.end("OK");
      }else{
        res.status(401).end();
      }
 
    }
 
  })
 
  app.listen(9090)
Copy the code

index.html


      
<html>
  <head>
    <meta charset="UTF-8">
    <title>HTTP Basic Authentication</title>
  </head>
  <body>
    <div></div>
    <script src="Js/jquery - 3.2.1. Js"></script>
    <script>
      $(function(){
       send('./Authentication_base');
      })
      var send = function(url){
            $.ajax({ 
            url : url, 
            method : 'GET'}); }</script>
  </body>
</html>
Copy the code

Advantages: One advantage of basic authentication is that almost all popular Web browsers support basic authentication. Basic authentication is rarely used on publicly accessible Internet sites and is sometimes used in small private systems (such as the router Web page management interface). A later mechanism, HTTP digest authentication, was developed as an alternative to basic authentication, allowing keys to be transferred in a relatively secure manner over insecure channels. Programmers and system administrators sometimes use basic authentication in trusted network environments to manually test Web servers using Telnet or other plaintext network protocol tools. It is a cumbersome process, but the content transmitted over the network is human-readable for diagnosis.

Disadvantages: Although basic authentication is very easy to implement, the scheme is based on the assumption that the connection between client and server hosts is secure and trusted. In particular, keys and passwords transmitted in clear text can be easily intercepted if transport-layer security protocols such as SSL/TLS are not used. The scheme also provides no protection for the information returned by the server. Existing browsers store authentication information until the TAB or browser is closed, or the user clears history. HTTP does not provide a way for the server to instruct the client to discard these cached keys. This means that there is no effective way for the server to log out without the user closing the browser.

Three, the session cookie

3.1 the cookie

Http is a stateless protocol, and the server does not know which browser is accessing it, so an identifier is needed to let the server distinguish between different browsers. The cookie is the identity that manages the state between the server and the client.

The principle of cookie is that when the browser sends a request to the server for the first time, the server sets the set-cookie field in the response header. Upon receiving the response, the browser sets the cookie and stores it. Next time the browser sends a request to the server, The Cookie field is automatically placed in the header of the request, and the server receives the Cookie to distinguish between different browsers. Of course, the relationship between the cookie and a user should exist on the server at first access, which is where the session is needed.

const http = require('http')
http.createServer((req, res) = > {
  if (req.url === '/favicon.ico') {
    return
  } else {
    res.setHeader('Set-Cookie'.'name=zhunny')
    res.end('Hello Cookie')
  }
}).listen(3000) 
Copy the code

3.2 the session

Session is a session. The first time a browser accesses the server, the server creates a session in which information identifying the browser is stored. The difference between it and cookie is that session is cached on the server, while cookie is cached on the client. They are generated by the server to compensate for the stateless defect of Http protocol.

3.3 the session cookie authentication. –

  1. The server creates seesion on the server side when it receives the first access from the client, and then saves seesion(we can keep seesion in memory or in Redis, the latter is recommended), It then generates a unique identifier string for the session and plants this unique identifier string in the response header.
  2. The signature. This step uses the secret key to sign the SID, preventing the client from changing the SID. (Optional steps)
  3. When the browser receives a response, it parses the response header and saves the SID in a local cookie. The browser carries the cookie information of the domain name in the header of the next HTTP request.
  4. When the server receives the request from the client, it will resolve the SID in the cookie of the request header, and then find the session of the client saved by the server according to the SID, and determine whether the request is valid.

const http = require('http')
// The session is stored in memory
const session = {}
http.createServer((req, res) = > {
  const sessionKey = 'sid'
  if (req.url === '/favicon.ico') {
    return
  } else {
    const cookie = req.headers.cookie
    // Access again to authenticate the SID request
    if (cookie && cookie.indexOf(sessionKey) > - 1) {
      res.end('Come Back')}// First access, generate sid, save on the server side
    else {
      const sid = (Math.random() * 9999999).toFixed()
      res.setHeader('Set-Cookie'.`${sessionKey}=${sid}`)
      session[sid] = { name: 'zhunny' }
      res.end('Hello Cookie')
    }
  }
}).listen(3000)
Copy the code

3.4 redis

Redis is a key-value server that can hold session key-value pairs. How to use session in KOA:

const koa = require('koa')
const app = new koa()
const session = require('koa-session')

const redisStore = require('koa-redis')
const redis = require('redis')
const redisClient = redis.createClient(6379.'localhost')

const wrapper = require('co-redis')
const client = wrapper(redisClient)

/ / encryption sessionid
app.keys = ['session secret']

const SESS_CONFIG = {
  key: 'kbb:sess'.// Let session be stored in redis
  store: redisStore({ client })
}

app.use(session(SESS_CONFIG, app))

app.use(ctx= > {
  // View the contents in redis
  redisClient.keys(The '*', (errr, keys) => {
    console.log('keys:', keys)
    keys.forEach(key= > {
      redisClient.get(key, (err, val) => {
        console.log(val)
      })
    })
  })
  if (ctx.path === '/favicon.ico') return
  let n = ctx.session.count || 0
  ctx.session.count = ++n
  ctx.body = The first `${n}Visit `
})

app.listen(3000)
Copy the code

3.5 User Login Authentication

When session-cookie is used for login authentication, a session is stored during login and deleted when you log out. For other interfaces that need to be operated after login, you need to verify whether a session exists in advance. If a session exists, the login page can be redirected, and if not, the login page can be returned.

Do a validation middleware in KOA and use it in the interface that needs validation.

// Front-end code
async login() {
    await axios.post('/login', {
        username: this.username,
        password: this.password
    })
},
async logout() {
    await axios.post('/logout')},async getUser() {
    await axios.get('/getUser')}Copy the code
// Middleware auth.js
module.exports = async (ctx, next) => {
  if(! ctx.session.userinfo) { ctx.body = {ok: 0.message: "User not logged in" };
  } else {
    awaitnext(); }};// Interface to validate
router.get('/getUser'.require('auth'), async (ctx) => {
  ctx.body = {
    message: "Data obtained successfully".userinfo: ctx.session.userinfo
  }
})
/ / login
router.post('/login'.async (ctx) => {
  const {
    body
  } = ctx.request
  console.log('body', body)
  / / set the session
  ctx.session.userinfo = body.username;
  ctx.body = {
    message: "Login successful"}})/ / logout
router.post('/logout'.async (ctx) => {
  / / set the session
  delete ctx.session.userinfo
  ctx.body = {
    message: "Logout system"}})Copy the code

Fourth, the Token

Token is a token, the browser when they access the server for the first time issued a token, every time after the browser to carry this token to access the server will verify the token is valid, as long as the server can decrypt the token, means that the request is legitimate, contained in the token of the identity of the user information can also distinguish between different users. Common tokens consist of user information, time stamps, and signatures encrypted by the Hash algorithm.

4.1 Token Authentication Process

  1. The client requests login using the username and password
  2. The server receives a request to verify the user name and password
  3. After the authentication succeeds, the server issues oneTokenAnd thisTokenSend to the client
  4. The client receivesTokenYou can store it later, like inCookieOr in theLocal Storage
  5. Each time a client requests a resource from the server, it must carry the resource signed by the serverToken
  6. The server receives the request and then validates the client with the requestToken(Add Authorization to the request header). If the authentication succeeds, the requested data is returned to the client. If the authentication fails, the 401 error code is returned, and the authentication fails.

4.2 Differences between Token and Session

The disadvantages of session-cookie are as follows: (1) The authentication mode is limited to the browser. Cookie is a browser-side mechanism, and it cannot be used in the APP side. (2) In order to meet the global consistency, we had better store the session in Redis for persistence, while in the distributed environment, we may need to back up on each server, occupying a large amount of storage space. (3) Cookies that are not Https protocols are vulnerable to CSRF cross-site request forgery attacks.

Disadvantages of token: (1) Encryption and decryption consumption makes token authentication consume more performance than session-cookie. (2) Token is larger than sessionId and occupies more bandwidth.

(1) Token authentication is not limited to cookies, which enables the authentication mode to support multiple clients, not just browsers. It is not affected by the same origin policy. (2) CSRF attacks can be avoided without using cookies. (3) The token does not need to be stored. The token already contains user information, and the server becomes stateless. The server only needs to verify whether the token is legitimate according to the defined rules. This also makes token more scalable.

4.3 JWT (JSON Web Token)

There are many token-based solutions, and the most common one is JWT. JWT is based on the principle that the server generates a JSON object after authentication. The JSON object cannot be passed to the user naked, so anyone can tamper with the object to send a request. So the JSON object is signed and encrypted by the server and returned to the user with a token that the user carries with him every time he visits the server.

The JSON object might contain information about the user, the user’s identity, and the expiration date of the token.

4.3.1 Components of JWT

At the site JWT, a JWT can be decoded or encoded. A JWT looks like this:

It consists of three parts: Header, Payload, and Signature.

  1. The Header section is a JSON object that describes the metadata of the JWT. General information includes the encryption algorithm and Token type. {” ALG “: “HS256″,” TYP “: “JWT”} means that the token is encrypted with HS256 and the token type is JWT. This section is basically plaintext, and it does a Base64 transcoding of the JSON object into a string. Base64 encoding and decoding is algorithmic and the decoding process is reversible. The header information carries two fields by default.
  2. The Payload part is also a JSON object that stores the data that needs to be transmitted. There are seven official fields, and you can also define private fields in this section. It generally holds the user name, user identity, and some JWT description fields. It also does only Base64 encoding, so it certainly can’t store secret information, such as login passwords, in it.
  3. If the first two parts of the information are modified and sent to the server, the server can use the Signature to verify the correctness of the information. The signature requires a key, which is stored on the server and unknown to the user. After calculating the Signature, add the Header, Payload, and Signature parts into a string, and use dots (.) between each part. Delimit, and it can be returned to the user.
4.3.2 Characteristics of JWT
  1. JWT is not encrypted by default, but it can be encrypted. Once the original Token is generated, it can be encrypted again with the key.
  2. Secret data cannot be written to the JWT without encryption.
  3. JWT can be used not only for authentication, but also for information exchange. Using JWT effectively can reduce the number of times the server queries the database.
  4. The biggest drawback of JWT is that since the server does not store session state, there is no way to invalidate a token or change the token’s permissions during use. That is, once a JWT is issued, it remains valid until expiration, unless the server deploys additional logic.
  5. The JWT itself contains authentication information, and if it is disclosed, anyone can gain full access to the token. To reduce theft, JWT expiration dates should be shorter. For some important permissions, the user should be authenticated again.
  6. To reduce theft, JWT should use HTTPS instead of HTTP.
4.3.3 JWT Authenticates user login
// Front-end code
// The AXIos request interceptor adds JWT authentication information to the header of each request
axios.interceptors.request.use(
    config= > {
        const token = window.localStorage.getItem("token");
        if (token) {
        // Determine whether a token exists, and if so, add a token to each HTTP header
        // Bearer is a confirmed header of JWT
            config.headers.common["Authorization"] = "Bearer " + token;
        }
        return config;
    },
    err => {
        return Promise.reject(err); });// How to log in: store the JWT returned from the backend to localStorage
async login() {
    const res = await axios.post("/login-token", {
        username: this.username,
        password: this.password
    });
    localStorage.setItem("token", res.data.token);
},
// Logout method: delete JWT
async logout() {
    localStorage.removeItem("token");
},
async getUser() {
    await axios.get("/getUser-token");
}
Copy the code
// Back-end code
const jwt = require("jsonwebtoken");
const jwtAuth = require("koa-jwt");
// The key used to sign
const secret = "it's a secret";

router.post("/login-token".async ctx => {
  const { body } = ctx.request;
  If the user and password are valid, a JWT token will be generated and passed to the user
  const userinfo = body.username;
  ctx.body = {
    message: "Login successful".user: userinfo,
    // Generate a token and return it to the client
    token: jwt.sign(
      {
        data: userinfo,
        // Set the token expiration time in seconds after one hour
        exp: Math.floor(Date.now() / 1000) + 60 * 60
      },
      secret
    )
  };
});

// The jwtAuth middleware takes the key and resolves whether the JWT is valid.
// The payload in JWT is set to state. Ctx. state is used for middleware transmission.
router.get(
  "/getUser-token",
  jwtAuth({
    secret
  }),
  async ctx => {
    // The authentication passed, state.user
    console.log(ctx.state.user);
    ctx.body = {
      message: "Data obtained successfully".userinfo: ctx.state.user.data }; })// The token does not need to be stored. As long as the server can parse the user's information with the key, the user is valid.
// To further verify permissions, determine whether the resolved user is an administrator or a common user.
Copy the code

V. OAuth(Open License)

OAuth (Open Authorization) is an Open standard that allows users to authorize third-party websites to access their information stored on other service providers without providing user names and passwords to third-party websites or sharing all content of their data. In order to protect user data security and privacy, Third-party websites need to explicitly ask users for authorization before accessing user data. We commonly provide OAuth certification service manufacturers are Alipay, QQ, wechat.

The OAuth protocol is also available in versions 1.0 and 2.0. Compared with version 1.0, version 2.0 is the most important authentication and authorization method for users.

For OAuth 2.0 related articles, you can see a brief explanation of OAuth 2.0, four ways to understand OAuth 2.0

5.1 OAuth Certification Process

OAuth is an authorization mechanism. The owner of the data tells the system that it agrees to authorize third-party applications to access the data. The system then generates a short-term access token (token), which can be used in place of a password for third-party applications.

OAuth has four ways to obtain a token. Regardless of the authorization method, the third-party application must first go to the system to record its identity before applying for a token, and then it will get two identification codes: client ID and client secret. This is to prevent misuse of tokens, and third-party applications that have not been registered will not get tokens.

In the context of the separation of the front and back ends, we often use the authorization code approach, where a third-party application first applies for an authorization code and then uses that code to obtain a token.

5.2 GitHub Third-party Login Example

We use examples to clarify the flow of authorization codes.

  1. Register a third-party application on GitHub and get its client ID and key.

Create an OAuth App in github-settings-developer Settings. Fill in the relevant information. Github will give you a client ID and key.

  1. At this point, you can provide a Github login link on your third-party website, and users will jump to Github after clicking this link. This step takes the client ID and asks Github for the authorization code.
const config = {
  client_id: '28926186082164bbea8f'.client_secret: '07c4fdae1d5ca458dae3345b6d77a0add5a785ca'
}

router.get('/github/login'.async (ctx) => {
  var dataStr = (new Date()).valueOf();
  // Redirect to the authentication interface and set parameters
  var path = "https://github.com/login/oauth/authorize";
  path += '? client_id=' + config.client_id;

  // Forward to the authorization server
  ctx.redirect(path);
})
Copy the code
  1. If a user goes to Github and enters the Github user name and password, the user agrees to log in to a third-party website using Github identity. At this point, it hops back to the third-party site with an authorization code. The hop back address was set when the OAuth was created. http://localhost:3000/github/callback
  2. When a third-party website receives an authorization code, it can request an Access_token from Github with the authorization code, client ID, and client key.
  3. Github received a request to issue a token to a third-party site.
  4. After receiving the token, the third party website can temporarily have the permission of some requests from Github. For example, after receiving the user information, the third party website can construct its own token and perform relevant authentication operations.
router.get('/github/callback'.async (ctx) => {
  console.log('callback.. ')
  const code = ctx.query.code;
  const params = {
    client_id: config.client_id,
    client_secret: config.client_secret,
    code: code
  }
  let res = await axios.post('https://github.com/login/oauth/access_token', params)
  const access_token = querystring.parse(res.data).access_token
  res = await axios.get('https://api.github.com/user?access_token=' + access_token)
  console.log('userAccess:', res.data)
  ctx.body = `
        <h1>Hello ${res.data.login}</h1>
        <img src="${res.data.avatar_url}" alt=""/>
    `

})
Copy the code

OAuth authorization login flowchart:

Sixth, the original

www.lishuaishuai.com/nodejs/1167…