Preface
Since Angular is a single-page application, most of the resources will be loaded into the browser from the beginning, so you need to pay more attention to the timing of verification and ensure that only users who have passed the verification can see the corresponding interface.
The authentication in this article refers to how to determine whether the user has logged in and ensure that the server's verification needs can be met in every communication with the server. Note that it does not include the judgment on whether there is a specific authority.
For login, it is mainly to accept the user's username and password input , submit it to the server for verification , process the verification response , and build authentication data on the browser side .
Two ways to implement identity authentication
At present, there are two main categories of methods to implement identity authentication:
Cookies
Traditional browser web pages use cookies to verify identity. In fact, in the application layer of the browser, there is basically no need to worry about identity verification. The settings of cookies are completed by the server. When submitting the request, the browser automatically attaches the corresponding cookie information. Therefore, in JavaScript code, there is no need to write special code for this. But every time I request, I will bring all the cookies data.
With the application of CDN and the gradual rise of mobile devices, cookies are increasingly unable to meet the complex, multi-domain authentication needs.
Key
In fact, key-based authentication hasn't just emerged recently, it has been around, even longer than Cookies' history. When the browser requests the server, it attaches the key to the request in a specific way, such as in the headers of the request. To do this, special client code is required to be written for management.
The recently emerging JSON-based Web Key (JSON Web Token) standard is a typical authentication using a key.
In web applications, if you are constructing APIs, you should give priority to using the key method.
Process login
Login is the first step in identity verification. Only by logging in can the corresponding identity verification data be organized.
Need to use a separate login page?
There are two ways to deal with login pages:
A separate login page will jump to the single-page application after login is completed. This allows access control of the resources of the single-page application to prevent non-login users from accessing, and is suitable for application scenarios of background or management tools. But it actually reduces the user experience of single-page applications
Execute login within a single page application , which is more in line with the design concept of a single page application and is more suitable for popular product scenarios, because malicious people can always get the front-end code of your single page application.
A separate login page
Generally speaking, the purpose of using a separate login page is to protect the pages that are jumped after login from being accessed by anonymous users. Therefore, in the login page, a form is constructed, and the traditional commendation submission method (non-Ajax) is directly used. After the backend validates the username and password successfully, the HTML of the single-sided application page after login is output.
In this case, you can directly place the authentication information in the output HTML, for example, you can use Jade to construct a page like this:
<!-- dashboard.jade -->doctype htmlhtmlhtml head link(rel="stylesheet", href="/assets/app.e1c2c6ea9350869264da10de799dced1.css") body script. window.token = !{JSON.stringify(token)}; div.md-padding(ui-view) script(src="/assets/app.84b1e53df1b4b23171da.js")After the username and password verification is successful, the backend can use the following method to render and output HTML:
return res.render('dashboard', { token: token});Once the Angular application is launched, it can communicate with authentication. It also ensures that only users who have successfully logged in can enter this page.
Organizations that log in to a single page app
For multi-view Angular applications, routing is generally used. Within the page, there is generally a fixed sidebar menu or a top navigation menu, and the text area is controlled by the routing module.
The following sample code uses Angular Material to organize the page, and the routing module uses ui-router. When the application is opened, there is a special loading animation. After the loading is completed, the page displayed is used by the AppController controller. For users who are not logged in, a login form will be displayed. After the login is completed, the page is divided into three parts: one is the top breadcrumb navigation , the second is the sidebar menu , and the other is the main part of the route control .
The code is as follows:
<body ng-app="app" layout="row"> <div id="loading"> <!--Page loading prompt--> </div> <div flex layout="row" ng-cloak ng-controller="AppController" ng-init="load()"> <div ng-if="!isUserAuth", ng-controller="LoginController"> <!--LoginForm--> </div> <div ng-if="isUserAuth" flex layout="row"> <md-sidenav flex="15" md-is-locked-open="true"> <!--Sidebar Menu--> </md-sidenav> <md-content flex layout="column" role="main"> <md-toolbar> <!--Top menu--> </md-toolbar> <md-content> <!--Routing--> <div ui-view></div> </md-content> </md-content> </div> </div></body>
For Loading animations, they are outside AppController and can be hidden in AppController code. This achieves the purpose of Loading disappearing after all CSS/JavaScript is loaded.
There is a variable in AppController isUserAuth It is false when initialized. When the locally stored session information is valid, or after the login is completed, this value will be set to ture . Due to the control of ng-if , the purpose of hiding the login form and displaying the application content can be achieved. It should be noted that only by using ng-if instead of ng-show/ng-hide here, the former will truly delete and add DOM elements, while the latter will only modify the CSS attributes of a DOM element. This is very important. Only in this way can we ensure that after logging in, the content in the single-page application will be loaded to prevent the controller code in the current route from being executed directly before logging in.
Why do clients also encrypt passwords
An ideal login process based on username and password is as follows:
1. The browser side obtains the password entered by the user, uses a hashing algorithm such as MD5 to generate a new password of fixed length, such as md5(username + md5(md5(password))) , and then submits the password hash value and username to the backend
2. The backend obtains the corresponding salt based on the user name, uses the user name and password hash value, calculates a ciphertext, and searches the database based on the user name and password
3. If the query is successful, generate the key, return it to the browser, and perform step 4
4. The backend generates new salt, and generates a new ciphertext based on the new salt and the password hash value submitted by the browser. Update salt and ciphertext in database
Perhaps 80% of people cannot understand why a login is so complicated. This may require a special article to explain clearly. Here I will first explain why the browser hash the password, and the reasons are as follows:
1. Protect the user's password from the source to ensure that only by doing key records can you get the user's original password
2. Even if the network is eavesdropped and https is not used, the stolen password is only after hashing, which can affect the user's data on this server at most, and does not affect other websites that use the same password.
3. Even the owner of the server cannot obtain the original password of the user.
This approach makes the biggest risk for users, and the data in the current application is stolen. The loss range will not be expanded, and there will never be any problems with CSDN or other streams.
Login successful notification
For some applications, not all pages require users to log in. They may only need to log in when performing certain operations. In this case, after logging in, the entire application must be notified. This allows you to use the broadcast function.
The simple code is as follows:
angular .module('app') .controller('LoginController', ['$rootScope', LoginController]);function LoginController($rootScope) { // Function called afterLoginSuccess() { $rootScope.$broadcast('user.login.success', { // Data to be transmitted}); }}In other controllers, you can listen to this broadcast and perform the operations that need to be performed after the login is successful, such as obtaining a list or details:
$scope.$on('user.login.success', function(handle, data){ // Processing});Identity authentication information
After logging in successfully, the server returns the key, and the subsequent API requests need to bring the key, and the response returned by the request also needs to be checked whether it is an error about the invalidity of the identity information. This series of work is quite cumbersome and should be completed automatically.
save
There are roughly the following ways to save the key:
1.Cookies: As mentioned earlier, this is not recommended. At the same time, it also has a maximum limit of 4k
2.sessionStorage: The tab page is valid. Once closed or a new tab page is opened, sessionStorage cannot be shared.
3.localStorage: an ideal storage method. Unless the browser data is cleaned, the data stored in localStorage will always exist.
4. Angular singleton service: If stored in the application, the data will be lost after refreshing, and of course, it cannot be shared between tab pages.
A better approach is that the authentication information is stored in localStorage, but it is initialized into Angular's singleton service when the application starts.
Add authentication information to the request
The purpose of identity authentication information is to indicate identity to the server and obtain data. Therefore, additional authentication information is required in the request.
In general applications, authentication information is placed in the headers of the request. If you set headers one by one at each request, it will be too time-consuming and laborious. $httpProvider in Angular provides an interceptor interceptors through which a unified processing of every request and response can be achieved.
The way to add an interceptor is as follows:
angular .module('app') .config(['$httpProvider', function($httpProvider){ $httpProvider.interceptors.push(HttpInterceptor); }]); The definition of HttpInterceptor is as follows:
angular .module('app') .factory('HttpInterceptor', ['$q', HttpInterceptor]);function HttpInterceptor($q) { return { // Before the request is issued, it can be used to add various authentication information request: function(config){ if(localStorage.token) { config.headers.token = localStorage.token; } return config; }, // An error occurred while requesting is issued requestError: function(err){ return $q.reject(err); }, // Response response: function(res){ return res; }, // The returned response error, including when the backend returns the response, the http status code of non-200 is set: function(err){ return $q.reject(err); } };}Interceptors provide the full life cycle processing from the request to the return response, which can generally be used to do the following:
1. Add data to the requests issued uniformly, such as adding authentication information
2. Unified error handling, including errors when the request is issued (such as the network on the browser side is not connected), and errors when the response is returned
3. Unified response processing, such as cache some data, etc.
4. Display the request progress bar
In the above example code, when localStorage includes the value token , a token value is added at the head of each request.
Failure and handling
Generally, the backend should set the response http status code to 401 when token verification fails, so that it can be handled uniformly in the interceptor responseError :
responseError: function(err){ if(-1 === err.status) { // The remote server is unresponsive} else if(401 === err.status) { // The 401 error is generally used for authentication failure. It depends on the error thrown by the backend when authentication fails} else if(404 === err.status) { // The server returns 404 } return $q.reject(err);}Summarize
In fact, as long as the status code returned by the server is not 200, responseError will be called. Here, the error can be handled uniformly and displayed.
The above content is related to login and identity verification in Angular development applications. I hope it will be helpful to everyone to learn Angular.