Recently, I had to use RESTful apis for IOS and Android application development in my new project, so I accidentally stepped into the pit of REST. There were a lot of deliveries during development. This time we will look at the idea and practice of token in API design.

Technology stack

Use Express framework on Node.js for our routing design, Mongoose to interact with Mongodb database connections, and Postman to debug our designed Api. Get started!

About the RESTful API

There are a lot of descriptions of RESTful on the web, and I won’t repeat them here. What I want to say is that its main function is that for today’s network applications, there are two parts: front-end and back-end. However, the current development trend is the expansion of application platform demand (IOS, Android, Webapp, etc.).

Therefore, a unified mechanism is required to facilitate the communication between front-end devices and back-end devices of different application platforms, that is, the separation of front-end and back-end devices. This led to the popularity of API architectures and even the idea of “API First” design. RESTful API is a relatively mature set of Internet application API design theory.

The idea of token in API design

In API design,TOKEN is used to determine whether a user has permission to access the API.TOKEN first does not require codec processing. Generally, the TOKEN is the MD5 irreversible encryption of some user names and time. Then a USER_TOKEN table is used to determine whether the tokens contained in the user request are the same as those in the USER_TOKEN table.

The concrete practice process is mainly as follows:

  1. Set a key such as key = ‘2323DSfadFEWRASa3434’.

  2. This key is known only to the sender and receiver.

  3. When called, the sender combines the parameters with the key and generates an access_key according to certain rules (various sorts, MD5, IP, etc.). Post together to the API.

  4. The receiver gets the argument from the POST and the access_key. Using the same rules (various sorts, MD5, IP, etc.) for each parameter with the key as for sending also generates an access_key2.

  5. Compare access_key with access_key2. The same. If the operation is not the same, return with an error or join the blacklist.

Token design concrete practice

This time, node.js + Experss with Mongoose will be selected to enter the TOKEN practice of REST

Project address: GitHub address

Or git clone https://github.com/Nicksapp/nAuth-restful-api.git

The overall architecture

The idea of our design is carried out before development

  • Routing design

    • POST/API/signUP: user registration

    • POST/API /user/accesstoken: authenticates the account and obtains the token

    • GET/API /user/user_info: obtain user information, which needs to be verified

  • User model design

    • Name: indicates the user name

    • Password: password

    • Token: Verifies the related token

New project

Take a look at our project folder



- routes/
---- index.js
---- users.js
- models/
---- user.js
- config.js
- package.json
- passport.js
- index.jsCopy the code

NPM init creates our package.json

Next, install the dependencies we need in the project root folder



npm install express body-parser morgan mongoose jsonwebtoken bcrypt passport passport-http-bearer --save 
Copy the code
  • Express: Our main development framework

  • Mongoose: framework used to interact with MongoDB database. Please install MongoDB on PC in advance

  • Morgan: It will display information about the application’s request process in Terminal so that we can debug the code

  • Jsonwebtoken: Used to generate our tokens

  • Passport: Very popular permission validation library

  • Bcrypt: hashes the user password

— Save will write our installed library files to package.json dependencies so that others can open the project to install the dependencies they need correctly.

The user model

Define the user model we need for Moogoose and create new models/user.js



const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt');

const UserSchema = new Schema({
  name: {
    type: String,
    unique: true.// Non-repeatable constraints
    require: true // Cannot be null constraint
  },
  password: {
    type: String.require: true
  },
  token: {
    type: String}});The middleware encrypts the password bcrypt when the user is saved, so that only the user knows the user password
UserSchema.pre('save'.function (next) {
    var user = this;
    if (this.isModified('password') | |this.isNew) {
        bcrypt.genSalt(10.function (err, salt) {
            if (err) {
                return next(err);
            }
            bcrypt.hash(user.password, salt, function (err, hash) {
                if (err) {
                    return next(err);
                }
                user.password = hash;
                next();
            });
        });
    } else {
        returnnext(); }});// Verify that the entered password is correct
UserSchema.methods.comparePassword = function(passw, cb) {
    bcrypt.compare(passw, this.password, (err, isMatch) => {
        if (err) {
            return cb(err);
        }
        cb(null, isMatch);
    });
};

module.exports = mongoose.model('User', UserSchema);
Copy the code

The configuration file

./config.js is used to configure the key for our MongoDB database connection and token.



module.exports = {
  'secret': 'learnRestApiwithNickjs'.// used when we create and verify JSON Web Tokens
  'database': 'mongodb://localhost:27017/test' // Enter the local mongodb connection address, XXX is the name of the data table
};
Copy the code

Local Server Configuration

./index.js server configuration file, which is also the entry point of the program.

Here we mainly used to contain our program needs to load the library file, call the initialization program needed to rely on.



const express = require('express');
const app = express();
const bodyParser = require('body-parser');// Parse the body field module
const morgan = require('morgan'); // Command line log display
const mongoose = require('mongoose');
const passport = require('passport');// User authentication module passport
const Strategy = require('passport-http-bearer').Strategy;// Token authentication module
const routes = require('./routes');
const config = require('./config');

let port = process.env.PORT || 8080;

app.use(passport.initialize());// Initialize the passport module
app.use(morgan('dev'));// The program run log is displayed in the command line for bug debugging
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); // Call the bodyParser module so that the program correctly parses the incoming body value

routes(app); // Route import

mongoose.Promise = global.Promise;
mongoose.connect(config.database); // Connect to the database

app.listen(port, () => {
  console.log('listening on port : ' + port);
})
Copy the code

The routing configuration

./routes Stores routing files

./routes/index.js Indicates the route entry



module.exports = (app) => {
  app.get('/', (req, res) => {
    res.json({ message: 'hello index! '});
  });

  app.use('/api'.require('./users')); // Add/API before all Users routes
};Copy the code

./routes/users.js




const express = require('express');
const User = require('.. /models/user');
const jwt = require('jsonwebtoken');
const config = require('.. /config');
const passport = require('passport');
const router = express.Router();

require('.. /passport')(passport);

// Register an account
router.post('/signup', (req, res) => {
  if(! req.body.name || ! req.body.password) { res.json({success:false, message: 'Please enter your account password.'});
  } else {
    var newUser = new User({
      name: req.body.name,
      password: req.body.password
    });
    // Save the user account
    newUser.save((err) => {
      if (err) {
        return res.json({success: false, message: 'Registration failed! '});
      }
      res.json({success: true, message: 'New user created successfully! '}); }); }});// Check username and password and generate an Accesstoken if validated
router.post('/user/accesstoken', (req, res) => {
  User.findOne({
    name: req.body.name
  }, (err, user) => {
    if (err) {
      throw err;
    }
    if(! user) { res.json({success:false, message:'Authentication failed, user does not exist! '});
    } else if(user) {
      // Check that the password is correct
      user.comparePassword(req.body.password, (err, isMatch) => {
        if(isMatch && ! err) {var token = jwt.sign({name: user.name}, config.secret,{
            expiresIn: 10080
          });
          user.token = token;
          user.save(function(err){
            if(err) { res.send(err); }}); res.json({ success:true,
            message: 'Verification successful! ',
            token: 'Bearer ' + token,
            name: user.name
          });
        } else {
          res.send({success: false, message: 'Authentication failed, password error! '}); }}); }}); });// Passport - HTTP-bearer token middleware validation
// Send Authorization -> Bearer + token via header
// Or pass? access_token = token
router.get('/users/info',
  passport.authenticate('bearer', { session: false }),
  function(req, res) {
    res.json({username: req.user.name});
});

module.exports = router;
Copy the code

Passport configuration

./passport. Js configures the functions required by the permission module



const passport = require('passport');
const Strategy = require('passport-http-bearer').Strategy;

const User = require('./models/user');
const config = require('./config');

module.exports = function(passport) {
    passport.use(new Strategy(
        function(token, done) {
            User.findOne({
                token: token
            }, function(err, user) {
                if (err) {
                    return done(err);
                }
                if(! user) {return done(null.false);
                }
                return done(null, user); }); })); };Copy the code

This section describes how to verify whether the sent token value matches the token value on the user server.

Specific debugging

Now you can run our code and see how it works! For easy debugging and parameter sending and receiving, we use Postman (which can be installed on Chrome or Mac).

Node Index run our local server and visit [localhost:8080/]() and you should see the initial JSON value we returned, but let’s test further.

POST access localhost: 8080 / API/signup (), we have to register a new user, Remember to set the body content-type to X-www-form-urlencoded so that our body-Parser would parse correctly. Ok, we successfully simulated creating our new user.

Connect to the database to see if our user information is also stored correctly (note: I’m using MongoChef, a very powerful MongoDB database management software), and we can see that my password is encrypted and saved correctly.

Then POST access [localhost: 8080 / API/user/accesstoken] (), for my users have exclusive token, POST process related to the registration, you can see our token value also correctly generated.

Looking at the user information in our database, the token value is also stored in the database, which is convenient for us to verify the permission later.

GET access [localhost: 8080 / API/users/info] (), at the same time we token values in the Header to the Authorization: token incoming, right to obtain a user name indicates we passed the verification access request.

If the token value is incorrect, revert to Unauthorized and deny the access request. To here our permission verification function is basically realized (xi Big pu Ben ~~~).

conclusion

After reading this tutorial, I hope you can get some inspiration on RESTful Api development. If you have any shortcomings in the process, you are welcome to point out.


Did this article help you? Welcome to join the front End learning Group wechat group: