Nancy跨平台开发总结(六)三层架构之Token认证的Rest API

在开始写本节内容前,我使用Nancy.Authentication.Token实现的Token认证,但是就在我开始写本节内容的时,我看到Nancyfx的文档中的内容更新

所以我改为使用Nancy.Authentication.Stateless自己实现Token认证

  • 新一个空的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;
    }

     

 

posted @ 2016-03-02 14:09  凌锋  阅读(3558)  评论(1编辑  收藏  举报