More documentation

preface

I haven’t written blog for a month, because RECENTLY I have been busy with the research of project and low code platform. The low code server is implemented with Node. The first problem I encountered is the problem of login verification

session

Session, first introduce simple HTTP requests are stateless, usually don’t do any configuration, the client and the server is not know each other is communication, like online chatting only know each other’s net client, server, but the bad guys a lot, online chatting is very dangerous, I will expose some basic information for you, But if you want to know more, you have to let me know who you are, and the purpose of a session is a way for the client to gain trust from the server

Next, take Express as an example to briefly introduce the session validation process

Set up the session

// Use express-session middleware to set the session
const session=require("express-session");

app.use(session({
    secret: "secret"./ / the secret key
    cookie: { maxAge: 8 * 1000 }, Cookie, default {path: '/', httpOnly: true, secure: false, maxAge: null}
    resave: false.// Force the session to be saved, even if the session has not been changed
    saveUninitialized: false // Force initialization
}))

Copy the code

Logging In to the Storage Device

// Store user information to session on the first login
router.get('/login'.(req, res, next) = > {
  req.session.user = 'user'
  res.json({
    code: 200.message: 'Login successful'})})Copy the code

Interface verification after login

// Add validation for the interface that needs to be invoked after login
router.get('/list'.(req, res, next) = > {
  const { session: { user } } = req
  res.json({
    code: user ? 200 : 403.message: user ? 'success' : 'Please log in again'})})// Usually the middleware will be used for unified processing
app.use((req, res, next) = > {
  const { url, session: { user } } = req;
  if(url.indexOf('/login') > -1) {
    next()
  } else {
    if(user) {
      next()
    } else {
      res.json({
        code: 403.message: 'Please log in again'}}}})Copy the code

Update the session

// In order to optimize the experience, usually active users will actively update the session expiration time. Combined with the above middleware, the process is as follows
app.use((req, res, next) = > {
  const { url, session: { user } } = req;
  if(url.indexOf('/login') > -1) {
    next()
  } else {
    if(user) {
      req.session._garbage = Date(a); req.session.touch(); next() }else {
      res.json({
        code: 403.message: 'Please log in again'}}}})Copy the code

Delete the session

// Log out and clear the session
router.get('/loginOut'.(req, res, next) = > {
  req.session.destroy();
  res.json({
    code: 200.message: 'Exit successfully'})})Copy the code

The general process is like this, usually session with the database to do some persistence scheme, here do not do in-depth, simple summary:

  • sessionThe update is more flexible and there are many more mature frameworks
  • Both the front and back ends need to store information. When the number of users is large, the cost of the server is relatively large
  • cookie + sessionThe server needs to be set due to cross-domain problemsAccess-Control-Allow-OriginAnd the front end needs to be setwithCredentials
  • sessionInformation stored on the current server, distributed to dosessionSharing mechanisms (just know)
  • cookieInformation has CSRF security issues

token

Unlike session, token does not need to be stored on the server. It is more like a password during curfew. If the password is correct, it can pass, otherwise, it is not allowed to pass

Login to generate a token

// The token can be encrypted according to certain rules
// JsonWebToken is used to generate a token. The use of jsonWebToken is not described in detail here
// Return the token to the front end
const sign = (jtJson = {}, options = {}) = > {
  const token = jwt.sign({ iss: 'lesscode'. jtJson},"secret", { expiresIn: 60 * 60 * 24. options})return token
}

router.get('/login'.(req, res, next) = > {
  const { body: { user, pass } } = req;
  const token = sign({ user })
  res.json({
    code: 200.data: { token, user },
    message: 'Login successful'})})Copy the code

The front-end gets the token

// The front-end obtains the token from the login interface and stores it to the localStorage
// Subsequent interface requests carry tokens
const request = async (
  url: string.options: optionsTypes
): Promise<dataTypes> => {
  const userInfo: string | null = localStorage.getItem("userInfo");
  const headers: headersTypes = {};
  if (userInfo) {
    // Add the token to the request header if the token exists
    const { token } = JSON.parse(userInfo) ?? {};
    headers.token = token;
  }
  const res: dataTypes = awaitselfFetch(baseUrl + url, { ... options, headers, });const { code, message } = res ?? {};

  if (code === 403) {
    store.commit("set_loginVisible".true);
    store.commit("set_userInfo"{});localStorage.clear();
  } else if(code ! = =200) {
    ElMessage.error(message);
  }
  return res;
};
Copy the code

The server verifies the token

// Just like session, add the token verification middleware to authenticate the necessary interfaces
const verResult = (req, res, next) = > {
  const { url, headers: { token } } = req
  // Login, universal interface skip verification
  if(url.indexOf('/user/') > -1 || url.indexOf('/common/') > -1) {
    next()
  } else {
    if(! token) {// The token does not exist
      setJson(res, 403."Please log in".null)}else {
      const { iat, exp, message = ' ' } = jwt.verify(token, 'secret'.(error, decoded) = > {
        if(error) {
          if(error.name === 'TokenExpiredError') {
            return {
              iat: 1.exp: 0.message: "Token expired"}}else if (error.name === 'JsonWebTokenError') {
            return {
              iat: 1.exp: 0.message: "The token is invalid"}}}else {
          return decoded
        }
      })
      // Log in again after the token expires or is invalid
      if(iat < exp) {
        next()
      } else {
        setJson(res, 403, message, null)
      }
    }
  }
}
app.use(verResult)
Copy the code

Update the token

Here are some questions:

  1. tokenAs an indicator of permission verification, the shorter the time, the better
  2. tokenThe expiration time cannot be changed once generated

However, the actual problem is that frequent login will inevitably discourage users from using the interface. Therefore, active users must do some insensitive update operations. The initial idea of course is to return a new token when the user calls the interface once, just like session. However, it causes a bigger security problem, assuming that the token is about to expire in a minute, and the interface is called 10,000 times in another minute, there will be 10,000 new tokens, which means there are 10,000 keys to open the door of the server. Therefore, this scheme is not feasible. Here is an optimization solution

refresh_token

Two tokens are generated at the user’s initial login: Access_token expires in 5 minutes. Refresh_token expires in 7 days. The interface uses the access_token for verification. If the access_token expires, refresh_token is used to obtain a new access_token, and new access_token is used later. However, if refresh_token also expires, the user must log in again

To summarize:

  • Stateless, consistent with resetful API principles
  • New extension, more distributed friendly
  • Once, oncetokenThe issue is not changeable. If you need to update and renew, you need additional logic and you need to implement it yourself
  • Do not store sensitive data because the data is large

conclusion

Req.session. _garbage method req.session._garbage method req.session._garbage method req.session._garbage method req.session._garbage method req.session._garbage method req