7

मैं पिछले हफ्ते एक मौजूदा MVC आवेदन के लिए एक API बनाने खर्च किया है, और अब के रूप में की जरूरत MVC ओर सुरक्षा दोबारा काम के साथ-साथ एपीआई सुरक्षित करने के लिए प्रयास कर रहा हूँ ।MVC पोस्ट प्राधिकरण हैडर खोने अनुरोध - कैसे एपीआई का उपयोग करने बियरर टोकन एक बार पुनः प्राप्त

वर्तमान में, एमवीसी एप्लिकेशन ओविन/ओथ/पहचान के माध्यम से एक एप्लिकेशन कुकी का उपयोग करने के लिए स्थापित किया गया है। मैंने बेयरर टोकन को शामिल करने का प्रयास किया है कि जब भी प्रतिबंधित एपीआई विधियों पर कॉल करने के लिए वेब एपीआई उत्पन्न करने के लिए सेट किया गया है, लेकिन अब तक बहुत कम सफलता मिली है - अनुरोधों को ठीक से काम करें, लेकिन POST अनुरोध प्राप्त होने पर प्राधिकरण शीर्षलेख खो रहे हैं एपीआई

मैंने एक एसडीके क्लाइंट बनाया है जिसका उपयोग एमवीसी ऐप द्वारा एपीआई को कॉल करने के लिए किया जा रहा है, और एपीआई को दिए गए किसी भी कॉल के लिए प्राधिकरण शीर्षलेख सेट करने की कुल तीन विधियों का प्रयास किया है, जिनमें से सभी प्राप्त अनुरोधों के लिए ठीक काम करते हैं, लेकिन किसी भी पोस्ट अनुरोध मैं बनाने की जरूरत के लिए पूरी तरह से असफल ...

मैं MVC नियंत्रक में अनुरोध हेडर सेट कर सकते हैं लगता है:

HttpContext.Request.Headers। जोड़ें ("प्रमाणीकरण", "बेयरर" + प्रतिक्रिया। एक्सेस टोकन);


मैं एसडीके क्लाइंट पर एक विस्तार विधि के माध्यम से अनुरोध हेडर सेट कर सकते हैं (जहां response.AccessToken पहले से एपीआई से लिया गया टोकन है):

_apiclient.SetBearerAuthentication (token.AccessToken)

या मैं एसडीके क्लाइंट पर मैन्युअल रूप से अनुरोध हेडर सेट कर सकते हैं:

_apiClient.Authentication = नया प्रमाणीकरण हैडरवैल्यू ("बेयरर, एक्सेस टोकन);

(कहाँ accessToken टोकन पहले से लिया गया, ग्राहक विधि को पारित कर दिया है बुलाया जा रहा है)।

इस बिंदु से मुझे इस मुद्दे से बहुत कम होना है कि इस मुद्दे के कारण क्या हो रहा है। एकमात्र चीज जो मैं अब तक हासिल करने में सक्षम हूं वह यह है कि एएसपी.नेट सभी POST अनुरोधों को पहले HTTP 100-जारी प्रतिक्रिया के लिए एक अपेक्षित शीर्षलेख के साथ अनुरोध में भेजने का कारण बनता है, जिसके बाद यह वास्तविक POST अनुरोध समाप्त कर देगा। हालांकि, ऐसा लगता है कि जब यह दूसरा अनुरोध करता है, तो प्राधिकरण शीर्षलेख अब मौजूद नहीं होता है और इसलिए एपीआई की अधिकृत विशेषता वास्तव में एपीआई विधि चलाने की बजाय 401-अनधिकृत प्रतिक्रिया का कारण बनती है।

तो, मैं बीयरर टोकन कैसे ले सकता हूं कि मैं एपीआई से पुनर्प्राप्त करने में सक्षम हूं, और इसके बाद के अनुरोधों पर इसका उपयोग करता हूं, जिसमें विभिन्न POST अनुरोध शामिल हैं जिन्हें मुझे बनाना होगा?

इसके अलावा, एमवीसी अनुप्रयोग पर इस टोकन को संग्रहीत करने का सबसे अच्छा तरीका क्या है? मैं उस एप्लिकेशन में प्रत्येक विधि के लिए स्ट्रिंग के चारों ओर पारित होने से बचना चाहूंगा, जिसकी आवश्यकता हो सकती है, लेकिन मैं यह भी पढ़ रहा हूं कि इसे कुकी में संग्रहीत करना सुरक्षा कारणों के लिए एक बहुत बुरा विचार है।

कुछ आगे अंक कि ब्याज की हो जाएगा तुरंत बाद मैं इस मुद्दे को पारित कर दिया हो:

का उपयोग कर करता OAuth बियरर टोकन का मतलब है कि मैं अब MVC आवेदन के लिए ApplicationCookies उपयोग कर सकते हैं?और/या यह पूरे कोड में निम्नलिखित कोड बेकार प्रस्तुत करेगा?

User.Identity.GetUserId()

वर्तमान में मैं अपने एपीआई बाहर टिप्पणी के लिए मजबूर कर रहा हूँ [अधिकृत] आदेश मेरा काम है, जो स्पष्ट रूप से आदर्श नहीं है के साथ जारी रखने के लिए जिम्मेदार बताते हैं, लेकिन यह अनुमति नहीं है मुझे अस्थायी रूप से चीजों के साथ मिलना है।

स्टार्टअप फ़ाइलों:

MVC:

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     ConfigureAuth(app); 
    } 

    private void ConfigureAuth(IAppBuilder app) 
    { 
     app.CreatePerOwinContext(ADUIdentityDbContext.Create); 
     app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); 

     app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions 
           { 
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
            //This should be set to FALSE before we move to production. 
            AllowInsecureHttp = true, 
            ApplicationCanDisplayErrors = true, 
            TokenEndpointPath = new PathString("/api/token"), 

           }); 

     app.UseCookieAuthentication(new CookieAuthenticationOptions 
            { 
             AuthenticationType = DefaultAuthenticationTypes.ExternalBearer, 
             CookieName = "ADU", 
             ExpireTimeSpan = TimeSpan.FromHours(2), 
             LoginPath = new PathString("/Account/Login"), 
             SlidingExpiration = true, 

            }); 
    } 
} 

एपीआई

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     HttpConfiguration config = new HttpConfiguration(); 

     config.DependencyResolver = new NinjectResolver(new Ninject.Web.Common.Bootstrapper().Kernel); 

     WebApiConfig.Register(config); 

     ConfigureOAuth(app); 
     app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); 

     app.UseWebApi(config); 
    } 

    public void ConfigureOAuth(IAppBuilder app) 
    { 
     app.CreatePerOwinContext(ADUIdentityDbContext.Create); 
     app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); 

     OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions() 
     { 
      AllowInsecureHttp = true, 
      TokenEndpointPath = new PathString("/api/token"), 
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
      Provider = new SimpleAuthorizationServerProvider(), 
     }; 

     //token generation 
     app.UseOAuthAuthorizationServer(oAuthServerOptions); 
     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 
    } 
} 


public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider 
{ 
    private IUserBusinessLogic _userBusinessLogic; 

    /// <summary> 
    /// Creates the objects necessary to initialize the user business logic field and initializes it, as this cannot be done by dependency injection in this case. 
    /// </summary> 
    public void CreateBusinessLogic() 
    { 
     IUserRepository userRepo = new UserRepository(); 
     IGeneratedExamRepository examRepo = new GeneratedExamRepository(); 
     IGeneratedExamBusinessLogic examBLL = new GeneratedExamBusinessLogic(examRepo); 
     _userBusinessLogic = new UserBusinessLogic(userRepo, examBLL); 
    } 

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); } 

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

     //create a claim for the user 
     ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); 
     identity.AddClaim(new Claim("sub", user.Id)); 
     context.Validated(identity); 
    } 
} 

उत्तर

1

समय की एक अच्छा सौदा समर्थक के अन्य पहलुओं पर काम करने के बाद जेक्ट, अन्य सुविधाओं को लागू करने से वास्तव में यह आसान हो गया है - अब एपीआई के हिस्से के रूप में एक प्रतिक्रिया व्रपर हैंडलर है, और उस हैंडलर का हिस्सा आने वाले अनुरोधों से सभी शीर्षकों को बचाता है और उन्हें आउटगोइंग प्रतिक्रियाओं में जोड़ता है। मेरा मानना ​​है कि यह 200-ओके अनुरोध शुरू में शुरू होने के बाद एप्लिकेशन के एएसपी.नेट एमवीसी पक्ष को प्राधिकरण शीर्षलेख भेजने के लिए अनुमति दे रहा है।

मैं आदेश भूमिकाओं का लाभ लेने के लिए मेरी प्रमाणीकरण में बदलाव कर दिया है, लेकिन मुझे लगता है कि कोड को बाहर करने के रूप में यह यहाँ प्रासंगिक नहीं होने चाहिए प्रयास करेगा:

MVC Startup.cs:

public class Startup 
{ 
    public void Configuration(IAppBuilder app) { ConfigureAuth(app); } 

    /// <summary> 
    ///  Configures authentication settings for OAuth. 
    /// </summary> 
    /// <param name="app"></param> 
    private void ConfigureAuth(IAppBuilder app) 
    { 
     app.CreatePerOwinContext(ADUIdentityDbContext.Create); 
     app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); 

     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 

     app.UseCookieAuthentication(new CookieAuthenticationOptions 
            { 
             AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
             CookieName = "ADU", 
             ExpireTimeSpan = TimeSpan.FromHours(2), 
             LoginPath = new PathString("/Account/Login"), 
             SlidingExpiration = true 
            }); 
    } 
} 

कहाँ यह प्रयोग किया जाता है (AccountController):

private async Task CreateLoginCookie(AuthorizationToken response, User result) 
    { 
     //Create the claims needed to log a user in 
     //(uses UserManager several layers down in the stack) 
     ClaimsIdentity cookieIdent = await _clientSDK.CreateClaimsIdentityForUser(response.AccessToken, result, true).ConfigureAwait(false); 

     if (cookieIdent == null) throw new NullReferenceException("Failed to create claims for cookie."); 
     cookieIdent.AddClaim(new Claim("AuthToken", response.AccessToken)); 

     AuthenticationProperties authProperties = new AuthenticationProperties(); 
     authProperties.AllowRefresh = true; 
     authProperties.IsPersistent = true; 
     authProperties.IssuedUtc = DateTime.Now.ToUniversalTime(); 

     IOwinContext context = HttpContext.GetOwinContext(); 
     AuthenticateResult authContext = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie); 

     if (authContext != null) 
      context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(cookieIdent, authContext.Properties); 

     //Wrapper methods for IOwinContext.Authentication.SignOut()/SignIn() 
     SignOut(); 
     SignIn(authProperties, cookieIdent); 
    } 

अपना SDK परत में, मैं एक विधि है कि मैं विभिन्न अन्य तरीकों मैं सेट करने के लिए मेरी एपीआई तक पहुंचने के लिए उपयोग करने से फोन बनाया प्रत्येक निवर्तमान अनुरोध के लिए प्राधिकरण (मैं यह पता लगाने की कैसे एक गुण में इस बनाने के लिए करना चाहते हैं, लेकिन मुझे लगता है कि बाद में के बारे में चिंता करता हूँ):

private void SetAuthentication() 
    { 
     ClaimsIdentity ident = (ClaimsIdentity)Thread.CurrentPrincipal.Identity; 
     Claim claim; 
     //Both of these methods (Thread.CurrentPrincipal, and ClaimsPrincipal.Current should work, 
     //leaving both in for the sake of example. 
     try 
     { 
      claim = ident.Claims.First(x => x.Type == "AuthToken"); 
     } 
     catch (Exception) 
     { 
      claim = ClaimsPrincipal.Current.Claims.First(x => x.Type == "AuthToken"); 
     } 

     _apiClient.SetBearerAuthentication(claim.Value); 
    } 

एपीआई Startup.cs

/// <summary> 
    ///  Configures the settings used by the framework on application start. Dependency Resolver, OAuth, Routing, and CORS 
    ///  are configured. 
    /// </summary> 
    /// <param name="app"></param> 
    public void Configuration(IAppBuilder app) 
    { 
     HttpConfiguration config = new HttpConfiguration(); 

     config.DependencyResolver = new NinjectResolver(new Bootstrapper().Kernel); 

     WebApiConfig.Register(config); 

     ConfigureOAuth(app); 
     app.UseCors(CorsOptions.AllowAll); 

     app.UseWebApi(config); 
    } 

    /// <summary> 
    ///  Configures authentication options for OAuth. 
    /// </summary> 
    /// <param name="app"></param> 
    public void ConfigureOAuth(IAppBuilder app) 
    { 
     app.CreatePerOwinContext(ADUIdentityDbContext.Create); 
     app.CreatePerOwinContext<ADUUserManager>(ADUUserManager.Create); 

     OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions 
                  { 
                   AllowInsecureHttp = true, 
                   TokenEndpointPath = new PathString("/api/token"), 
                   AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), 
                   Provider = new SimpleAuthorizationServerProvider() 
                  }; 

     //token generation 
     app.UseOAuthAuthorizationServer(oAuthServerOptions); 
     app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()); 
    } 

SimpleAuthorizationServerProvider .cs: ​​

/// <summary> 
    ///  Creates an access bearer token and applies custom login validation logic to prevent invalid login attempts. 
    /// </summary> 
    /// <param name="context"></param> 
    /// <returns></returns> 
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); 

     // Performs any login logic required, such as accessing Active Directory and password validation. 
     User user = await CustomLoginLogic(context).ConfigureAwait(false); 

     //If a use was not found, add an error if one has not been added yet 
     if((user == null) && !context.HasError) SetInvalidGrantError(context); 

     //Break if any errors have been set. 
     if (context.HasError) return; 

     //create a claim for the user 
     ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); 

     //Add some basic information to the claim that will be used for the token. 
     identity.AddClaim(new Claim("Id", user?.Id)); 
     identity.AddClaim(new Claim("TimeOf", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString())); 

     //Roles auth 
     SetRoleClaim(user, ref identity); 

     context.Validated(identity); 
    } 

और अंत में, स्पष्ट कुंजी एक साथ सब कुछ लपेटता है कि:

public class ResponseWrappingHandler : DelegatingHandler 
{ 
    /// <summary> 
    /// Catches the request before processing is completed and wraps the resulting response in a consistent response wrapper depending on the response returned by the api. 
    /// </summary> 
    /// <param name="request">The request that is being processed.</param> 
    /// <param name="cancellationToken">A cancellation token to cancel the processing of a request.</param> 
    /// <returns></returns> 
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     HttpResponseMessage response = await base.SendAsync(request, cancellationToken); 

     //Calls Wrapping methods depending on conditions, 
     //All of the Wrapping methods will make a call to PreserveHeaders() 
    } 

    /// <summary> 
    /// Creates a response based on the provided request with the provided response's status code and request headers, and the provided response data. 
    /// </summary> 
    /// <param name="request">The original request.</param> 
    /// <param name="response">The reqsponse that was generated.</param> 
    /// <param name="responseData">The data to include in the wrapped response.</param> 
    /// <returns></returns> 
    private static HttpResponseMessage PreserveHeaders(HttpRequestMessage request, HttpResponseMessage response, object responseData) 
    { 
     HttpResponseMessage newResponse = request.CreateResponse(response.StatusCode, responseData); 

     foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers) 
      newResponse.Headers.Add(header.Key, header.Value); 

     return newResponse; 
    } 

इसके साथ ही मेरी परियोजना अब क्लाइंट रहस्यों की आवश्यकता के बिना प्राधिकरण/प्रमाणीकरण का उपयोग करने में सक्षम है (जैसे कि मेरे नियोक्ता के लक्ष्यों में से एक था)।

संबंधित मुद्दे