你們都知道咱們的項目中已有web api,如今能夠正式訪問,不論任何人只要經過輸入對應的api網址就能夠訪問到咱們的api 資源,這樣是很不安全的,咱們需求對當前用戶進行身份驗證,所以咱們在項目中使用IdentityServer4來對受保護資源並實現身份驗證和/或受權,直接開始上代碼,這些代碼直接能夠在你的項目中使用,並跑起來。android
一、 新建一個空的.netcore web項目,並引入IdentityService4的NuGet包。web
二、 在項目中增長一個config.cs文件json
public class Config { public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId="iphone", ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) }, RefreshTokenExpiration = TokenExpiration.Sliding, AllowOfflineAccess = true, RequireClientSecret = false, AllowedGrantTypes = new List<string>{"sms_suth_code"}, AlwaysIncludeUserClaimsInIdToken = true, AllowedScopes = new List<string> { "gateway_api", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OfflineAccess, }, }, new Client { ClientId="android", ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) }, RefreshTokenExpiration = TokenExpiration.Sliding, AllowOfflineAccess = true, RequireClientSecret = false, AllowedGrantTypes = new List<string>{"sms_auth_code"}, AlwaysIncludeUserClaimsInIdToken = true, AllowedScopes = new List<string> { "gateway_api", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OfflineAccess, }, } }; } public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("gateway_api","user service") }; } }
三、在項目中新增類SmsAuthCodeValidator該類主要是實現IdentityServer4組件中的IextensionGrantValidator接口ValidateAsync()的方法,在該方法寫上本身的驗證邏輯,這裏咱們只用戶在登陸時輸入的手機號和驗證碼進行了校驗,固然在校驗時會調用用戶模塊的api,也就是咱們_userService.CheckOrCreate(phone)方法,服務以前如何發現和調用這裏不展開,下節介紹。SmsAuthCodeValidator代碼以下api
public class SmsAuthCodeValidator : IExtensionGrantValidator { private readonly IAuthCodeService _authCodeService; private readonly IUserService _userService; public SmsAuthCodeValidator(IAuthCodeService authCodeService, IUserService userService) { _authCodeService = authCodeService; _userService = userService; } public string GrantType => "sms_auth_code"; public async Task ValidateAsync(ExtensionGrantValidationContext context) { var phone = context.Request.Raw["phone"]; var code = context.Request.Raw["auth_code"]; var error = new GrantValidationResult(TokenRequestErrors.InvalidGrant); if(!string.IsNullOrEmpty(phone)&& !string.IsNullOrEmpty(code)) { //用戶檢查 _authCodeService.Validate(phone, code); //用戶註冊 var userId = await _userService.CheckOrCreate(phone); if(userId <=0) { context.Result = error; return; } context.Result = new GrantValidationResult(userId.ToString(), GrantType); } else { context.Result = error; } } }
四、在SmsAuthCodeValidator類咱們引用了兩個本地的服務,一個是對驗證碼進行校驗的AuthCodeService類,一個是對手機號進行校驗的UserService,也就是在這個類中對用戶服務模塊進行的手機號校驗。現將這兩個代碼的代碼寫上安全
public interface IAuthCodeService { /// <summary> /// 驗證 /// </summary> /// <param name="phone">手機號</param> /// <param name="authCone">驗證碼</param> /// <returns></returns> bool Validate(string phone, string authCone); } public class AuthCodeService : IAuthCodeService { public bool Validate(string phone, string authCone) { return true; } }
public interface IUserService { /// <summary> /// 檢查手機是否註冊,若是沒有就建立 /// </summary> /// <param name="phone"></param> Task<int> CheckOrCreate(string phone); } public class UserService : IUserService { public async Task<int> CheckOrCreate(string phone) { return 1; } }
五、最後咱們須要增長IdentityServer4中間件,並對咱們的服務進行配置服務器
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddExtensionGrantValidator<SmsAuthCodeValidator>() .AddDeveloperSigningCredential() .AddInMemoryClients(Config.GetClients()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()); services.AddSingleton(new HttpClient()); services.AddScoped<IAuthCodeService, AuthCodeService>() .AddScoped<IUserService, UserService>(); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.UseMvc(); } }
五、 生成並啓動該項目,經過postman訪問,需求增長以下六個參數app
六、若是沒有問題的話咱們會獲取到系統反饋給咱們的token值,返回結果以下:框架
{ "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjlhMjJlN2E3Zjg1ZmY5MjNiMTJmM2Nm
NGZkMGM3YzYzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1ODg5MjE2MjUsImV4cCI6
MTU4ODkyNTIyNSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDoxMTEwIiwiYXVkIjpb
Imh0dHA6Ly9sb2NhbGhvc3Q6MTExMC9yZXNvdXJjZXMiLCJnYXRld2F5X2FwaSJd
LCJjbGllbnRfaWQiOiJhbmRyb2lkIiwic3ViIjoiMyIsImF1dGhfdGltZSI6MTU4
ODkyMTYyNSwiaWRwIjoibG9jYWwiLCJzY29wZSI6WyJvcGVua
WQiLCJwcm9maWxlIiwiZ2F0ZXdheV9hcGkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhb
XIiOlsic21zX2F1dGhfY29kZSJdfQ.pyEKOe08jiqtg1rgcf0UGO0hmfEhI5a2cIXw
_-YgXdLVceKa14Jhyy8Ezgom3ipNlci5FwmN-p5ro_3ORtzreU0qxhiCzI5kyPgLRP
lOO8cFykYKY4yQOCD_z2LohSxyvAsTPn0B75_iodujGPQAB4Outs9uAjcHXAnxjBkn
DKl6L5uu609ZaugG4X6T2xx0ZDU-VftrrmB-YX5oe6FU70R4jsRLayL8nrM-u-Q_We
UIfY04M91REX9HqneOGyxSDj2Qku22pC68dlIYQNGhBlYUnSqRMkk39Pe9UmjO8dSp
qqBMtHBEwCQn3cMzG7UbP5gB6F2GgTICUBERbxxwRA", "expires_in": 3600, "token_type": "Bearer", "refresh_token": "f3051fa24cebf7cbfa73b55563a283bb3c15b129c8c5ff732324a653a7c6eff1" }
七、 懷着好奇的心咱們來看看這個access_token的值反饋給咱們的是什麼,其實他就是JWT(Json Web Token),解析成json格式以下iphone
{ alg: "RS256", kid: "9a22e7a7f85ff923b12f3cf4fd0c7c63", typ: "JWT" }. { nbf: 1588921625, exp: 1588925225, iss: "http://localhost:1110", aud: [ "http://localhost:1110/resources", "gateway_api" ], client_id: "android", sub: "3", auth_time: 1588921625, idp: "local", scope: [ "openid", "profile", "gateway_api", "offline_access" ], amr: [ "sms_auth_code" ] }.
[signature]
順便學習一下JWT吧:async
HTTP提供了一套標準的身份驗證框架:服務器能夠用來針對客戶端的請求發送質詢(challenge),客戶端根據質詢提供身份驗證憑證。質詢與應答的工做流程以下:服務器端向客戶端返回401(Unauthorized,未受權)狀態碼,並在WWW-Authenticate頭中添加如何進行驗證的信息,其中至少包含有一種質詢方式。而後客戶端能夠在請求中添加Authorization頭進行驗證,其Value爲身份驗證的憑證信息。
Bearer認證(也叫作令牌認證)是一種HTTP認證方案,其中包含的安全令牌的叫作Bearer Token。所以Bearer認證的核心是Token。那如何確保Token的安全是重中之重。一種方式是使用Https,另外一種方式就是對Token進行加密簽名。而JWT就是一種比較流行的Token編碼方式。能夠看出JWT有三部分組成:
<header>.<payload>.<signature>
alg
和typ
組成,alg
是algorithm的縮寫,typ
是type的縮寫,指定token的類型。該部分使用Base64Url
編碼。BaseURL
編碼。下一篇咱們將介紹網關,經過網關來訪問咱們的用戶模塊的api資源,並集成IdentityServer4