With the advent of multi-terminal, more and more sites through the Web API restful form of external services, many sites are also used in front-end separation mode for development, so in the way of authentication may be different from the traditional cookie-based session ID practice, In addition to the annoying problem of submitting cookies across domains, more importantly, some endpoints may not support cookies at all.
The Json Web Token (JWT) is a good authentication and authorization scheme, simply to verify the caller's authorization information when invoking the API with a Token issued by the API side.
However, due to the time relationship, do not describe the JWT more, please refer to Jwt.io
Here is a JWT-based authentication of the ASP. Demo, in order to simulate the development of front-end separation, the demo contains a static page site (the path in IIS is http://localhost:8057) and a Web API site ( http://localhost:8056). The static page site has only one index.html, which includes a login feature and two functions that call the interface that requires authentication to obtain data, all of which are called through Ajax.
Here are the main code that needs to be written, so let's step through the code.
Developing the Login interface
Since we used the JWT technology, we added a framework that encapsulates the JWT used first on NuGet. I used the following framework.
According to JWT definition, the data segment that hosts user identity information in a JWT is called payload. There is a need to create a class "AuthInfo" to represent payload.
/// <summary> ///represents the payload of a JWT/// </summary> Public classAuthInfo {/// <summary> ///User name/// </summary> Public stringUserName {Get;Set; } /// <summary> ///A list of roles that can be used to record the user's role, equivalent to the concept of claims (if you are not sure what claim, please Google "claims-based permission control")/// </summary> Publiclist<string> Roles {Get;Set; } /// <summary> ///whether it is an administrator/// </summary> Public BOOLISAdmin {Get;Set; }
Then write the login interface
Public classLogincontroller:apicontroller { Publicloginresult Post ([frombody]loginrequest request) {Loginresult rs=NewLoginresult (); //Suppose the username is "admin" and the password is "123" if(Request. UserName = ="Admin"&& request. Password = ="123") { //If the user is logged on successfully, the user's identity data can be obtained. Of course, in actual development, it is necessary to obtain the user's role and permissions in the database .AuthInfo AuthInfo =NewAuthInfo {isadmin=true, Roles=Newlist<string> {"Admin","owner"}, UserName="Admin" }; Try { //The build Token,securekey is a configured web. config that is used to encrypt token keys, and to kill and not to tell anyone byte[] key = Encoding.Default.GetBytes (configurationmanager.appsettings["SecureKey"]); //using HS256 encryption algorithm stringtoken =JWT. Jsonwebtoken.encode (AuthInfo, Key, JWT.) JWTHASHALGORITHM.HS256); Rs. Token=token; Rs. Success=true; } Catch{Rs. Success=false; Rs. Message="Login Failed"; } } Else{Rs. Success=false; Rs. Message="The user name or password is incorrect"; } returnrs; } }
In this connection, we have written the login interface. If the username and password are correct, the login interface generates a token containing the user's identity information as a response. Once the token is received by the front end, the token will be included in subsequent requests to prove its identity.
Authorizeattribute
Next, we need to write code about permission control and token parsing.
The Web API framework provides authorizeattribute to validate requests before invoking the API, and to customize the validation logic by overriding the Authorizeattribute.isauthorized method
Public classApiauthorizeattribute:authorizeattribute {protected Override BOOLisauthorized (Httpactioncontext actioncontext) {//The front-end request API will store tokens in the request header named "Auth" varAuthheader = fromHinchActionContext.Request.HeaderswhereH.key = ="Auth" SelectH.value.firstordefault (); if(Authheader! =NULL) { stringtoken =Authheader.firstordefault (); if(!string. IsNullOrEmpty (token)) {Try { //to decrypt token stringSecureKey = system.configuration.configurationmanager.appsettings["SecureKey"]; AuthInfo AuthInfo= JWT. Jsonwebtoken.decodetoobject<authinfo>(token, securekey); if(AuthInfo! =NULL) { //the user information is stored for subsequent calls .ACTIONCONTEXT.REQUESTCONTEXT.ROUTEDATA.VALUES.ADD ("Auth", AuthInfo); return true; } Else return false; } Catch { return false; } } Else return false; } Else return false; } }
Write an interface that is protected by authorizeattribute, assuming that the interface returns sensitive information that is relevant to the user.
It is important to note that because the front-end site and the Web API site use different ports, even the scheme (HTTP) and address are the same, but still cause cross-domain access. Therefore, cross-domain access must be enabled on the Web API site. In fact, cors (cross-domain resource sharing) or so-called same origin policy (same-origin policies) is a concept on the browser, and the server needs only to return a few response headers as needed:
Access-control-allow-origin: Indicates that the site is allowed access by those sources (domains)
Access-control-allow-headers: Indicates that the site allows those custom request headers (we send a request header with token named "Auth" via Jquery.ajax, so the API site settings are required to allow the "Auth" request header)
Access-control-allow-methods: Indicates that the site allows those request predicates (Get,post,options,put ...)
In the ASP. NET Web API, there are two ways to enable cross-domain access:
The first is to install the "Microsoft.AspNet.WebApi.Cors" package on NuGet and use the [Enablecors] feature for the API controller
The second type is configured in Web. config:
You must comment out "<remove name=" Optionsverbhandler "/>" To turn on the options verb processing, otherwise prefight will fail when cross-domain access.
The API code that returns sensitive information related to the user is as follows:
//marking the controller requires authentication[Apiauthorize] Public classUsercontroller:apicontroller { Public stringGet () {//Get back user information (in Apiauthorize by parsing token's payload and saving in Routedata)AuthInfo AuthInfo = This. requestcontext.routedata.values["Auth"] asAuthInfo; if(AuthInfo = =NULL) return "Invalid acceptance Information"; Else return string. Format ("hello: {0}, successful data acquisition", Authinfo.username); } }
Front-end site
To this, the API site code is written to complete. Next, write the code for the front-end site.
The front-end site has only one HTML page and contains two simple features: invoking the Login interface to log on, and invoking an interface that is secured for authentication to obtain data
The key script code for the front-end page is as follows:
$(function () { //Call the API site's login interface, and the interface returns a token after the login is successful. $ ("#login"). On ("click",function() {$.ajax ({URL:"Http://localhost:8056/api/login", Data: $ ("Form"). Serialize (), Method:"POST", Success:function(data) {if(data. Success) {//for simplicity, save the token in a global variable. Window.token =data. Token; Alert ("Login Successful"); } Else{alert ("Login failed:" +data. Message); } } }); }); //invokes an interface that obtains data from an API site that requires authentication. $ ("#invoke"). On ("click",function() {$.ajax ({URL:"Http://localhost:8056/api/user", Method:"Get", headers: {"Auth": Window.token},//sending tokens via the request header, discarding the way cookies are sentCompletefunction(jqxhr,textstatus) {alert (jqxhr.responsetext); }, }); }); }); </script>
Next, call the interface to get the data without logging in and log in, and use Fiddler to monitor the request and response process.
Press "Invoke interface" directly without logging in, the server returns 401 unauthorized information
The following are the communication conditions:
This time first login, and then call the interface, also in the Fiddler to monitor the communication situation.
In fiddler, you can see that the entire process browser makes 3 requests, namely login, prefight before calling the interface, and the actual calling interface:
Take a look at each communication situation
Login process:
Prefight
Actually issue a GET request call interface to get the data
In this case, the demo for authentication and cross-domain access based on JWT has been completed, so please correct me if there is a mistake.
Demo Source
JWT-based Web API authentication and cross-domain invocation practices