IdentityServer4之Implicit(隱式許可) —— oidc-client-js先後端分離

IdentityServer4之Implicit(隱式許可) —— oidc-client-js先後端分離

參考

官方文檔oidc-client-js:oidc-client是一個JavaScript庫,用於在瀏覽器(也多是Cordova風格的應用程序)中運行。它爲OIDC和OAuth2提供協議支持,併爲用戶會話和訪問令牌管理提供管理功能。文檔最後也給出了Angular二、Aurelia、ReactJS & Redux、Blog post on Angular這幾種前端框架的示例。html

JsOidc:使用oidc-client-js的客戶端示例。前端

概念隱式許可:簡單描述過程以及適用範圍。git

Authorize Endpoint:主要介紹了請求過程參數含義。github

 三個Web站點:一、localhost:5000:認證服務器。二、www.jsclient.com:js純靜態客戶端。三、localhost:5001:Api資源服務器。sql

認證服務端配置

認證服務ApiResource配置後端

new ApiResource("api1", "api項目 一")
{
    ApiSecrets = { new Secret("api1pwd".Sha256()) }
},

認證服務Client配置api

js client的靜態資源服務端配置了host。跨域

// JavaScript Client
new Client
{
    ClientId = "js",
    ClientName = "JavaScript Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    AllowAccessTokensViaBrowser = true,

    RedirectUris = {
        "http://localhost:5009/callback.html",
        "http://www.jsclient.com/callback.html"
    },
    PostLogoutRedirectUris = {
        "http://localhost:5009/index.html",
        "http://www.jsclient.com/index.html"
    },
    AllowedCorsOrigins = {
        "http://localhost:5009",
        "http://www.jsclient.com"
    },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    }
},

 

資源服務Api配置

資源服務器Startup配置瀏覽器

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    //services.AddMvc();
    services.AddMvcCore()
        .AddAuthorization()
        .AddJsonFormatters();

    services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;

            options.ApiName = "api1";  
            options.ApiSecret = "api1pwd";  //對應ApiResources中的密鑰
        });
    //支持跨域
    services.AddCors(options =>
    {
        // this defines a CORS policy called "default"
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://www.jsclient.com")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });
}

Configure中添加 app.UseCors("default"); 前端框架

 

添加接口

[Route("[controller]")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        var info = from c in User.Claims select new { c.Type, c.Value };
        var list = info.ToList();
        list.Add(new { Type = "api1返回", Value = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") });
        return new JsonResult(list);
    }
}

Client客戶端

一、JS Client

js Web-Hosted Client Resource的靜態資源。

運行後效果以下:

頁面中操做按鈕以下:
<li><a class="btn btn-primary" href="index.html">Home</a></li>
<li><button class="btn btn-default request" data-scope='openid' data-type='id_token'>Login Only</button></li>
<li><button class="btn btn-default request" data-scope='openid profile' data-type='id_token'>Profile</button></li>
<li><button class="btn btn-default request" data-scope='openid profile api1' data-type='id_token token'>Profile and API Access</button></li>
<li><button class="btn btn-default request" data-scope='api1' data-type='token'>API Access Only</button></li>
<li><button class="btn btn-primary call">Call Service</button></li>
<li><button class="btn btn-info revoke">Revoke Access Token</button></li>
<li><button class="btn btn-info logout">Logout</button></li>
id_token:請求一個身份令牌(只容許身份範圍)。
token:請求訪問令牌(只容許資源做用域)。
id_token token:請求身份令牌和訪問令牌。
清楚這個幾個概念後頁面上的操做按鈕就都比較好理解了。

 app.js中的配置以下:

var config = {
    authority: "http://localhost:5000/",
    client_id: "js",
    redirect_uri: window.location.origin + "/callback.html",
    post_logout_redirect_uri: window.location.origin + "/index.html",

    // if we choose to use popup window instead for logins
    popup_redirect_uri: window.location.origin + "/popup.html",
    popupWindowFeatures: "menubar=yes,location=yes,toolbar=yes,width=1200,height=800,left=100,top=100;resizable=yes",

    // these two will be done dynamically from the buttons clicked, but are
    // needed if you want to use the silent_renew
    response_type: "id_token token",
    //scope: "openid profile email api1 api2.read_only",
    scope: "openid profile api1",

    // this will toggle if profile endpoint is used
    loadUserInfo: false,

    // silent renew will get a new access_token via an iframe 
    // just prior to the old access_token expiring (60 seconds prior)
    silent_redirect_uri: window.location.origin + "/silent.html",
    automaticSilentRenew: true,

    // will revoke (reference) access tokens at logout time
    revokeAccessTokenOnSignout: true,

    // this will allow all the OIDC protocol claims to be visible in the window. normally a client app 
    // wouldn't care about them or want them taking up space
    filterProtocolClaims: false
};
Oidc.Log.logger = window.console;
Oidc.Log.level = Oidc.Log.DEBUG;

var mgr = new Oidc.UserManager(config);

mgr.events.addUserLoaded(function (user) {
    log("User loaded");
    showTokens();
});
mgr.events.addUserUnloaded(function () {
    log("User logged out locally");
    showTokens();
});
mgr.events.addAccessTokenExpiring(function () {
    log("Access token expiring..." + new Date());
    console.log("Access token expiring..." + new Date());
});
mgr.events.addSilentRenewError(function (err) {
    log("Silent renew error: " + err.message);
});
mgr.events.addUserSignedOut(function () {
    log("User signed out of OP");
    mgr.removeUser();
});

登陸完成後會請求GET /connect/checksession在頁面內追加iframe,iframe只包含js維護一系列的events,認證信息會存儲在Session Storage中

automaticSilentRenew: true

時會在設置的AccessTokenLifetime過時前AccessTokenExpiringNotificationTime(默認60秒)經過當前留存的id_token去請求新的身份認證信息。

GET /connect/authorize?client_id=js&redirect_uri=http%3A%2F%2Fwww.jsclient.com%2Fsilent.html&
response_type=id_token%20token&
scope=openid%20profile%20api1&
state=d814b897efc249a0b2028355f6ce80e8&
nonce=fb555249154b43eea816a122a34b0119&
prompt=none&
id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjAzMTZkNWQwYTViNTgyYzc0NmJlMmVhZWU1MTg1NzhiIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MTI3MDY2MTMsImV4cCI6MTUxMjcwNjkxMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoianMiLCJub25jZSI6IjUwM2YyY2VhNzE5ZjRlMjc5N2EzNThlZDlhYTI0ZDkyIiwiaWF0IjoxNTEyNzA2NjEzLCJhdF9oYXNoIjoiSUJVbTU1dFhEd3VKUTVJNlRJUGI2ZyIsInNpZCI6IjAwOGU2N2E2MWY2MzY1MjA1OTU5MTJmODQ2ZDdhNTVkIiwic3ViIjoiMiIsImF1dGhfdGltZSI6MTUxMjcwNjYxMiwiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.bJA7ejtzhjfobADoTpoNghhT19a7cnCBDGHSXvTOUpHwb_1To84l0Lu2pcGtDFeWqpWUTUKcXseNaao6bR41N45A_xCCfFsCHvSJ9pG_l-3NWIqceU-Zy-6JVH3MupbQLw3fm0Swt2NdfrjQ5G7q-e2WSXezHxXUvHbD2-PLqTG8_Qcotl0pK7lizGgdHiYF4SQM-qMLvTSOF0FHwkqiusxwrbDjR33UFYX7xlvjYRMoRvnw10qQlOQFO_0cox8BqxC-rW3EOu7mXyfz8TLyLqBIp9hlss0sayBEzxr-G_WFczmN5sL2xFpXK9dizhONAKUP7gpUjfrwy0CLiWefRg

AccessTokenLifetime設置的是90秒,因此Access token expiring...的時間就是在獲取新的id_token token信息後的31秒也就是過時前AccessTokenExpiringNotificationTime(默認60秒)。

 

 

獲取token過程解析

登陸獲取token的過程就參考上一篇。

相關文章
相關標籤/搜索