2015年9月2日 星期三

在 Web Api 自訂 ClaimsIdentity 和在取 token 的回傳 josn 中新增資訊

新增一個 Web Api 專案, 查看 App_Start/Startup.Auth.cs 的這個方法 :
public void ConfigureAuth(IAppBuilder app)
{
    // 設定資料庫內容和使用者管理員以針對每個要求使用單一執行個體
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
 
    // 讓應用程式使用 Cookie 儲存已登入使用者的資訊
    // 並使用 Cookie 暫時儲存使用者利用協力廠商登入提供者登入的相關資訊;
    app.UseCookieAuthentication(new CookieAuthenticationOptions());
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
 
    // 設定 OAuth 基礎流程的應用程式
    PublicClientId = "self";
    OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/Token"),
        Provider = new ApplicationOAuthProvider(PublicClientId),
        AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
        // 在生產模式中設定 AllowInsecureHttp = false
        AllowInsecureHttp = true
    };
 
    // 讓應用程式使用 Bearer 權杖驗證使用者
    app.UseOAuthBearerTokens(OAuthOptions);
}
得知 OAuthAuthorizationServerOptions.Provider 使用 ApplicationOAuthProvider, 想要在取 token 所回傳的 json 中加入其他資料(例如 name 和 city), 因此針對他的 GrantResourceOwnerCredentials 方法進行修改 :
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    await Task.Run(() =>
    {
        // 自行驗證
        if (context.UserName == "ian" && context.Password == "1111")
        {
            // 驗證通過
 
            // 建立一個 ClaimsIdentity
            var identity = new ClaimsIdentity(context.Options.AuthenticationType);
            // 加入一些自行命名且好讀的 Claim
            identity.AddClaim(new Claim("name""Ian"));
            identity.AddClaim(new Claim("city""Tainan"));
            // 建立一個 AuthenticationProperties
            var p1 = new Dictionary<stringstring>
            {
                {"name","Ian"}, // 顯示於回傳的json中
                {"city","Tainan"}
            };
            AuthenticationProperties properties = new AuthenticationProperties(p1);
            // 使用 ClaimsIdentity 和 AuthenticationProperties 來產生一個 AuthenticationTicket
            AuthenticationTicket ticket = new AuthenticationTicket(identity, properties);
            // 替換此內容上的票證資訊,並讓其由應用程式驗證。 呼叫之後,IsValidated 為 true 且 HasError 為 false。
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(identity);
        }
        else
        {
            context.SetError("invalid_grant""使用者名稱或密碼不正確。");
            return;
        }
    });
}

測試 :
  1. POST /token
  2. form data :
  3. username:
    ian
  4. password:
    1111
  5. grant_type:
    password
使用 jQuery.ajax 取 Token :
$.ajax({
    url: '/token',
    data: { username: username, password: password, grant_type: 'password' },
    type: 'POST',
    success: function (data) {
        console.log(data);
        $('#token').val(data.access_token);
    }
 
});

結果 :

{"access_token":"F5PU_EhpbLl7aw_EwlTuP8unNXc4L9olOlqbuan0oQbjPyh-u5iEUjQnRcs7AkV6ia7clMPn8JyZZxNucD5mP_vUTvmeGjDGZJI33qzWzehGP4xQr5HCMQ0EtaCBi7pxq0WttOtLYumoZNXmBDnRWqTtn3s7iBszewS1IHb__J2-zd1nzVmT7VKOVe_GFQhKCB_cXMqCPyfPaERLrzBYjT3ju3RYIrDn1m-ZuaLXwVM","token_type":"bearer","expires_in":1209599,"name":"Ian","city":"Tainan",".issued":"Wed, 02 Sep 2015 07:33:46 GMT",".expires":"Wed, 16 Sep 2015 07:33:46 GMT"}

name 和 city 被加入到回傳的 json 中

在後端程式中使用 ClaimsIdentity 中的資料, 例如取得使用者的 city :
[Authorize]
public class UserCityController : ApiController
{
    public string Get()
    {
        var identity = User.Identity as ClaimsIdentity;
        var city = (identity.Claims).Where(x => x.Type == "city").Select(x => x.Value).FirstOrDefault();
        return city;
    }
}

前端使用 jQuery 撈取 :
$.ajax({
    url: '/api/usercity',
    type: 'GET',
    headers: {
        'Authorization': 'Bearer ' + $('#token').val()
    },
    success: function (data) {
        $('#user-city').text(data);
    }
 
});
Authorization : Bearer {token}






沒有留言: