[Practice of WEB APIs]-API logon and identity authentication (3)

Source: Internet
Author: User
Tags ssl certificate

[Practice of WEB APIs]-API logon and identity authentication (3)

Previous Article: [web api Project Practice Series]-interface documentation and online testing (2)

This article describes how to log on to and authenticate an API in an API project. Therefore, this article consists of two parts: log on to the API and API authentication.

The main principle of this article is: the API will provide a separate login API to generate a SessionKey through the user name and password. The SessionKey has the characteristics of expiration time, and the system will record this SessionKey, in each subsequent API return, the client must carry this Sessionkey, And the API will verify this SessionKey.

Login API

Let's take a look at the method signature for logging on to the API.

[RoutePrefix ("api/accounts")] public class AccountController: ApiController {private readonly IAuthenticationService _ authenticationService = null; public AccountController () {// this. _ authenticationService = IocManager. intance. reslove <IAuthenticationService> ();} [HttpGet] public void AccountsAPI () {}/// <summary> /// log on to the API /// </summary> /// <param name = "loginIdorEmail"> log on to the Account (email address or other LoginID) </p Aram> // <param name = "hashedPassword"> the encrypted password. Clear Text is avoided here, the client is encrypted and uploaded to the API </param> /// <param name = "deviceType"> device type of the client </param> /// <param name = "clientId"> client identification number, generally, the APP has a client identification number </param> /// <remarks> for Other Logon locations, which must be something that can be uploaded by the client, </remarks> // <returns> </returns> [Route ("account/login")] public SessionObject Login (string loginIdorEmail, string hashedPassword, int deviceType = 0, string clientId = "") {If (string. isNullOrEmpty (loginIdorEmail) throw new ApiException ("username can't be empty. "," RequireParameter_username "); if (string. isNullOrEmpty (hashedPassword) throw new ApiException ("hashedPassword can't be empty. "," RequireParameter_hashedPassword "); int timeout = 60; var nowUser = _ authenticationService. getUserByLoginId (loginIdorEmail); if (nowUser = null) throw new ApiException ("Accoun T Not Exists "," Account_NotExits "); # region Verify Password if (! String. Equals (nowUser. Password, hashedPassword) {throw new ApiException ("Wrong Password", "Account_WrongPassword");} # endregion if (! NowUser. isActive) throw new ApiException ("The user is inactive. "," InactiveUser "); UserDevice existsDevice = _ authenticationService. getUserDevice (nowUser. userId, deviceType); // Session. queryOver <UserDevice> (). where (x => x. accountId = nowAccount. id & x. deviceType = deviceType ). singleOrDefault (); if (existsDevice = null) {string passkey = MD5CryptoProvider. getMD5Hash (nowUser. userId + nowUser. loginName + DateTime. utcNow. toString () + Guid. newGuid (). toString (); existsDevice = new UserDevice () {UserId = nowUser. userId, CreateTime = DateTime. utcNow, ActiveTime = DateTime. utcNow, ExpiredTime = DateTime. utcNow. addMinutes (timeout), DeviceType = deviceType, SessionKey = passkey}; _ authenticationService. addUserDevice (existsDevice);} else {existsDevice. activeTime = DateTime. utcNow; existsDevice. expiredTime = DateTime. utcNow. addMinutes (timeout); _ authenticationService. updateUserDevice (existsDevice);} nowUser. password = ""; return new SessionObject () {SessionKey = existsDevice. sessionKey, LogonUser = nowUser };}}

 

API Authentication

Identity Authentication is implemented through the ActionFilter of Web APIs. Each API request that requires authentication requires the client to transmit a SessionKey to the URL.

Here, we use a custom SessionValidateAttribute to authenticate the client, which inherits from System. web. http. filters. actionFilterAttribute: add this Attribute to each ApiControler that requires authentication. In this way, all actions under the Controller will have the authentication function, there will be a small number of APIs that do not require authentication, so how to deal with them will be excluded. To keep the idea of the article clear, this will be explained in the subsequent sections.

Public class SessionValidateAttribute: System. web. http. filters. actionFilterAttribute {public const string SessionKeyName = "SessionKey"; public const string LogonUserName = "LogonUser"; public override void OnActionExecuting (HttpActionContext filterContext) {var qs = HttpUtility. parseQueryString (filterContext. request. requestUri. query); string sessionKey = qs [SessionKeyName]; if (string. isNullOrE Mpty (sessionKey) {throw new ApiException ("Invalid Session. "," InvalidSession ");} IAuthenticationService authenticationService = IocManager. intance. reslove <IAuthenticationService> (); // validate user session var userSession = authenticationService. getUserDevice (sessionKey); if (userSession = null) {throw new ApiException ("sessionKey not found", "RequireParameter_sessionKey");} else {// todo: add If (userSession. expiredTime <DateTime. utcNow) throw new ApiException ("session expired", "SessionTimeOut"); var logonUser = authenticationService. getUser (userSession. userId); if (logonUser = null) {throw new ApiException ("User not found", "Invalid_User");} else {filterContext. controllerContext. routeData. values [LogonUserName] = logonUser; SetPrincipal (new UserPrincipal <int> (logon User);} userSession. activeTime = DateTime. utcNow; userSession. expiredTime = DateTime. utcNow. addMinutes (60); authenticationService. updateUserDevice (userSession) ;}} private void SetPrincipal (IPrincipal principal) {Thread. currentPrincipal = principal; if (HttpContext. current! = Null) {HttpContext. Current. User = principal ;}}}

 

OnActionExcuting method:

This is to check before entering an Action. At this time, we can just take out the SessionKey from RequestQueryString to the UserDevice table for query to verify the authenticity of Sessionkey, so as to achieve the purpose of identity authentication.

 

User expiration time:

During each API access, the expiration time of the Session (that is, UserDevice) is automatically updated to ensure that the SessionKey does not expire. If the Session is not updated for a long time, the next access will expire, you need to log on again.

 

Request. IsAuthented:

The last SetPrincipal of the code above is to set the user identity information in the thread context and HttpContext context. Here we implement our own user identity type.

public class UserIdentity<TKey> : IIdentity    {        public UserIdentity(IUser<TKey> user)        {            if (user != null)            {                IsAuthenticated = true;                UserId = user.UserId;                Name = user.LoginName.ToString();                DisplayName = user.DisplayName;            }        }        public string AuthenticationType        {            get { return "CustomAuthentication"; }        }        public TKey UserId { get; private set; }        public bool IsAuthenticated { get; private set; }        public string Name { get; private set; }        public string DisplayName { get; private set; }    }    public class UserPrincipal<TKey> : IPrincipal    {        public UserPrincipal(UserIdentity<TKey> identity)        {            Identity = identity;        }        public UserPrincipal(IUser<TKey> user)            : this(new UserIdentity<TKey>(user))        {        }        /// <summary>        ///         /// </summary>        public UserIdentity<TKey> Identity { get; private set; }        IIdentity IPrincipal.Identity        {            get { return Identity; }        }        bool IPrincipal.IsInRole(string role)        {            throw new NotImplementedException();        }    }    public interface IUser<T>    {        T UserId { get; set; }        string LoginName { get; set; }        string DisplayName { get; set; }    }

This ensures that the User information of the current Thread context can be obtained anywhere in the System through HttpContext. User or System. Threading. Thread. CurrentPrincipal, which is convenient to use everywhere.

 

The Product-related APIs after identity authentication are as follows:

[RoutePrefix ("api/products"), SessionValidate] public class ProductController: ApiController {[HttpGet] public void ProductsAPI () {}/// <summary> /// obtain the product paging data /// </summary> /// <returns> </returns> [HttpGet, route ("product/getList")] public Page <Product> GetProductList (string sessionKey) {return new Page <Product> ();} /// <summary> /// obtain a single product /// </summary> /// <param name = "productId"> </param> /// <returns> </returns> [HttpGet, route ("product/get")] public Product GetProduct (string sessionKey, Guid productId) {return new Product () {ProductId = productId };} /// <summary> /// add product /// </summary> /// <param name = "product"> </param> /// <returns> </returns> [HttpPost, route ("product/add")] public Guid AddProduct (string sessionKey, Product product) {return Guid. newGuid ();} /// <summary> /// update the product /// </summary> /// <param name = "productId"> </param> /// <param name = "product"> </param> [HttpPost, route ("product/update")] public void UpdateProduct (string sessionKey, Guid productId, Product product) {}/// <summary> /// Delete the product /// </summary> /// <param name = "productId"> </param> [HttpDelete, route ("product/delete")] public void DeleteProduct (string sessionKey, Guid productId ){}

 

We can see that SessionValidateAttribute is added to ProductController, and the first position of each Action parameter is added with a string sessionKey placeholder. This is mainly used to enable Swagger. net can generate a test window on the UI

This article does not use authorization mechanisms such as OAuth, but simply implements logon authorization. This method is suitable for small projects.

Here, we only implement system login. API access is secure and the absolute security of the API system cannot be guaranteed. we can intercept our API requests through HTTP messages on the route, we also need to add an SSL certificate to our API to implement HTTPS encrypted transmission.

In addition, a few days ago, we saw that the Sessionkey is generated in combination with the client IP address and so on for security, but it also has some limitations. That solution is suitable and should be determined based on your actual project situation.

 

Due to time reasons, this article only introduces the principle of API user login and access identity authentication, because this part of the real test design to database interaction, Ioc and other infrastructure support, therefore, this code can only appear in SwaggerUI, but cannot actually test the interface. In the following code, I will improve this part.

Code: Download

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.