Soaring_tiger
http://blog.csdn.net/Soaring_Tiger/article/details/51418209
This article will cover the following (read this article requires a certain express, mongoose basis):
- Add user authentication to mean full stack development
- Manage user authentication with the Passport module in Express
- Generating a JSON Web Tokens (JWT) in exrpess
- Implement user Registration and login
- Use local storage to manage user session in angular
1.1 How to implement user authentication in mean development?
For a single-page program like angular (SPA), user authentication seems like a hassle, because all the (front-end) programs are sent to the browser, so how to hide what you want to hide is a problem.
1.1.1 Let's take a look at how traditional service-based programs are implemented.
If you are more familiar with traditional server-based web development (such as PHP, the best in the world), you may be confused about how a single page application (SPA) can implement user authentication.
The traditional user authentication process for Web development based on the service side is typically this:
(1) The user enters the user name and password on the form and submits it to the server;
(2) The program on the server verifies the user name and password and the correct permissions through the database;
(3) If the verification is successful, the server marks the user's session and tells the user that he has logged in successfully;
(4) When the user browses the page, the browser will send the cookie to the server, the server will verify the user's session and browse permissions, and return the page to the user.
So, like mean full stack development mode and how to do it?
1.1.2 mean full stack user authentication implementation method
Mean full stack user authentication is faced with two problems:
- The API implemented via Express is stateless, that is, the concept of no user session.
- The programming logic of the single-page program (SPA) has been transferred to the browser, so you cannot restrict these already in the browser-side code.
The logical solution is to maintain a certain user session state on the browser side, so that the front-end program determines what can be displayed to the user, what cannot be displayed to the user, which differs from the way the server controls it, but this is the main change.
A good and safe approach is to use a JSON Web Token (JWT) to store user data on the client. For the details of JWT we'll talk about it later, and now you just have to know that it's an encrypted JSON object.
Manage Login Processes
Figure 5
Figure 5 illustrates a login process: (1) The user submits their authentication information to the server via the API, (2) The server verifies the user's identity information through the database, (3) The server returns a token to the client, (4) The client token is saved and is used the next time it is needed.
In fact, the entire process is similar to the traditional server implementation scheme, but the user session exists on the client.
rely on user authentication information to show content
Figure 6
6, during the user session, when the user wants to see the new page, the front-end program can determine whether the user has permission to browse based on the information that is kept in the JWT.
Unlike traditional implementations, unless the user needs to get the information from the database through the API, the mean server does not care what the user is looking at.
Secure Call API
If some parts of the application are limited to a particular user, then for a stateless API, every call to the API needs to know the invocation permissions, and this is where the JWT comes in handy. As shown in 7, the client sends a JWT when invoking an API endpoint that requires authentication, and the server authenticates the user's request by decoding the JWT.
Figure 7
OK, the above section introduces the basic concepts, we already know what to do in general. Let's step through the process.
2. Creating a user Data model for MongoDB (users Schema)
Usernames and passwords are usually stored in the database, and we need to build a model through mongoose in mean full stack development. The special need to remind is: password in the database must not be saved in clear text! , because it creates a huge security hole.
2.1 One-way password encryption: hash + salt
There is one way to improve the security of your password: one-way encryption of passwords. One-way encryption prevents anyone from decrypting, and it's very easy to verify the password. When the user logs in, the program can encrypt the password and compare the values that are already stored.
Of course, if just simple encryption is not enough, because if a lot of people use the same password (for example: 123456), then the encrypted string will be identical, and hackers can easily find the weak password encryption mode.
This time you need to rely on "salt" to help, so-called "salt" is a user password is encrypted for each user randomly generated string, and the password mixed with the result is "hash", 8 is shown.
Figure 8
"Salt" and "hash" are stored together in the database, not just a "password" field, through the above process, all the "hash" is unique, so it is good to protect the password.
2.2 Creating a Mongoose Model
The Userschema we created contains
Several fields, such as user name, e-mail (email), hash (hash), salt, and so on. Where email is required and is the only field, name is the necessary field.
var=require‘mongoose‘ );var=new mongoose.Schema({ email: {? typeString, true, true}, name: { typeString, true }, String, String});
2.3 Setting the encryption path using the Mongoose method
Mongoose allows the user to add a custom method to the schema, such as the "SetPassword" method in the following code
var User = mongoose.model(‘User‘); //实例化用户模型varnew User(); //创建新用户"User’s name"; //设置用户名 "[email protected]"; //设置用户邮箱user.setPassword("myPassword"); //调用自定义setPassword方法设置用户密码user.save(); //保存用户
Let's take a look at how to add a method to the Mongoose schema
After the schema is defined, we can add a method to the schema before the data model is compiled. The following code example adds a SetPassword method to Userschema:
function(password){ this.salt = SALT_VALUE; this.hash = HASH_VALUE;};
For JavaScript, "This" actually refers to the model itself in mongoose, which in this case is Userschema.
Before we save the user profile, we must also generate a random "salt" and a "hash" after encrypting the password. Fortunately, node. JS has a native library that Vikings this: crypto.
Using Crypto for encryption
Crypto as the name implies is encryption, it provides a series of methods for processing data encryption; Let's take a look at these two:
- randombytes--generates a sufficiently "robust" string as "salt"
- pbkdf2sync--constructs a "hash" by means of a password (password) and salts (salt); PBKDF2 is the abbreviation for password-based key derivation function 2, which is an industry standard for encryption.
First, we need to introduce the Crypto library at the beginning of the file
varrequire‘mongoose‘ );varrequire(‘crypto‘);
Then, our SetPassword function to update, the resulting "salt" is a 16-bit string, and then use "salt" to encrypt the password "hash":
userSchema.methods.setPassword =function(password){this.salt = crypto.randomBytes(16).toString(‘hex‘thisthis1000,64).toString(‘hex‘);};
Now, the password entered by the user is securely encrypted, the original password is not saved anywhere (including memory), that is, no one can get the original password.
2.4 Verifying the submitted password
After encrypting the user password, another thing to do is to verify the user's password the next time the user logs in, we can write a simple mongoose method to do this:
function(password) {varthis100064).toString(‘hex‘returnthis.hash === hash;};
The code above is a "hash" of the password entered by the user, followed by a comparison with the original "hash". How? The code is easy to implement. ^_^
So the last thing we need to do next is generate the JSON Web Token (JWT).
2.5 Generating the JSON Web Token
The purpose of the so-called JWT (pronounced "jot") is to pass data between the server and our client spa programs. JWT can of course also be used for user authentication between the server and the client.
Let's look at the composition of the JWT:
The three components of a JWT
Let's look at a real JWT example:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI2xtZXMiLCJleHAiOjE0MzUwNDA0MTgsImlhdCI6MTQzNDQzNTYxOH0.GD7UrfnLk295rwvIrCikbk AKctFFoRCHotLYZwZpdlE
You look dizzy, don't you? In fact, if you have good eyesight, you will find that this super long string is actually two "." Divided into three parts, the three parts are:
- Header--A JSON object that is encoded and contains a type and hash algorithm;
- Valid data (Payload)--a JSON object encoded with token information;
- Signature (Signature)--
A "hash" that encrypts the header and payload with a key on the server.
Note: The first two sections are not encrypted--they are simply encoded, which means they are easily decoded--most "modern browsers" have a built-in function called Atob () that decodes the Base64 string.
The third part of the signature is encrypted, in order to decrypt it must use the server's key, and so-called key that is only on the server and can not be made public to the guy.
That sounds a little complicated! Don't worry, the good news is that you can easily fix a JWT with just one library installed.
Generating JWT on the server
The command to install the JWT build library is as follows:
$npminstalljsonwebtoken--save
Then introduce the Jsonwebtoken library into the code.
varrequire‘mongoose‘varrequire(‘crypto‘);varrequire(‘jsonwebtoken‘);
Finally we will add a GENERATEJWT method to the user model. To generate a JWT successfully we need to provide valid data (Payload) and keys. In the valid data we send the user's _id, email, name. We should also set a token expiration time--when the token expires, the user needs to log back in and get a new token, we use the reserved keyword "exp" of the JWT valid data (Payload) to deposit this expiration time. See Code for implementation:
function() { varnewDate(); 7//将过期时间设为7天????return jwt.sign({ this._id, this.email, this.name, parseInt1000‘thisIsSecret‘//"thisIsSecret"是密钥};
Of course, the above code is a bit of a problem: The key is in plaintext in the code inside, we will solve it next.
Save the key as an environment variable
If you want to versioning your code-for example, with GitHub, you should never write your key in your code,-_-# to protect your key, it's a safe way to set it up with environment variables. Setting environment variables is simple, see an. env file below the root of the project, and then write the key inside:
JWT_SECRET=thisIsSecret
Then, make sure that the. env file is not uploaded to GitHub, and you have to write a. gitignore file:
# Dependency directorynode_modules# Environment variables.env
To read the. env file, you also want to install a library: dotenv
$npminstalldotenv--save
Then read the environment variables through DOTENV:
require(‘dotenv‘).load();varrequire(‘express‘);
Finally, let's look at the code after we introduce the environment variable:
function() { varnewDate(); 7); return jwt.sign({ this._id, this.email, this.name, parseInt1000),}, process.env.JWT_SECRET); };
Of course, there are many ways to set environment variables, this article will not repeat. Let's talk about how to use the Passport Library to manage user authentication.
3. Establish user authentication API through Passport
Passport is made by Jared Hanson
The design and development of the node. JS User authentication Library has the advantage of using a variety of authentication strategies, including:
- Facebook
- Twitter
- Oauth
- Local User name and password
This article only briefly describes the authentication policy for local user name passwords.
3.1 Installing and configuring the Passport
The installation commands are as follows:
--save$ npm install passport-local--save
After loading, we can configure the passport.
Create a Passport configuration file
We build a config directory in the project folder, in which we build a passport.js file, at the top of the file, we introduce the library to be used:
varrequire(‘passport‘);varrequire(‘passport-local‘).Strategy;varrequire(‘mongoose‘);var User = mongoose.model(‘User‘);
Configure Local Policies
The underlying framework for configuring code is as follows:
passport.use(new LocalStrategy({}, function(username, password, done) { }));
By default, the local policy of the passport uses "username" and "password" as the field name, in this case we replace the username with an e-mail as the login name, so we need to make some changes. Fortunately, the passport allows us to reload username with the following code:
passport.use(new LocalStrategy( ‘email‘ }, function(username, password, done) { }));
The next main function, mainly rely on mongoose to find the corresponding user name, password, we have to complete the following several things:
- User-provided email to find user profiles;
- Verify that the password is correct;
- If the validation is correct, return the user object;
- If wrong, then error.
Since email is unique, we can use Mongoose's FindOne function to find the user, then we can use the Validpassword function written in the previous section to verify that the user provided the correct password, the code is as follows:
Passport. Use(NewLocalstrategy ({usernamefield:' Email '}, function(username, password, done) {User.findone ({email:username}, function (err, user) { if(ERR) {returnDone (ERR); }??if(!user) {returnDoneNULL,false, {message:' wrong user name or password. '});}if(!user.validpassword (password)) {returnDoneNULL,false, {?? Message' wrong user name or password. '});}returnDoneNULL, user); });} ));
Of course, in the main file app.js also have to add a few lines:
varrequire(‘passport‘);require(‘./app_api/config/passport‘);app.use(passport.initialize());
This way, the passport is installed, configured and initialized successfully! Here's what we're going to do is the user login API endpoint.
3.2 Creating an API endpoint that returns a JSON Web tokens
Mean full stack development user authentication