ASP.NET - 跨 Server 認證登入跳轉方式

在網站建置時,我們可能將不同子系統建在不同 Server 上,但每個子系統之間又需要可以互相認證,這時候就要在每個子系統上實作跨 Server 的認證。一般方式是在主入口網站登入後取得 Token,再進入子系統頁面時將 Token 與帳號傳給子系統進行認證,認證 OK 後就直接跳轉到子系統頁面,反之就跳轉回原頁面。


限量一開始實作跨 Server 認證機制時,原本想法是子系統提供一個API,在原頁面利用 AJAX POST 呼叫該 API 將帳號與Token傳入,驗證之後會將結果回傳至原頁,原頁接收到結果後判斷是否 OK,如果 OK 就跳轉,反之就顯示錯誤訊息。這種方式乍看之下蠻合理的,但其實是大有問題的。以下先來看看錯誤寫法:

子系統認證 API


[HttpPost]
public ApiResult SignInFromOutside(string cId, string acNo, string token)
{
    var result = new ApiResult();

    try
    {
        // 呼叫認證模組驗證身分
        if (Kernel.Authenticator.SignInFromOutside(cId, acNo, token))
        {
            result.Message = "登入成功";
        }
        else
        {
            throw new Exception("登入失敗");
        }
    }
    catch (Exception ex)
    {
        result.SetError(ex);
    }

    return result;
}

原頁呼叫


$(function () {
    $('#login').click(function () {
        $.ajax({
            url: 'http://www.limitedcode.com/User/SignInFromOutside',
            type: 'POST',
            dataType: 'json',
            data: {
                cId: $('#idno').val(),
                acNo: $('#acno').val(),
                token: $('#token').val()
            },
            success: function (json) {
                if (!json.Success) {
                    alert('登入失敗');
                    return;
                } else {
                    location.href = '/Home/Index';
                }
            }
        });
    });
});

程式是沒有問題的,但問題來了,執行登入後結果顯示OK但是竟然會回到登入頁面。到底問題是什麼呢?

問題就是我們在認證模組認證過後會將認證相關資料寫入 SesssionCookie,藉此使用者可在子系統內的頁面通行無阻。可是使用錯誤寫法時,認證模組的確有寫入Cookie與Session,但是因為是AJAX Request,所有權最後會回到原頁的 Server上,因此剛剛寫入的Cookie無法記錄在 Browser上,所以原頁收到結果 OK 導向目標子系統頁面時,對於子系統頁面來說屬於第一次進入,所以就重開一個 Session,想當然 Cookie 與 Session都沒有囉,認證當然會失敗而導致登入頁。

解決方法:
因為看起來是 Cookie 與 Session 的問題,那麼限量想的方式就是一開始就導到子系統開放給外部的一個空的頁面,對於子系統 Server 來說就是從這一個 Session 內進行驗證,這樣成功後就可以直接繼續到目標的子系統頁面,完全在同一個 Session 裡。但是失敗呢?失敗當然是要導回原頁面,所以原頁面要傳當前的網址給子系統。程式碼如下:

子系統認證API


[HttpGet]
public ActionResult RedirectETradeNew(string cId, string acNo, string token, string returnUrl)
{
    // 呼叫認證模組驗證身分
    if (!Kernel.Authenticator.SignInFromOutside(cId, acNo, token))
    {
        return Redirect(returnUrl);
    }
 
    return RedirectToAction("Index", "Home");
}

原頁呼叫


$(function () {
    $('#login').click(function () {
        var params = {
            cId: $('#idno').val(),
            acNo: $('#acno').val(),
            token: $('#token').val(),
            returnUrl: location.href
        };

        location.href = 'http://www.limitedcode.com/User/RedirectETradeNew?' + $.param(params);
    });
});


總算解決了,這樣就可以在各子系統暢行無阻了。




留言