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

8
我在决定项目路线时遇到了问题。
我一直在阅读OWIN规范和.NET中的Katana实现。我想选择Katana路线的原因是与ADFS和Token/Cookie生成相关的owin组件。
我有两个项目,一个是MVC 5网站,另一个是Web API。它们可能将来会成为两个独立的服务器,但目前它们在同一个服务器上。
我知道我将使用IIS,因此不需要调查Owin管道。
我的要求是将有用户使用ADFS登录,其他用户将使用Token/Cookie生成进行登录,并使用Role/Membership提供程序。根据谁被认证,我的网页的某些部分将被公开。网页引擎使用razor完成。
是否有任何材料可以帮助解释我可以采取的设计流程?或者是否有人做过类似于我正在经历的项目,可以提供任何建议?有很多不同的文档描述我需要的特定内容,但没有大局观;例如只谈论WebAPI和ADFS,或WebAPI和Windows Azure等等。
我的理论是在MVC5网站项目上实现身份验证/授权,在Web API上实现授权(需要某种通信方式)。然后,我可能会为ADFS创建一个项目副本,为Token/cookie身份验证创建另一个项目副本?或者我必须制作4种不同类型的身份验证:2种用于对MVC5网站和Web API进行身份验证的ADFS,以及另外2种用于Token/cookie生成。
由于我不太熟悉这种技术,请提供任何建议。
2个回答

10
我可以提供一个建议,OWIN中的WsFederation选项不错,但需要使用cookies。这些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;
}

1
嗨,Brett,你能给我一个TransformClaims函数的例子吗?我正在尝试将我的安全基础架构迁移到owin/katana,但我还是有点迷茫。我想在身份验证后立即从我的数据库中获取所有用户声明,并将所有声明放入缓存的cookie中,以供一小时使用。你有这样的东西吗?非常感谢! - snekkke
Brett,感谢您发布这段代码,非常有帮助。声明转换是否可以在 cookie 中缓存一定时间,在此时间过后,再次运行请求的声明转换,从数据库中获取权限?谢谢!!! - snekkke
我来试试看,Brett。另外,还有一个我在想的事情……是否可能为认证 cookie 设置特定的缓存时间,并为从数据库获取的声明设置不同的缓存时间?我的意思是一个用于认证的 cookie,另一个用于授权。这可行吗?另外,你知道我可以找到一些阅读文档吗?我一直在搜索,但没有官方的东西。谢谢,Brett! - snekkke
谢谢Brett - 我对使用ADFS进行联合身份验证还很陌生,我正在尝试找出如何设置一个可以使用ADFS与单独的WebApi通信的Web应用程序(MVC)。我似乎找不到一个好的例子,所有我找到的都是使用Azure,而我需要本地的ADFS。你有什么建议或链接可以帮助我吗? - cmedine
1
是的,这很棘手。请查看https://dev59.com/3nTYa4cB1Zd3GeqPycPe。 - Brett
显示剩余4条评论

1
根据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]]);
    }
}

身份创建器接受ClaimsIdentity和应用程序名称,并访问数据库获取该用户在该应用程序中的声明。希望对您有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接