2015-08-27 116 views
1

我正在使用angular和webapi的跨平台web应用程序。问题是角度应用程序在科尔多瓦容器中运行时。为了与设备上的其他应用程序一起玩,我需要为SSO使用插件。此插件是导致我遇到的问题的原因,因为它执行了一些操作。它拦截所有的http请求,并向由第三方令牌提供者生成的头添加不记名令牌,所以我不能解码它,并覆盖我在头中设置的任何不记名令牌。它似乎也阻止cookies ..没有cookie或本地凭证的外部Owin身份验证

因此,当您无法向您发送自己的本地证书时,它会有点棘手。

于是我开始与https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs

所以我想我应该写我自己的中间件来照顾这;我认为既然标准的oauth中间件可以无cookie地工作,我不应该花太多时间让我的略有不同的不记名的令牌中间件去做。但事实并非如此......编写我自己的中间件..所以我“M抽到了头,与外部令牌提供验证,但我实际上不能登录。

protected override async Task<AuthenticationTicket> AuthenticateCoreAsync() 
     { 
      try 
      { 
       // Find token in default location 
       string requestToken = null; 
       string authorization = Request.Headers.Get("Authorization"); 
       if (!string.IsNullOrEmpty(authorization)) 
       { 
        if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) 
        { 
         requestToken = authorization.Substring("Bearer ".Length).Trim(); 
        } 
       } 
.... Take the Request token call other Server, verify token... 

而且

public override async Task<bool> InvokeAsync() 
    { 
     var ticket = await this.AuthenticateAsync(); 
     if(ticket != null) 
     { 
      this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity); 
      return false; 
     } 
    } 

那么到底签到不会导致错误或任何东西,但实际上并没有签署。只要我使用[Authorize]属性进行控制器操作,就会得到401。我没有启用任何外部cookie。很有可能我错了,或者我太努力了。

+0

我发现只是重写授权属性更简单。我问了一个类似的问题,这个人给了我以下链接(BitOfTech.net)。请发布,如果你得到这个想通了(http://stackoverflow.com/questions/32099027/webapi-token-issuance-authorization) –

+0

@ Mr.B - 嘿检查我的答案..我终于能够做到这一点。 –

回答

0

所以...我更早地回答了它,但是我能够弄明白,没有重写授权属性。我最终看到了OWIN安全代码的来源。诀窍是,你真的需要2个OWIN中间件组件。其中一个就是我称之为的服务器中间件(我从owin源中偷走了这个)。服务器中间件响应挑战和/或如果你感觉疯狂为你生成本地证书。这个中间件也是一个被动中间件组件。除非有人提出要求,否则我不会获得本地证书,因为这有点不合时宜,但如果有人认为这会有所帮助,我可以更新。

public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions> 
{ 
    //Important this needs to be overriden, but just calls the base. 
    protected override Task<AuthenticationTicket> AuthenticateCoreAsync() 
    { 
     return Task.FromResult<AuthenticationTicket>(null); 
    } 

    /// <summary>The apply response challenge async.</summary> 
    /// <returns>The <see cref="Task"/>.</returns> 
    protected override async Task ApplyResponseChallengeAsync() 
    { 
     if (this.Response.StatusCode != 401) 
     { 
      Task.FromResult<object>(null); 
      return; 
     } 

     var challenge = this.Helper.LookupChallenge(
      this.Options.AuthenticationType, 
      this.Options.AuthenticationMode); 
     if (challenge != null) 
     { 
      //OK in here you call the rediret to the 3rd party 
      //return a redirect to some endpoint 
     } 
     Task.FromResult<object>(null); 
     return; 
    } 
} 

反正注意如何倍率AuthenticateCoreAsync()只返回 回报Task.FromResult(NULL); 这是因为我们不希望此中间件修改请求。 ApplyResponseChallengeAsync将等待挑战并将您重定向到第三方登录。如果您想创建某种本地令牌,您可以覆盖InvokeAsync方法

您需要的第二个中间件是令牌/外部凭证验证程序。这将以某种方式验证用户身份。在OWIN安全性中内置的本地不记名令牌的情况下,它简单地对令牌进行反序列化,如果可以,并且令牌未过期,则对用户进行验证。因此,如果您想使用第三部分sso验证令牌,例如google或任何其他内容,则可以在此处插入逻辑。在我的情况下,我不仅想打电话给第三方提供商以获取用户信息,但要检查它们的令牌是否仍然适用于单次退出,并防止多个会话。

public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions> 
{ 

    //Going to give you the user for the request.. You Need to do 3 things here 
    //1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want 
    //2. validate the user with whatever user store or 3rd party SSO you want 
    //3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want. 
    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync() 
    { 




     //Good to throw in a point of override here.. but to keep it simple-ish 
     string requestToken = null; 
     string authorization = Request.Headers.Get("Authorization"); 

     //TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this 
     authorization = "Bearer 1234567869"; 

     //STEP 1 
     if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) 
     { 
      requestToken = authorization.Substring("Bearer ".Length).Trim(); 
      return await FakeExternalBearer(requestToken); 
     } 

     return null; 
    } 

    private async Task<AuthenticationTicket> FakeExternalBearer(string token) 
    { 
     var authenticationType = Options.AuthenticationType; 
     //pretend to call extenal Resource server to get user //STEP 2 
     //CallExternal(token) 

     //Create the AuthTicket from the return.. I will fake it out 
     var identity = new ClaimsIdentity(
          authenticationType, 
          ClaimsIdentity.DefaultNameClaimType, 
          ClaimsIdentity.DefaultRoleClaimType); 

     identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType)); 
     identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType)); 

     var properties = new AuthenticationProperties(); 
     properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1); 
     properties.IssuedUtc = DateTime.UtcNow; 

     var ticket = new AuthenticationTicket(identity, properties); 
     return ticket; 
    } 
} 

好的,我们重写AuthenticateCoreAsync,但我们现在确实做了一些事情。这是你的用户认证。这是中间件的ACTIVE部分。请注意它需要返回一个有效的AuthenticationTicket。这将在每个请求上运行,所以要小心你打电话的频率和频率。 所以我在这里有一个非常简单的例子https://github.com/jzoss/LowCalorieOwin如果有人对更多细节感兴趣,请询问。我可以添加更多。我确实做得太难了,因为现在我明白了,这很容易,但是如何做到这一点真的没有好的例子。

1

你这样做太难了。

而不是创建自己的承载身份验证中间件,您应该更改默认OAuthBearerAuthenticationProvider

以下是在查询字符串中发送令牌的示例。

//in Startup class 
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions 
{ 
    Provider = new QueryStringOAuthBearerProvider(), 
    //your settings 
}); 

//implementation 
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider 
{ 
    private const string AccessTokenQueryKey = "access_token"; 

    public override Task RequestToken(OAuthRequestTokenContext context) 
    { 
     //check if token found in the default location - "Authorization: Bearer <token>" header 
     if (string.IsNullOrEmpty(context.Token)) 
     { 
      var token = context.Request.Query.Get(AccessTokenQueryKey); 

      if (!string.IsNullOrEmpty(token)) 
      { 
       context.Token = token; 
      } 
     } 

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