在开始写本节内容前,我使用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{ ///
/// 代表经过认证的用户 /// public class UserIdentity : IUserIdentity { public UserIdentity(string userName) : this(userName, new List()) { } public UserIdentity(string userName, IEnumerable claims) { this.UserName = userName; this.Claims = claims; } public IEnumerable Claims { get; private set; } public string UserName { get; private set; } } /// /// 包含生成token和校验token的静态方法 /// public class UserMapper { private static readonly MemoryCacheManager manager = new MemoryCacheManager(); ////// 根据token获取用户信息,检测用户是否有效 /// /// ///public static IUserIdentity GetUserFromAccessToken(string token) { if (string.IsNullOrEmpty(token)) { return null; } return manager.Get (token); } /// /// 生成一个新的token,并缓存 /// /// ///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中添加对应接口
///
/// Gets the User by identifier./// ///The User by identifier. /// Identifier.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
(); return GetToken(user.UserName, user.Password); };}private object GetToken(string username, string password){ DataResult 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;}