JWT 是JSON風格輕量級的受權和身份認證規範,可實現無狀態、分佈式的Web應用受權;git
從客戶端請求服務器獲取token, 用該token 去訪問實現了jwt認證的web服務器。 token 可保存自定義信息,如用戶基本信息, web服務器用key去解析token,就獲取到請求用戶的信息了;
很方便解決跨域受權的問題,由於跨域沒法共享cookie,.net平臺集成的 FormAuthentication 認證系統是基於Session保存受權信息,拿不到cookie就沒法認證,用jwt完美解決了。
不少時候,web服務器和受權服務器是同一個項目,因此也能夠用如下架構:
web
1.vs2015 新建一個WebApi,安裝下面的庫,可用nuget 或 命令安裝:chrome
install-package Thinktecture.IdentityModel.Core
install-package Microsoft.Owin.Security.Jwt
2.把Startup.Auth.cs 下的 ConfigureAuth 方法清空掉,改成:數據庫
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { var issuer = ConfigurationManager.AppSettings["issuer"]; var secret = TextEncodings.Base64Url.Decode(Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(ConfigurationManager.AppSettings["secret"]))); //用jwt進行身份認證 app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AllowedAudiences = new[] { "Any" }, IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]{ new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) } }); app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { //生產環境設爲false AllowInsecureHttp = true, //請求token的路徑 TokenEndpointPath = new PathString("/oauth2/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(30), //請求獲取token時,驗證username, password Provider = new CustomOAuthProvider(), //定義token信息格式 AccessTokenFormat = new CustomJwtFormat(issuer, secret), }); } }
3.ConfigureAuth中的 AccessTokenFormat = new CustomJwtFormat(issuer, secret)是自定義token 保存的信息格式, CustomJwtFormat.cs 類代碼json
/// <summary> /// 自定義 jwt token 的格式 /// </summary> public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket> { private readonly byte[] _secret; private readonly string _issuer; public CustomJwtFormat(string issuer, byte[] secret) { _issuer = issuer; _secret = secret; } public string Protect(AuthenticationTicket data) { if (data == null) { throw new ArgumentNullException(nameof(data)); } var signingKey = new HmacSigningCredentials(_secret); var issued = data.Properties.IssuedUtc; var expires = data.Properties.ExpiresUtc; return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(_issuer, "Any", data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey)); } public AuthenticationTicket Unprotect(string protectedText) { throw new NotImplementedException(); } }
4.ConfigureAuth中的 Provider = new CustomOAuthProvider() 是自定義驗證username, password 的,能夠用它來實現訪問數據庫的驗證業務邏輯,CustomOAuthProvider.cs類代碼跨域
/// <summary> /// 自定義 jwt oauth 的受權驗證 /// </summary> public class CustomOAuthProvider : OAuthAuthorizationServerProvider { public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var username = context.UserName; var password = context.Password; string userid; if (!CheckCredential(username, password, out userid)) { context.SetError("invalid_grant", "The user name or password is incorrect"); context.Rejected(); return Task.FromResult<object>(null); } var ticket = new AuthenticationTicket(SetClaimsIdentity(context, userid, username), new AuthenticationProperties()); context.Validated(ticket); return Task.FromResult<object>(null); } public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); return Task.FromResult<object>(null); } private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, string userid, string usercode) { var identity = new ClaimsIdentity("JWT"); identity.AddClaim(new Claim("userid", userid)); identity.AddClaim(new Claim("username", usercode)); return identity; } private static bool CheckCredential(string usernme, string password, out string userid) { var success = false; // 用戶名和密碼驗證 if (usernme == "admin" && password == "admin") { userid = "1"; success = true; } else { userid = ""; } return success; } }
5.Web.config 添加 issue 和 secretbash
<appSettings> <add key="issuer" value="test"/> <!--32個字符的secret--> <add key="secret" value="12345678123456781234567812345678"/> </appSettings>
強烈建議用 chrome 的 postman 插件來調試服務器
獲取token
cookie
用token請求數據
架構
header 要添加 Authorization , 值爲: Bearer [token], 獲取到的 token 替換 [token], 如
Authorization Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOiIxIiwidXNlcmNvZGUiOiIxIiwiaXNzIjoidGVzdCIsImF1ZCI6IkFueSIsImV4cCI6MTQ4NzI0MTQ5MCwibmJmIjoxNDg0NjQ5NDkwfQ.RaWlJC3OF0RNz4mLtuW4uQtRKDHF8RXwZwzIcbZoNOo