在自訂AuthorizeAttribute中讀取存放在url中的access_token做驗證

在公司遇到一個需求,同事希望能夠從A專案透過連結開啟B專案裡某隻程式時,可以不用再登入一次。我第一個想法是做SSO,但公司目前的開發框架並沒有相關功能,加上專案有時程壓力,最後決定使用token驗證來跳過B專案的登入步驟。

在動手之前查了許多資料,幾乎都不建議把access_token放在url中傳送,因為放在url的話很容易在傳送的過程中將token外洩。目前是先將token的有效期限設的很短,之後有時間的話會考慮把token改為一次性驗證。

token產生的部分我是使用ASP.NET OWIN Bearer Token,只要在Controller加上authorize attribute,再把token塞進request header中,系統就會幫你驗證了。但這次是要用連結開啟,window.open()沒辦法把token塞進header。雖然有查到可以用jquery的get去把html取回來後塞進新視窗,但我不打算這麼做,最後決定從站台驗證的那段下手。

原本是希望能找到能改抓url中access_token的設定,但找了一陣子都沒看到相關的設定,只好寫個自訂的authorize attribute。

    public class UrlTokenAuthorize : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            //取得url中的access_token
            var token = httpContext.Request.Params.Get("access_token");

            if (!string.IsNullOrEmpty(token))
            {
                //將token解密
                var ticket = Startup.OAuthOptions.AccessTokenFormat.Unprotect(token);

                //檢查token是否過期
                if (ticket.Properties.ExpiresUtc?.DateTime < DateTime.Now.ToUniversalTime())
                {
                    return false;
                }

                //手動將token塞進httpContext中,此時IsAuthenticated會變為true
                httpContext.User = new ClaimsPrincipal(ticket.Identity);
            }

            return httpContext.User.Identity.IsAuthenticated;
        }
    }

參考資料: