KnockoutJS + WebAPI 2 Token Authentication - 保持登录状态直到令牌过期

KnockoutJS + WebAPI 2 Token Authentication - maintain login state until token expires

我对基于令牌的身份验证还很陌生,我遇到了登录后如何保持登录状态的问题。 我想创建一个 SPA 网站,我在前端使用 Knockoutjs,在路由和更改视图时使用 SammyJS。 登录并获取令牌后,我将其存储在 localStorage 中,并将用户名设置为我正在显示的可观察对象。

我的问题是,在我关闭选项卡或浏览器并返回站点后,令牌位于 localStorage 中,但我看不到用户已登录。 我想保持登录状态直到令牌过期。我的问题是,当我进入站点时,我应该如何处理来自 localStorage 的令牌以保持该用户的登录状态?

我是否需要在启动时做一些事情 class 或检查该用户是否存在于数据库中?

提前致谢!

这是我的代码:

StartupAuth.cs

    [assembly: OwinStartup(typeof(EventHub.PL.WebUI.Startup))] namespace EventHub.PL.WebUI {
        public partial class Startup
        {
            public static OAuthAuthorizationServerOptions OAuthOptions { get;private set; }
            public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
            public const string TokenEndpointPath = "/api/token";
            public static string PublicClientId { get; private set; }
            // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
            public void ConfigureAuth(IAppBuilder app)
            {
                // Configure the db context and user manager to use a single instance per request
                app.CreatePerOwinContext(ApplicationDbContext.Create);
                app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
                // Enable the application to use a cookie to store information for the signed in user
                // and to use a cookie to temporarily store information about a user logging in with a third party login provider
                app.UseCookieAuthentication(new CookieAuthenticationOptions());
                app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
                OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
                // Configure the application for OAuth based flow
                PublicClientId = "self";
                OAuthOptions = new OAuthAuthorizationServerOptions
                {
                    TokenEndpointPath = new PathString(TokenEndpointPath),
                    Provider = new ApplicationOAuthProvider(PublicClientId),
                    //AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                    // In production mode set AllowInsecureHttp = false
                    AllowInsecureHttp = true
                };
                // Enable the application to use bearer tokens to authenticate users
                //app.UseOAuthBearerTokens( OAuthOptions );
                app.UseOAuthAuthorizationServer(OAuthOptions);
                app.UseOAuthBearerAuthentication(OAuthBearerOptions); 
            }
         }

AccountController.cs

[HttpPost]
        [AllowAnonymous]
        [Route("Login")]
        public async Task<IHttpActionResult> Login(LoginUser model)
        {
            var request = HttpContext.Current.Request;
            var tokenServiceUrl = request.Url.GetLeftPart(UriPartial.Authority) + request.ApplicationPath + "/api/Token";
            using (var client = new HttpClient())
            {
                var requestParams = new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("grant_type", "password"),
                    new KeyValuePair<string, string>("username", model.Email),
                    new KeyValuePair<string, string>("password", model.Password)
                };
                var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
                var tokenServiceResponse = await client.PostAsync(tokenServiceUrl, requestParamsFormUrlEncoded);
                var responseString = await tokenServiceResponse.Content.ReadAsStringAsync();
                var json = JsonConvert.DeserializeObject<TokenResponse>(responseString);
                var responseCode = tokenServiceResponse.StatusCode;
                if (responseCode == HttpStatusCode.OK)
                {
                    RegisterUser user = userRepository.GetNameById(json.Id);
                    var data = new
                    {
                        status = "success",
                        json.access_token,
                        user.Lastname
                    };
                    return Json(data);
                }
                return Json(new { status = "failed" });
            }
        }

这里是 KO 部分:

var LoginApp = function () {
    var instance = this;
    instance.mainViewModel = new MainViewModel();
    instance.loginViewModel = new LoginViewModel();
    instance.loginRepository = new LoginRepository();
    instance.loginViewModel.signIn = function() {
        $('.loader-header').show();
        var postData = {
            email: instance.loginViewModel.email(),
            password: instance.loginViewModel.password
        }
        instance.loginRepository.SignIn(SignInSuccess, postData);
    };
    instance.SignInSuccess = function(response) {
        if (response.status === 'success') {
            instance.mainViewModel.username(response.Lastname);
            instance.mainViewModel.isVisible(true);
            var userData = {
                token: response.access_token,
                username: response.Lastname
            };
            localStorage.setItem('AuthorizationData', JSON.stringify(userData));
            $('.loader-header').hide();
            dialog.close();
        } else {
            $('.loader-header').hide();
        }
    };
    instance.init = function () {
        ko.applyBindings(instance.loginViewModel, document.getElementById("signin-form"));
        ko.applyBindings(instance.mainViewModel, document.getElementById("main-wrapper"));
    }
    instance.init();
}
$(document).ready(function () {
    var loginApp = LoginApp();
});

更新

这也是我的路由

var appRoot = root;

(function ($) {
    var app = $.sammy('#page', function () {
        this.get('#/home', function (context) {
            document.title = 'Home - ' + title;
            var url = getUrlFromHash(context.path);
            loadView(url, new MainViewModel(), MainApp);
            //context.load(url).swap();
        });
        this.get('#/about', function (context) {
            var url = getUrlFromHash(context.path);
            loadView(url, new AboutViewModel(), AboutApp);
        });
        this.get('#/manage', function (context) {
            var url = getUrlFromHash(context.path);
            loadView(url, new AboutViewModel(), AboutApp);
        });
    });
    $(function () {
        app.run('#/home');
    });
})(jQuery);
function loadView(url, viewModel, callback) {
    $.get(url, function (response) {
        var $container = $('#page');
        //var $view = $('#page').html(response);
        $container.html(response);
        callback();
    });
}
function getUrlFromHash(hash) {
    var url = hash.replace('#/', '');
    if (url === appRoot)
        url = 'home';
    return url;
}

现在您所做的只是将用户的凭据存储在 localStorage 中,而不是使用它们来执行授权。一种替代方法是使用 Sammy.OAuth2 插件(您可以找到它 here)。

您可以定义一个路由来进行身份验证:

app.post("#/oauth/login", function(context) {
    this.load('http://yourwebsite/login',
        {
            cache: false, 
            type: 'post',
            data: {
                email: $("input[name=email]").val(), 
                password: $("input[name=password]").val()
            }
        })
        .then(function(content) {
            if(content != false){
                if(app.getAccessToken() == null){
                    app.setAccessToken(token());
                }
            }else{
                app.trigger("oauth.denied");
                return false;
            }
        });
});

在 'protected' 路由中,您可以检查用户是否已经登录,如下所示:

app.get("#/profile", function(context) {

    if(app.getAccessToken() != null)
        context.render('view/profile.template');
    else
        this.requireOAuth();

});

必须修改此示例以根据您的情况填充令牌。这是关于 Sammy.Oath2.

的完整教程