Nancy跨平臺開發總結(六)三層架構之Token認證的Rest API

在開始寫本節內容前,我使用Nancy.Authentication.Token實現的Token認證,可是就在我開始寫本節內容的時,我看到Nancyfx的文檔中的內容更新sql

因此我改成使用Nancy.Authentication.Stateless本身實現Token認證api

  • 新一個空的Asp.net Web項目,添加Nuget包
    •  Owin
    • Nancy
    • Nancy.Owin
    • Nancy.Bootstrappers.Autofac
    • Microsoft.Owin.Host.SystemWeb
    • Nancy.Authentication.Stateless
    • EntityFramework
    • Mysql.Data
    • Mysql.Data.Entity
  • 從上一節中的WebSite項目中拷貝如下幾個文件,修改命名空間,修改Bootstrapper去掉Form認證的相關代碼緩存

  • 在Models文件夾下新建一個AuthModel類文件,代碼以下
    using System.Collections.Generic;
    
    using Nancy.Security;
    using Nop.Core.Caching;
    
    namespace WebSite.WebApi.Models
    {
        /// <summary>
        /// 表明通過認證的用戶
        /// </summary>
        public class UserIdentity : IUserIdentity
        {
            public UserIdentity(string userName) :
                this(userName, new List<string>())
            {
            }
            public UserIdentity(string userName, IEnumerable<string> claims)
            {
                this.UserName = userName;
                this.Claims = claims;
            }
    
            public IEnumerable<string> Claims
            {
                get; private set;
            }
    
            public string UserName
            {
                get; private set;
            }
        }
    
        /// <summary>
        /// 包含生成token和校驗token的靜態方法
        /// </summary>
        public class UserMapper
        {
            private static readonly MemoryCacheManager manager = new MemoryCacheManager();
    
            /// <summary>
            /// 根據token獲取用戶信息,檢測用戶是否有效
            /// </summary>
            /// <param name="token"></param>
            /// <returns></returns>
            public static IUserIdentity GetUserFromAccessToken(string token)
            {
                if (string.IsNullOrEmpty(token))
                {
                    return null;
                }
                return manager.Get<UserIdentity>(token);
            }
    
            /// <summary>
            /// 生成一個新的token,並緩存
            /// </summary>
            /// <param name="userName"></param>
            /// <returns></returns>
            public static string GenerateToken(string userName)
            {
                string token= Guid.NewGuid().ToString();
                //token有效期
                manager.Set(token, new UserIdentity(userName),60*24);
                return token;
            }
        }
    
    }
  • 在Bootstrapper的RequestStartup事件中添加配置stateless認證.
    pipelines.AfterRequest.AddItemToEndOfPipeline(x =>
                {
                    x.Response.Headers.Add("Access-Control-Allow-Origin", "*");
                    x.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,DELETE,PUT,OPTIONS");
                });
    var configuration =new StatelessAuthenticationConfiguration(nancyContext =>
                   {
                    //返回null代碼token無效或用戶未認證
                      string token =       nancyContext.Request.Headers.Authorization;
                      if (!string.IsNullOrEmpty(token))
                      {
                          return UserMapper.GetUserFromAccessToken(token);
                      }
                      else
                      {
                          return null;
                      }
                   });
     StatelessAuthentication.Enable(pipelines, configuration);
  • 在UserService中添加根據Id查找用戶信息的方法,並在IUserService中添加對應接口
    /// <summary>
    /// Gets the User by identifier.
    /// </summary>
    /// <returns>The User by identifier.</returns>
    /// <param name="id">Identifier.</param>
    public virtual User GetUserById(int id)
    {
      if (id == 0)
           return null;
    
        string key = string.Format(Users_BY_ID_KEY, id);
        var user = _cacheManager.Get(key, () => userRepository.GetById(id));
        return user;
    }
  • 在Controller中建一個類AuthController類,用於驗證用戶並生成Token,代碼以下
    private IUserService service;
    public AuthController(IUserService service) : base("token")
    {
         this.service = service;
         Post["/"] = x =>
         {
             var user = this.Bind<User>();
             return GetToken(user.UserName, user.Password);
         };
    }
    
    private object GetToken(string username, string password)
    {
         DataResult<User> result = service.ValidateUser(username, password);
         if (result.Result == 0)
         {
              return new
               {
                   error = "認證失敗!",
                   error_description = result.Message,
               };
          }
          else
          {
               return new
               {
                   access_token = UserMapper.GenerateToken(username),
               };
          }
    }
  • 添加UserController,根據Id獲取用戶信息,並限定訪問接口必須認證後才能使用
    public class UserController:NancyModule
    {
            private IUserService service;
            public UserController(IUserService service):base("api/user")
            {
                //限制認證
                this.RequiresAuthentication();
                this.service = service;
    
              //異步模型
                Get["/{Id}", true] = async (_, ct) =>
                {
                    User user = await Task.Run(() =>
                    {
                        return service.GetUserById(_.Id);
                    });
                    return user;
                };
            }
    }
  • 建一個WebSite.Test的控制檯應用程序,經過nuget添加RestSharp引用包,在Main函數中添加如下代碼測試接口
    RestClient client = new RestClient("http://localhost:56751");
    string result = string.Empty;
    
    var request = new RestRequest("/token");
    var body = new
    {
           grant_type = "password",
           username = "admin",
           password = "1234567"
    };
    request.AddObject(body);
    IRestResponse response = client.Post(request);
    result = response.Content;
    dynamic content = SimpleJson.DeserializeObject(result); ;
    if (response.StatusCode != HttpStatusCode.OK)
    {
           string error = content.error_description;
           return;
    }
    
    string token = content.access_token;
    var request2 = new RestRequest("/api/user/1");
    request2.AddHeader("Authorization", token);
    IRestResponse response2 = client.Get(request2);
    if (response2.StatusCode == HttpStatusCode.OK)
    {
        result = response2.Content;
    }
相關文章
相關標籤/搜索