身份验证/授权 MVC 5 和 Web API - Katana/Owin

Posted

技术标签:

【中文标题】身份验证/授权 MVC 5 和 Web API - Katana/Owin【英文标题】:Authentication / Authorization MVC 5 and Web API - Katana/Owin 【发布时间】:2014-04-08 16:10:07 【问题描述】:

我在尝试决定承担我的项目的路线时遇到问题。

我一直在阅读 OWIN 规范和 .NET 中的 Katana 实现。我之所以选择 Katana 路线,是因为与 ADFS 和 Token/Cookie 生成相关的 owin 组件。

我有两个项目,一个用于 MVC 5 网站,一个用于 Web API。将来它们可能位于两台不同的服务器上,但目前它们在同一台服务器上。

我知道我将使用 IIS,因此我不需要调查 Owin 管道。

我的要求是,有些用户将使用 ADFS 登录,而其他用户将使用 Token/Cookie 生成以及角色/成员资格提供程序登录。根据谁通过身份验证,我的网页的某些部分将被公开。网页引擎是在razor中完成的。

有没有人可以阅读任何材料来帮助解释我可以采用的设计流程?或者有人做过类似于我正在经历的项目,可以添加任何建议?有很多不同的文档描述了我需要的具体事物,但不是全局;比如只谈 WebAPI 和 ADFS,或者 WebAPI 和 windows azure 等等。

我的理论是在MVC5网站项目上实现认证/授权,在Web API上进行授权(不知何故两者之间需要存在通信)。然后我可能会为 ADFS 创建一个项目副本并为令牌/cookie 身份验证创建另一个副本?或者也许我必须进行 4 种不同类型的身份验证:2 种用于我针对 MVC5 网站和 Web API 进行身份验证的 adfs,另外 2 种用于生成令牌/cookie。

任何建议都会有所帮助,因为我对这种技术不是很熟悉。

【问题讨论】:

【参考方案1】:

我可以提供 OWIN 中的 WsFederation 选项很好,但需要 cookie……而且它们是一种不同于带有 cookie 的本地身份验证的 cookie。 ADFS 2.0/WsFederation 使用 AuthenticationType="Cookies",本地身份验证使用 AuthenticationType="ApplicationCookie"。据我所知,它们显然是不相容的。我认为您必须对 ADFS 使用令牌身份验证,但我认为这需要 2012R2 上的 ADFS 3.0。为此,请使用 OWIN OAuth。

更新:在研究了一段时间后,我想出了如何让这两种身份验证类型在同一个 Web 应用程序中和平共存。使用 OWIN,设置两次调用 UseCookieAuthentication,一次启用新的 WsFederationAuthentication 中间件,再次启用本地 cookie 身份验证。这并不直观,但在幕后,为每个指定不同的身份验证类型将它们设置为不同的身份验证“引擎”。这是它在我的 Startup 中的样子:

app.UseCookieAuthentication(new CookieAuthenticationOptions

    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
        
            OnResponseSignIn = ctx =>
            
                ctx.Identity = TransformClaims(ctx, app);
            
        
);

app.UseCookieAuthentication(new CookieAuthenticationOptions

    Provider = new CookieAuthenticationProvider
    
        OnResponseSignIn = ctx =>
        
            ctx.Identity = TransformClaims(ctx, app);
        
    
);

app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions

    Wtrealm = Realm,
    MetadataAddress = Metadata,
    Caption = "Active Directory",
    SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType
);

这成功地允许用户对本地 SQL 表或 ADFS 2.0 进行身份验证。 TransformClaims 标注允许我规范这两个提供商之间的声明,以便它们保持一致。

编辑:这是一个非常基本的 TransformClaims。你可以在其中做很多事情:从你的数据库中获取用户,设置导航声明,自定义权限,角色集合等等。我刚刚从一个更大的实现中构建了这个精简版本,所以我没有运行它,但希望您了解如何利用 OnResponseSignIn 事件。

private static ClaimsIdentity TransformClaims(CookieResponseSignInContext ctx, IAppBuilder app)

    var ident = ctx.Identity;

    var claimEmail = ident.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Email);
    var claimName = ident.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Name);

    //normalize my string identifier
    var loginString = (claimEmail != null) ? claimEmail.Value : (claimName != null) ? claimName.Value : null;
    var efctx = ctx.OwinContext.Get<DBEntities>();

    var user = UserBL.GetActiveUserByEmailOrName(efctx, loginString);
    if (user == null)
    
        //user was auth'd by ADFS but hasn't been auth'd by this app
        ident.AddClaim(new Claim(ClaimTypesCustom.Unauthorized, "true"));
        return ident;
    

    if (ident.Claims.First().Issuer == "LOCAL AUTHORITY")
    
        //Local
        //local already has claim type "Name"
        //local didn't have claim type "Email" - adding it
        ident.AddClaim(new Claim(ClaimTypes.Email, user.Email));
    
    else
    
        //ADFS
        //ADFS already has claim type "Email"
        //ADFS didn't have claim type "Name" - adding it
        ident.SetClaim(ClaimTypes.Name, user.UserName);
    

    //now ident has "Name" and "Email", regardless of where it came from
    return ident;

【讨论】:

哇,这对我帮助很大!谢谢!! 嗨,Brett,你能给我一个 TransformClaims 函数的例子吗?我正在尝试将我的安全基础架构迁移到 owin/katana,但我仍然有点迷失。我想在身份验证后立即从我的数据库中获取所有用户声明,并将所有声明放入缓存的 cookie 中一小时。你有这样的事吗?非常感谢!! Brett,感谢您发布代码,这非常有帮助。声明转换是否可以在 cookie 中缓存一段时间,并且在该时间过去之后,声明转换再次在请求上运行,再次从数据库获取权限?谢谢!!!! 我不是 100% 确定,但我相信这将是 ADFS 中令牌超时设置的一个功能。一旦您最初通过身份验证,您转换或添加的声明将在刷新时跟随您,直到身份验证令牌过期,此时在下一次刷新时,站点将感觉到您的令牌无效,您将重新进行身份验证然后点击再次出现 OnResponseSignIn 事件。 是的,这很棘手。查看***.com/questions/17797698/…。【参考方案2】:

Cmedine,根据 Brett 的回答,我配置了我的身份验证和授权。当您请求一些示例代码时,我会向您展示代码。

public partial class Startup

    public void ConfigureAuth(IAppBuilder app)
    
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        
            SlidingExpiration = false,
            CookieName = "identity",
            //short time for testing only
            ExpireTimeSpan = TimeSpan.FromSeconds(120),
            Provider = new CookieAuthenticationProvider
            
                OnResponseSignIn = ctx =>
                
                    ctx.Identity = TransformClaims(ctx);
                
            
        );

        app.UseWsFederationAuthentication(
            new WsFederationAuthenticationOptions
            
                MetadataAddress = "https://[[ADFS_Url]]/FederationMetadata/2007-06/federationmetadata.xml",
                Wtrealm = "[[realm]]",
                UseTokenLifetime = false
            
        );

    

    private ClaimsIdentity TransformClaims(CookieResponseSignInContext ctx)
    
        return new IdentityCreator().CreateIdentity(ctx.Identity, [[ApplicationName]]);
    

IdentityCreator 获取 ClaimsIdentity 和应用程序名称,然后转到数据库并获取该用户在该应用程序中的声明。希望对你有帮助!!

【讨论】:

以上是关于身份验证/授权 MVC 5 和 Web API - Katana/Owin的主要内容,如果未能解决你的问题,请参考以下文章

如何使用基于 cookie 的身份验证授权 Web API

.NET Core - 在 IIS 上使用 Windows 身份验证从 MVC 应用程序调用 Web API 导致 HttpRequestException 401(未经授权)状态代码

NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)

如何在 MVC 5 中设置本地授权身份验证?

为网站和移动应用程序混合 MVC 5 + WEB API 2 身份验证

如何向已经使用 Identity 的 MVC 控制器添加 API 身份验证和授权?