Before the separation of the front and back end, the page is rendered through the background, can access to the page directly by the background logic. After the front and back ends are separated, the elements of the page are controlled by the page itself, so the routing between pages is controlled by the front end. Of course, only the front end to do permission control is far from enough, the background also needs to do verification for each interface. Why is front-end access control not enough? Because the front-end routing control is only visual, the front-end can hide a page or a button, but there are still many ways to send a request, you can skip the action page to send a request. So even if the front-end permission control is very tight, the background still needs to verify each interface. There are three main types of front-end permission control: route control (jump to route), view control (button level), and request control (request interceptor). These methods will be discussed in detail later. After the front-end permission control, the background still needs to verify each interface, which is authentication. Currently, the front and back ends can cooperate with the following authentication modes:

  1. session-cookie
  2. Token validation (JWT)
  3. OAuth(Open License)

session-cookie

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

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.

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 according to this SID, it will find the session of the client saved by the server, and then 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

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

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

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.

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 a Token and sends the Token to the client
  4. After receiving the Token, the client can store it, for example, in a Cookie or Local Storage
  5. Each time a client requests resources from the server, it must carry a Token signed by the server
  6. After receiving the request, the server authenticates the Token (add Authorization in the request header) carried in the request from the client. If the authentication succeeds, the server returns the requested data to the client. If the authentication fails, the server returns the 401 error code, and the authentication fails.

The difference between Token and session

  1. Disadvantages of session-cookie :(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.
  2. 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.
  3. 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.

JWT – JSON Web Token

The JWT principle is that after the server authenticates, it generates a JSON object, which must not 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.

Component of JWT

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


  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"}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.
The 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.
JWT verifies 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

OAuth

Three-party login is mainly based on OAuth 2.0. OAuth protocol provides a secure, open and simple standard for user resource authorization. Different from previous authorization methods, OAuth authorization does not allow the third party to touch the user’s account information (such as user name and password), that is, the third party does not need to use the user’s user name and password to apply for the authorization of the user resources, so OAuth is safe. We commonly provide OAuth certification service manufacturers have Alipay, QQ, wechat. In this way, users have a low threshold to use and can better promote their applications. A series of articles by Professor Ruan Yifeng, OAuth 2.0.

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.

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.


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

reference

Cookie, session? OAuth 2.0 front-end authentication about front-end permissions four ways