dotnet core微服务框架Jimu ~ 会员授权微服务
提供授权服务,用户使用会员的用户名和密码获取 token, 带着 token 访问受保护的接口,如浏览和发布新闻。
有 2 个公开的 api:
- token: 获取 token;
- GetCurrentMemberInfo: 获取当前 token 的会员信息;
有 1 个不公开的方法:
- GetMemberInfo:根据会员用户名和密码,获取会员信息,该方法供获取 token 的 api 调用;
Install-Package Jimu
using System; using System.Collections.Generic; using System.Text; namespace Auth.IServices { public class MemberInfo { public string Id { get; set; } public string Name { get; set; } public string NickName { get; set; } public string Role { get; set; } } }
using System; using System.Collections.Generic; using System.Text; using Jimu; namespace Auth.IServices { public interface IAuthMemberService : IJimuService { MemberInfo GetMemberInfo(string username, string password); } }
上面的接口只继承 IJimuService, 不声明访问路由和接口属性,因为它不公开给外部调用的,但要用 autofac 注册到系统,在生成 token 时调用,所以需要继承 IJimuService。
using System; using System.Threading.Tasks; using Jimu; namespace Auth.IServices { [JimuServiceRoute("/api/v1/member")] public interface IMemberService : IJimuService { [JimuService(EnableAuthorization = true, CreatedBy = "grissom", CreatedDate = "2018-07-17", Comment = "get current token member info")] MemberInfo GetCurrentMemberInfo(); } }
上面的接口声明了 EnableAuthorization = true,需要授权的用户才能访问(即请求时要带上 token),该方法是获取当前会员信息。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Auth.IServices; using Jimu; namespace Auth.Services { public class AuthMemberService : IAuthMemberService { static List<MemberInfo> _membersDb = new List<MemberInfo>(); readonly ILogger _logger; public AuthMemberService(ILogger logger) { _logger = logger; } static AuthMemberService() { // mock some member _membersDb.Add(new MemberInfo { Id = Guid.NewGuid().ToString(), Name = "grissom", NickName = "Gil", Role = "admin" }); _membersDb.Add(new MemberInfo { Id = Guid.NewGuid().ToString(), Name = "foo", NickName = "Fo", Role = "guest" }); } public MemberInfo GetMemberInfo(string username, string password) { var member = _membersDb.FirstOrDefault(x => x.Name == username && "123" == password); _logger.Debug($"username: {username}, found {(member == null ? "no " : "")} member."); return member; } } }
logger 是通过依赖注入的
using Jimu; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Auth.IServices { public class MemberService : IMemberService { readonly ILogger _logger; readonly JimuPayload _jimuPayload; public MemberService(ILogger logger, JimuPayload jimuPayload) { _logger = logger; _jimuPayload = jimuPayload; } public MemberInfo GetCurrentMemberInfo() { _logger.Debug($"current token member username: {_jimuPayload.Items["username"]}"); return Newtonsoft.Json.JsonConvert.DeserializeObject<MemberInfo>(_jimuPayload.Items["member"].ToString()); } } }
logger 和 jimuPayload 都是通过依赖注入的。JimuPayload 是 Jimu 框架解析 token 获取的一些生成 token 时,添加的信息(参考下面生成 token 的方法)。如果请求的 token 无效,则 JimuPayload 为 null。
Install-Package Jimu.Server
Install-Package Jimu.Common.Discovery.ConsulIntegration
using System; using Autofac; using Jimu; using Jimu.Server; using Jimu.Server.OAuth; using Auth.IServices; namespace Auth.Server { class Program { static void Main(string[] args) { IServiceHost host = null; var builder = new ServiceHostServerBuilder(new ContainerBuilder()) .UseLog4netLogger() .LoadServices("Auth.IServices", "Auth.Services") .UseDotNettyForTransfer("127.0.0.1", 8000) .UseConsulForDiscovery("127.0.0.1", 8500, "JimuService", $"127.0.0.1:8000") .UseJoseJwtForOAuth<DotNettyAddress>(new JwtAuthorizationOptions { SecretKey = "123456", // 生成 token 的密钥 ExpireTimeSpan = new TimeSpan(3, 0, 0, 0), // token 有效时间 3 天 ValidateLifetime = true, // 是否启动验证 token 的有效时间 ServerIp = "127.0.0.1", // 生成 token 的宿主服务器地址 ServerPort = 8000, // 生成 token 的宿主服务器端口 TokenEndpointPath = "api/oauth/token?username=&password=", // 获取 token 的路由,注意后缀 ?username=&password= 是固定的 CheckCredential = new Action<JwtAuthorizationContext>(ctx => { var memberService = host.Container.Resolve<IAuthMemberService>(); var member = memberService.GetMemberInfo(ctx.UserName, ctx.Password); if (member == null) { ctx.Rejected("username or password is incorrect.", ""); } else { // 上面提到的 JimuPayload 就是包含这些数据 ctx.AddClaim("roles", member.Role); // 添加角色到 token ctx.AddClaim("member", Newtonsoft.Json.JsonConvert.SerializeObject(member)); // 把整个 member 序列化打包到 token } }), // 生成 token 的配置项和验证逻辑 }); using (host = builder.Build()) { host.Run(); while (true) { Console.ReadKey(); } } } } }