提供授權服務,使用者使用會員的使用者名稱和密碼獲取 token, 帶著 token 訪問受保護的介面,如瀏覽和釋出新聞。
有 2 個公開的 api:
- token: 獲取 token;
- GetCurrentMemberInfo: 獲取當前 token 的會員資訊;
有 1 個不公開的方法:
- GetMemberInfo:根據會員使用者名稱和密碼,獲取會員資訊,該方法供獲取 token 的 api 呼叫;
1 宣告介面,建立基於 .Net Core 6.0 的類庫專案,命名為 Auth.IServices
1.1 新增 jimu 引用
Install-Package Jimu
1.2 建立 dto 類
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; } } }
1.3 宣告公開的微服務介面
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),該方法是獲取當前會員資訊。
2 實現介面,建立基於 .Net Core 6.0 的類庫專案,命名為 Auth.Services
2.1 新增對介面專案 Auth.IServices 的引用
2.2 實現介面
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。
3 微服務的宿主伺服器,建立基於 .Net Core 2.0 的控制檯應用, 命名為 Auth.Server
3.1 新增對專案: Auth.Services 的引用
3.2 新增 jimu.server 和 Jimu.Common.Discovery.ConsulIntegration 引用
Install-Package Jimu.Server
Install-Package Jimu.Common.Discovery.ConsulIntegration
3.3 啟動 jimu 服務和生成 token 的程式碼
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(); } } } } }