2017-04-03 130 views
1

我试图设置一个WebAPIMVC5/Web Api 2,它目前使用默认配置和其他默认值。当我使用浏览器中的帐户登录时,我可以使用[Authorize]属性来正常执行MVC控制器操作,例如主页显示为授权时应该显示的内容,但如果我访问/api/Me(默认的内置浏览器)在API控制器动作)或任何定制的API控制器动作,我用默认的MVC网页API 2脚手架需要授权的建成,我得到一个错误,像这样:WebAPI [授权]登录时返回错误

{"message":"Authorization has been denied for this request."} 

这是当我想要它微软Edge,我还没有尝试过我的实际客户端代码,这是一个UWP应用程序。我想我会先在浏览器中进行测试,以确保正常工作。

我开始看这篇文章:https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/individual-accounts-in-web-api它似乎更适合于Ajax请求和SPAs虽然。我的目标是通过网络和UWP进行工作。我可能会把更多的时间放在UWP方面,而不是用ajax开发一个健壮的web应用程序,因为应用程序将运行在Intranet中并访问运行web api的Intranet IIS服务器,我想在UWP中构建桌面客户端,最终Xamarin通过Web Api访问数据。

是否安全,我的假设,如果您使用的是常规的网络浏览器,如边缘时,他们通过[Authorize]属性确保您无法访问网络API控制器动作,因为它没有在发送一个访问令牌头?

+0

那么约翰,你是如何将授权令牌从客户端发送到授权操作的?无论是哪个客户端,MVC或UWP或Javascript客户端,您需要使用HttpClient(MVC,UWP)或使用ajax,jquery等将Authorization标头中的标记发送到您的api,如果javascript客户端 – Jinish

+0

您可以测试您的授权端点使用代理工具(如邮递员或Fiddler)创建请求时,添加带有令牌的授权标头 – Jinish

+0

所以现在在我的UWP应用程序中,我在/ Account/Login上进行GET,以便首先获取页面我可以获得防伪标记。我通过Regex解析了这个问题,因为我很难在UWP中找到一个Html DOM库(没有去第三方),除非我找错了位置。在那一点上,我仍然需要得到一个不记名的令牌,我想用Authorize命中Web API,但我不知道该怎么做。 –

回答

2

所以从你的UWP应用程序,你可以创建一些方法,如下面,或者可以将它们包装在可以通过DI(您的选择)注入的类中。

public async Task<TResult> GetAsync<TResult>(string uriString) where TResult : class 
    { 
     var uri = new Uri(uriString); 
     using (var client = GetHttpClient()) 
     { 
      HttpResponseMessage response = await client.GetAsync(uri); 
      if (response.StatusCode != HttpStatusCode.OK) 
      { 
       //Log.Error(response.ReasonPhrase); 
       return default(TResult); 
      } 
      var json = await response.Content.ReadAsStringAsync(); 
      return JsonConvert.DeserializeObject<TResult>(json, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 
     } 
    } 

    public async Task<TResult> PostAsync<TResult, TInput>(string uriString, TInput payload = null) where TInput : class 
    { 
     var uri = new Uri(uriString); 
     using (var client = GetHttpClient()) 
     { 
      var jsonContent = JsonConvert.SerializeObject(payload, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); 
      HttpResponseMessage response = await client.PostAsync(uri, new StringContent(jsonContent, Encoding.UTF8, "application/json")); 
      if (response.StatusCode != HttpStatusCode.OK) 
      { 
       //Log.Error(response.ReasonPhrase); 
       return default(TResult); 
      } 
      var json = await response.Content.ReadAsStringAsync(); 
      return JsonConvert.DeserializeObject<TResult>(json); 
     } 
    } 

如果你使用的是基本信息(用户名和密码验证),那么你GetHttpClient方法是这样的:

private HttpClient GetHttpClient() 
    { 
     var client = new HttpClient(); 
     var username = // get username; 
     var password = // get password; 
     client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
     return client; 
    } 

,或者如果您使用的是承载令牌,那么你可以这样做:

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer ", "string token goes here..."); 
+0

我还没有在UWP平台上测试过这段代码,但我相信你可以访问HttpClient类。所以你可以做类似的事情。您可能需要找到相应的类 – Jinish

+0

谢谢,虽然获得令牌仍然有困难。因此,在Postman中,我尝试使用用户名和密码以及文章中提到的grant_type =“password”作为x-www-form-urlencoded指向http:// localhost:55970/Token。我找回了一个带有{“error”:“invalid_client”}的json。我不确定我需要设置什么才能识别客户端。 –

+0

对不起,延迟回复。一直很忙。您是否尝试调试授权提供程序代码? – Jinish

0

我在这里发布了一些更多的补充信息,以供任何可能在这个问题上挣扎的人想要从像UWP或Xamarin这样的客户端访问MVC5/Web Api 2,但您需要使用Authorize属性锁定Web Api的区域。

高级别的过程不是通过任何MVC控制器,而是直接向/ Token端点(或任何指定的端点)执行POST。

第一件事,第一,如果你想要做发展SSL,但没有一个证书呢,继续前进,运行以下命令:

C:\Program Files (x86)\IIS Express>iisexpressadmincmd setupSslUrl -url:https://localhost:55970/ -UseSelfSigned 

其中55970是无论是本地网站端口的端口是的,我将在我的文本中提到这一点。

接下来要做的事情是确保并覆盖ApplicationOAuthProvider.cs上的客户端验证和资源授予方法,否则会遇到无效的客户端或无效的授予错误。

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

     UserManager<ApplicationUser> _userManager; 
     ApplicationDbContext db = new ApplicationDbContext(); 
     _userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db)); 
     try 
     { 

      ApplicationUser user = await _userManager.FindAsync(context.UserName, context.Password); 


      if (user == null) 
      { 
       context.SetError("invalid_grant", "The user name or password is incorrect."); 
       return; 
      } 

      var identity = new ClaimsIdentity(context.Options.AuthenticationType); 
      identity.AddClaim(new Claim("sub", context.UserName)); 
      identity.AddClaim(new Claim("role", "user")); 

      context.Validated(identity); 
     } 
     catch (Exception ex) 
     { 
      string str = ex.ToString(); 
     } 

     db.Dispose(); 
    } 

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
    { 
     string clientId = ""; 
     string clientSecret = ""; 
     context.TryGetFormCredentials(out clientId, out clientSecret); 

     List<string> validClients = new List<string>(){ "web","Alliance_UWP","Alliance_Xamarin","Alliance_Web" }; 
     if (validClients.Contains(clientId)) 
      context.Validated(); 
    } 

你并不需要像冗长,因为我是在允许的客户,只是“网”是好的,你会包括在你的HTTP POST方法作为一个URL编码形式值,沿用grant_type =“密码”和你的用户名/密码。这里有一个我写的快速而脏的UWP客户端,它只需要输入用户名/密码,并通过api绑定的Authorize属性访问我所拥有的数据集。如果你不认证你会得到一个授权错误,这正是我们想要的。

这里只需要注意,在我的客户端中,我过滤掉了自签名证书错误,因为我通过上述命令在IIS Express中设置了自签名证书。

Uri tokenUri = new Uri(@"https://localhost:55970/Token"); 
    // This is a test data set from my Web Api pulling data from 
    // Entity Framework and SQL Server protected by the Authorize attribute 
    Uri testCasesUri = new Uri(@"https://localhost:55970/api/Cases"); 
    string accessToken = ""; 

    public MainPage() 
    { 
     this.InitializeComponent(); 
    } 

    private async void btn_SubmitLogin_Click(object sender, RoutedEventArgs e) 
    { 
     string username = txt_User.Text; 
     string password = txt_Password.Password; 

     HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); 
     filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); 
     filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); 
     filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); 
     HttpClient client = new HttpClient(filter); 
     Dictionary<string, string> parameters = new Dictionary<string, string>(); 
     parameters.Add("client_id", "web"); 
     parameters.Add("grant_type", "password"); 
     parameters.Add("username", username); 
     parameters.Add("password", password); 
     try 
     { 
      HttpResponseMessage result = await client.PostAsync(tokenUri, new HttpFormUrlEncodedContent(parameters)); 
      string jsonResult = await result.Content.ReadAsStringAsync(); 
      // TokenResult is a custom model class for deserialization of the Token Endpoint 
      // Be sure to include Newtonsoft.Json from NuGet 
      var resultObject = JsonConvert.DeserializeObject<TokenResult>(jsonResult); 
      accessToken = resultObject.AccessToken; 

      // When setting the request for data from Web Api set the Authorization 
      // header to Bearer and the token you retrieved 
      client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken); 
      result = await client.GetAsync(testCasesUri); 
      jsonResult = await result.Content.ReadAsStringAsync(); 

     } catch(Exception ex) 
     { 
      string debugBreak = ex.ToString(); 
     } 

希望能帮助你们任何一个人在Xamarin或UWP使用Web Api时遇到麻烦。