30

Tôi đang phát triển API REST trong API Web ASP.Net. API của tôi sẽ chỉ có thể truy cập được thông qua các ứng dụng không dựa trên trình duyệt. Tôi cần phải thực hiện bảo mật cho API của mình vì vậy tôi quyết định đi với xác thực dựa trên Token. Tôi có một sự hiểu biết công bằng về xác thực dựa trên mã thông báo và đã đọc một vài hướng dẫn, nhưng tất cả đều có một số giao diện người dùng để đăng nhập. Tôi không cần bất kỳ giao diện người dùng nào để đăng nhập vì chi tiết đăng nhập sẽ được khách hàng chuyển qua HTTP POST sẽ được ủy quyền từ cơ sở dữ liệu của chúng tôi. Làm cách nào để triển khai xác thực dựa trên mã thông báo trong API của tôi? Xin lưu ý - API của tôi sẽ được truy cập ở tần suất cao vì vậy tôi cũng phải chăm sóc hiệu suất. Vui lòng cho tôi biết nếu tôi có thể giải thích rõ hơn.Xác thực dựa trên mã thông báo trong API Web mà không có bất kỳ giao diện người dùng nào

+1

ai đó, một nơi nào đó sẽ phải đặt tên người dùng và mật khẩu vào để thực hiện xác thực ban đầu; bạn có gợi ý rằng bất kỳ ai nhận được bản sao ứng dụng của bạn sẽ sử dụng cùng một tên người dùng và mật khẩu không? và nếu đó là trường hợp, bạn có ý định mã cứng các giá trị tên người dùng và mật khẩu trong mã của bạn không? – Claies

+0

Tôi có thể có nhiều người dùng đã đăng ký, vì vậy chi tiết đăng nhập ban đầu sẽ được họ thông qua qua HTTP POST. Tiếp theo là gì? –

+0

không có ý nghĩa gì. làm thế nào bạn có thể máy chủ vượt qua thông tin đăng nhập cho khách hàng của bạn? máy chủ phải biết máy khách nào là máy chủ nào? – Claies

Trả lời

36

Tôi nghĩ có một số nhầm lẫn về sự khác biệt giữa MVC và Web Api. Trong ngắn hạn, cho MVC bạn có thể sử dụng một hình thức đăng nhập và tạo một phiên sử dụng cookie. Đối với Api Web không có phiên. Đó là lý do tại sao bạn muốn sử dụng mã thông báo.

Bạn không cần biểu mẫu đăng nhập. Điểm cuối Token là tất cả những gì bạn cần. Giống như Win mô tả bạn sẽ gửi thông tin đăng nhập đến điểm cuối mã thông báo nơi nó được xử lý.

Dưới đây là một số mã phía khách hàng C# để nhận mã:

//using System; 
    //using System.Collections.Generic; 
    //using System.Net; 
    //using System.Net.Http; 
    //string token = GetToken("https://localhost:<port>/", userName, password); 

    static string GetToken(string url, string userName, string password) { 
     var pairs = new List<KeyValuePair<string, string>> 
        { 
         new KeyValuePair<string, string>("grant_type", "password"), 
         new KeyValuePair<string, string>("username", userName), 
         new KeyValuePair<string, string> ("Password", password) 
        }; 
     var content = new FormUrlEncodedContent(pairs); 
     ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; 
     using (var client = new HttpClient()) { 
      var response = client.PostAsync(url + "Token", content).Result; 
      return response.Content.ReadAsStringAsync().Result; 
     } 
    } 

Để sử dụng token thêm nó vào header của yêu cầu:

//using System; 
    //using System.Collections.Generic; 
    //using System.Net; 
    //using System.Net.Http; 
    //var result = CallApi("https://localhost:<port>/something", token); 

    static string CallApi(string url, string token) { 
     ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; 
     using (var client = new HttpClient()) { 
      if (!string.IsNullOrWhiteSpace(token)) { 
       var t = JsonConvert.DeserializeObject<Token>(token); 

       client.DefaultRequestHeaders.Clear(); 
       client.DefaultRequestHeaders.Add("Authorization", "Bearer " + t.access_token); 
      } 
      var response = client.GetAsync(url).Result; 
      return response.Content.ReadAsStringAsync().Result; 
     } 
    } 

đâu Token:

//using Newtonsoft.Json; 

class Token 
{ 
    public string access_token { get; set; } 
    public string token_type { get; set; } 
    public int expires_in { get; set; } 
    public string userName { get; set; } 
    [JsonProperty(".issued")] 
    public string issued { get; set; } 
    [JsonProperty(".expires")] 
    public string expires { get; set; } 
} 

Hiện tại cho phía máy chủ:

Trong Startup.Auth.cs

 var oAuthOptions = new OAuthAuthorizationServerOptions 
     { 
      TokenEndpointPath = new PathString("/Token"), 
      Provider = new ApplicationOAuthProvider("self"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), 
      // https 
      AllowInsecureHttp = false 
     }; 
     // Enable the application to use bearer tokens to authenticate users 
     app.UseOAuthBearerTokens(oAuthOptions); 

Và trong ApplicationOAuthProvider.cs mã mà thực sự thẩm quyền cấp hoặc từ chối truy cập:

//using Microsoft.AspNet.Identity.Owin; 
//using Microsoft.Owin.Security; 
//using Microsoft.Owin.Security.OAuth; 
//using System; 
//using System.Collections.Generic; 
//using System.Security.Claims; 
//using System.Threading.Tasks; 

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider 
{ 
    private readonly string _publicClientId; 

    public ApplicationOAuthProvider(string publicClientId) 
    { 
     if (publicClientId == null) 
      throw new ArgumentNullException("publicClientId"); 

     _publicClientId = publicClientId; 
    } 

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 

     var user = await userManager.FindAsync(context.UserName, context.Password); 
     if (user == null) 
     { 
      context.SetError("invalid_grant", "The user name or password is incorrect."); 
      return; 
     } 

     ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager); 
     var propertyDictionary = new Dictionary<string, string> { { "userName", user.UserName } }; 
     var properties = new AuthenticationProperties(propertyDictionary); 

     AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); 
     // Token is validated. 
     context.Validated(ticket); 
    } 

    public override Task TokenEndpoint(OAuthTokenEndpointContext context) 
    { 
     foreach (KeyValuePair<string, string> property in context.Properties.Dictionary) 
     { 
      context.AdditionalResponseParameters.Add(property.Key, property.Value); 
     } 
     return Task.FromResult<object>(null); 
    } 

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
    { 
     // Resource owner password credentials does not provide a client ID. 
     if (context.ClientId == null) 
      context.Validated(); 

     return Task.FromResult<object>(null); 
    } 

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) 
    { 
     if (context.ClientId == _publicClientId) 
     { 
      var expectedRootUri = new Uri(context.Request.Uri, "/"); 

      if (expectedRootUri.AbsoluteUri == context.RedirectUri) 
       context.Validated(); 
     } 
     return Task.FromResult<object>(null); 
    } 

} 

Như bạn có thể thấy không có điều khiển liên quan đến lấy token. Trong thực tế, bạn có thể loại bỏ tất cả các tài liệu tham khảo MVC nếu bạn muốn một Api Web chỉ. Tôi đã đơn giản hóa mã phía máy chủ để làm cho nó dễ đọc hơn. Bạn có thể thêm mã để nâng cấp bảo mật.

Đảm bảo bạn chỉ sử dụng SSL. Thực hiện RequireHttpsAttribute để ép buộc điều này.

Bạn có thể sử dụng thuộc tính Authorize/AllowAnonymous để bảo mật Api Web của mình. Ngoài ra, bạn có thể thêm bộ lọc (như RequireHttpsAttribute) để làm cho Api Web của bạn an toàn hơn. Tôi hi vọng cái này giúp được.

+0

u đã nói "cho MVC bạn có thể sử dụng biểu mẫu đăng nhập và tạo phiên sử dụng cookie. Đối với Api Web không có phiên" nhưng biểu mẫu xác thực có thể được triển khai trong api trên web. vì vậy khách hàng có thể gửi các thông tin đăng nhập để api web và api web sẽ phát hành cookie auth cho khách hàng. cho tất cả các ứng dụng khách gọi tiếp theo phải chuyển cookie xác thực tới trang web api ...... tôi đoán điều này là có thể. –

+0

chọn mã của bạn như sau. 'KeyValuePair mới (" grant_type "," password "), KeyValuePair mới (" username ", userName), KeyValuePair mới (" Mật khẩu ", mật khẩu) ' ' grant_type = password? ' tùy chọn nào khác chúng tôi có thể sử dụng với' grant_type thay vì mật khẩu'? hãy chia sẻ kiến ​​thức. cảm ơn –

11

API Web ASP.Net đã tích hợp sẵn Máy chủ ủy quyền. Bạn có thể nhìn thấy nó bên trong Startup.cs khi bạn tạo một ứng dụng Web ASP.Net mới với mẫu Web API.

OAuthOptions = new OAuthAuthorizationServerOptions 
{ 
    TokenEndpointPath = new PathString("/Token"), 
    Provider = new ApplicationOAuthProvider(PublicClientId), 
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"), 
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14), 
    // In production mode set AllowInsecureHttp = false 
    AllowInsecureHttp = true 
}; 

Tất cả những gì bạn phải làm là đăng tên người dùng và mật khẩu được mã hóa URL bên trong chuỗi truy vấn.

/Token/userName=johndoe%40example.com&password=1234&grant_type=password 

Nếu bạn muốn biết chi tiết hơn, bạn có thể xem User Registration and Login - Angular Front to Back with Web API by Deborah Kurata.

+0

Vì vậy, tôi sẽ tạo một yêu cầu POST đến/TOKEN với tên người dùng và mật khẩu trong Tiêu đề/Cơ thể HTTP? Tôi sẽ có tên người dùng và mật khẩu băm cho tất cả người dùng trong cơ sở dữ liệu ứng dụng của mình. Làm thế nào tôi nên thực hiện điều này? –

+0

Bạn cần ASP.Net Identity * (Tôi tin rằng bạn đã có) *. Nếu không, hãy tạo một dự án ASP.Net Web API và xem mã nguồn. – Win

+1

grant_type = mật khẩu là gì? hãy chia sẻ kiến ​​thức. cảm ơn –

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