2016-10-01 13 views
5

Tôi đã đọc (và xem video) về cách thực hiện tốt nhất mã thông báo truy cập và làm mới khi sử dụng ứng dụng MVC Core với nhiều cuộc gọi ajax trong đó. Tôi nghĩ tôi đã đúng nhưng chỉ muốn biết liệu có cách nào tốt hơn để làm điều này không. Tôi sẽ chỉnh sửa bài đăng này để nó có thể phục vụ như là một tài liệu tham khảo cho bất cứ ai tìm kiếm thông tin này.Thực hành tốt nhất mã thông báo truy cập trong ứng dụng MVC Core chứa cả chế độ xem và tài nguyên, sử dụng Identity Server 4

Thiết lập của tôi: Tôi có ứng dụng MVC Core với nhiều JavaScript. JavaScripts đang sử dụng các cuộc gọi ajax để truy xuất các hành động json hoặc gọi.

Vì tôi không muốn người dùng của mình có thể truy cập api bằng cách sử dụng xác thực cookie, tôi đang sử dụng app.Map để chia ứng dụng của tôi thành hai phần. Một nơi mà người dùng có thể truy cập vào Chế độ xem bằng mã thông báo nhận dạng và một mã thông báo sẽ yêu cầu mã thông báo truy cập. Tôi cũng đang thêm một cookie để giữ thời gian khi tôi cần làm mới mã thông báo truy cập của mình.

Startup.cs (Tôi đã gỡ bỏ những phần đó không phải là importent)

app.UseCookieAuthentication(new CookieAuthenticationOptions 
    { 
    AuthenticationScheme = "Cookies", 
    AutomaticAuthenticate = true, 
    ExpireTimeSpan = TimeSpan.FromMinutes(60) 
    }); 

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 

    var oidcOptions = new OpenIdConnectOptions 
    { 
    AuthenticationScheme = "oidc", 
    SignInScheme = "Cookies", 

    Authority = LoginServerUrl, 
    RequireHttpsMetadata = false, 
    ClientId = "MyApp", 
    ClientSecret = "*****", 
    ResponseType = "code id_token", 
    SaveTokens = true, 
    Events = new OpenIdConnectEvents() 
    { 
     OnTicketReceived = async notification => 
     { 
     notification.Response.Cookies.Append("NextAccessTokenRefresh", DateTime.Now.AddMinutes(30).ToString()); 
     notification.Response.Cookies.Delete("AccessToken"); 
     }, 
    }, 

    TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters 
    { 
     NameClaimType = JwtClaimTypes.Name, 
     RoleClaimType = JwtClaimTypes.Role, 
    }, 
    }; 

    oidcOptions.Scope.Clear(); 
    oidcOptions.Scope.Add("openid"); 
    oidcOptions.Scope.Add("roles"); 
    oidcOptions.Scope.Add("offline_access"); 

    app.UseOpenIdConnectAuthentication(oidcOptions); 

    app.Map("/api", (context) => 
    { 
    var bearerTokenOptions = new IdentityServerAuthenticationOptions 
    { 
     AuthenticationScheme = "Bearer", 
     Authority = LoginServerUrl,, 
     RequireHttpsMetadata = false, 
     ScopeName = "MyApi", 
     AutomaticAuthenticate = true 
    }; 

    context.UseIdentityServerAuthentication(bearerTokenOptions); 
    context.UseMvcWithDefaultRoute(); 
    }); 

Tất cả ajax gọi để điều khiển các hành động được thực hiện bằng cách sử dụng url sau/api/[điều khiển]/[Hành động].

Tôi không muốn api của mình có thể truy cập được bằng mã thông tin nhận dạng để tôi cũng thêm thuộc tính Authorize (ActiveAuthenticationSchemes = "Bearer") vào hành động điều khiển. Vì vậy bây giờ hành động điều khiển của tôi rằng đang nhận được gọi bằng javascript trông như thế này:

[HttpPost, Authorize(ActiveAuthenticationSchemes = "Bearer")] 
public async Task<JsonResult> DoStuff() 
{ 
} 

Khi một javascript cần phải truy cập vào một tài nguyên api, các ứng dụng điều khiển đầu tiên retrives access token và tiêm nó vào javascript sử dụng một tùy chỉnh javascript init phương pháp.

Phương pháp C# này có trách nhiệm làm mới và truy xuất cookie truy cập.

public async Task<string> GetAccessTokenAsync() 
{ 
    var accessToken = _contextAccessor.HttpContext.Request.Cookies["AccessToken"]; 
    var nextAccessTokenRefresh = _contextAccessor.HttpContext.Request.Cookies["NextAccessTokenRefresh"]; 
    if (string.IsNullOrEmpty(nextAccessTokenRefresh) || string.IsNullOrEmpty(accessToken) || DateTime.Parse(nextAccessTokenRefresh) <= DateTime.Now) 
    { 
    var refreshToken = await _contextAccessor.HttpContext.Authentication.GetTokenAsync("refresh_token"); 
    var tokenClient = new TokenClient(_appSettings.LoginServerUrl + "/connect/token", _appSettings.LoginClientId, _appSettings.LoginClientSecret); 
    var response = await tokenClient.RequestRefreshTokenAsync(refreshToken); 
    accessToken = response.AccessToken; 

    //Set cookies for next refresh 
    _contextAccessor.HttpContext.Response.Cookies.Append("NextAccessTokenRefresh", DateTime.Now.AddMinutes(30).ToString()); 
    _contextAccessor.HttpContext.Response.Cookies.Append("AccessToken", response.AccessToken); 
    } 

    return accessToken; 
} 

Trên tất cả các $ .ajax của tôi, tôi đã thêm các thông số sau:

beforeSend: function(xhr, settings) { xhr.setRequestHeader('Authorization','Bearer ' + accessToken); } 

Thats nó. Thời gian hết hạn mã thông báo truy cập mặc định là một giờ. Tôi luôn làm mới sau nửa thời gian đó.

Bây giờ để câu hỏi của tôi:

  1. Tôi có thể cải thiện mã của tôi bằng cách nào?
  2. Bạn có thấy bất kỳ rủi ro liên quan đến bảo mật nào theo cách này không?
  3. Tôi có thể truy xuất mã thông báo truy cập trong OnTicketĐược nhận không?
+1

Tôi bỏ phiếu để đóng câu hỏi này như off-topic vì nó được yêu cầu xem xét lại mã; các đánh giá mã không có chủ đề. Bạn cần phải hỏi tại http://codereview.stackexchange.com/ –

+0

Tôi biết nó là một chút tắt chủ đề, nhưng ALOT của những người sử dụng ID4 đang tìm kiếm thông tin này. Tôi thấy cùng một câu hỏi xuất hiện trên nhiều diễn đàn khác nhau vì vậy tôi nghĩ tôi sẽ làm một bài đăng ở đây để trả lời tất cả. Tôi có thể tạo một bài đăng trên blog, nhưng các nhà phát triển đang tìm kiếm thông tin tại đây. Không nên SO là nơi bạn tìm thấy loại thông tin này? –

+0

Sau đó, bạn nên thử cụm từ theo cách khác để bạn 1) không yêu cầu xem xét mã, 2) không yêu cầu các phương pháp hay nhất (thường chỉ là 'chủ yếu dựa trên ý kiến'). –

Trả lời

3

Tôi có thể cải thiện mã của mình bằng bất kỳ cách nào không?

bạn Startup.cs nên một cái gì đó như thế này (vì Map công trình chỉ '/ api' con đường Để biết thêm thông thấy https://docs.asp.net/en/latest/fundamentals/middleware.html#run-map-and-use.):

app.MapWhen(context => !context.Request.Path.Value.StartsWith("/api"), builder=> 
{ 
    app.UseCookieAuthentication(options); 
    ... 
    app.UseOpenIdConnectAuthentication(oidcOptions); 
    .... 
}); 

app.MapWhen(context => context.Request.Path.Value.StartsWith("/api"), builder=> 
{ 
    var bearerTokenOptions = new IdentityServerAuthenticationOptions 
    { 
     AuthenticationScheme = "Bearer", 
     Authority = LoginServerUrl,, 
     RequireHttpsMetadata = false, 
     ScopeName = "MyApi", 
     AutomaticAuthenticate = true 
    }; 

    context.UseIdentityServerAuthentication(bearerTokenOptions); 
    context.UseMvcWithDefaultRoute(); 
}); 

điểm Thứ hai, cookie của bạn hết hạn thời gian là 60 phút, trong trường hợp này, thời gian sử dụng mã thông báo làm mới của bạn sẽ là 60 phút. Tôi nghĩ rằng đây có thể là một vấn đề.

Bạn có thấy bất kỳ rủi ro liên quan đến bảo mật nào xảy ra theo cách này không?

Tôi chưa có đủ kinh nghiệm để sử dụng mã thông báo làm mới, vì vậy tôi không thể nói rằng triển khai của bạn có an toàn hay không. Nhưng tôi nghĩ rằng làm mới token (để thực hiện của bạn) làm tăng phức tạp cũng rủi ro bảo mật (đây chỉ là ý kiến ​​của tôi và tôi không phải là một chuyên gia bảo mật). Tôi sẽ chỉ sử dụng mã thông báo truy cập lâu dài với luồng ngầm (vì tính đơn giản).

Tôi có thể truy xuất mã thông báo truy cập trong OnTicket được nhận không?

Vâng, bạn có thể:

OnTicketReceived = ctx => 
{ 
    var token = ctx.Ticket.Properties.GetTokenValue("access_token"); 
    ... 
} 
+0

Cảm ơn bạn đã có câu trả lời tuyệt vời. Tôi sẽ thay đổi các đường dẫn bản đồ để cụ thể hơn và cũng lưu trữ các thẻ trong cookie trong OnTicketReceived. Điều này tiết kiệm một chuyến đi vòng đến máy chủ. –

+0

Tôi đã suy nghĩ về việc sử dụng mã thông báo truy cập lâu, nhưng tôi hy vọng tôi có thể làm cho nó an toàn hơn bằng cách làm mới các thẻ khi cần thiết mà không cần tương tác của người dùng. Đó là một chút phức tạp hơn nhưng vì tôi chỉ lưu trữ "bí mật của khách hàng" trên máy chủ của tôi, tôi nghĩ rằng nó sẽ được sử dụng tốt bằng cách sử dụng thẻ làm mới. –

+0

Sử dụng mã thông báo truy cập lâu dài chỉ là sở thích của tôi (tôi không thể nói sử dụng nó). Tôi tránh sự phức tạp càng nhiều càng tốt và trong sự phức tạp của tôi là một nguy cơ bảo mật. –

Các vấn đề liên quan