IAbpSession的实现与OWIN

IAbpSession

在AbpController中,通过属性注入的方式注入IAbpSession对象。

IAbpSession的实例ClaimsAbpSession。

    public class ClaimsAbpSession : IAbpSession, ISingletonDependency
    {
        public virtual long? UserId
        {
            get
            {
                var userIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
                if (string.IsNullOrEmpty(userIdClaim?.Value))
                {
                    return null;
                }

                long userId;
                if (!long.TryParse(userIdClaim.Value, out userId))
                {
                    return null;
                }

                return userId;
            }
        }

        public virtual int? TenantId
        {
            get
            {
                if (!MultiTenancy.IsEnabled)
                {
                    return MultiTenancyConsts.DefaultTenantId;
                }

                var tenantIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId);
                if (string.IsNullOrEmpty(tenantIdClaim?.Value))
                {
                    return null;
                }

                return Convert.ToInt32(tenantIdClaim.Value);
            }
        }

PrincipalAccessor.Principal指的是ClaimsPrincipal类型的对象。该对象获得的方式如下:

public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal;

public static IPrincipal CurrentPrincipal
{
    [System.Security.SecuritySafeCritical]  // auto-generated
    get
    {
        lock (CurrentThread)
        {
            IPrincipal principal = (IPrincipal)
                CallContext.Principal;
            if (principal == null)
            {
                principal = GetDomain().GetThreadPrincipal();
                CallContext.Principal = principal;
            }
            return principal;
        }
    }

    [System.Security.SecuritySafeCritical]  // auto-generated
    [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPrincipal)]
    set
    {
        CallContext.Principal = value;
    }
}

Thread.CurrentPrincipal通过CallContext.Principal或GetDomain().GetThreadPrincipal(). 获得IPrincipal对象。

无论通过哪种方式,该IPrincipal对象,都是从请求上下文获得的。

因此:IAbpSession保存的值是从请求上下文获得的,应用程序必然在某个时刻,将值保存到请求上下文中。这一步是通过OWIN实现的。

与IAbpSession相关的OWIN

在MVC项目App_Start文件夹添加Startup文件。

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/"),
                CookieSecure = CookieSecureOption.Never,
            });
        }

这一步的作用是向请求管道注册了一个事件。该事件在请求处理的Authenticate阶段执行,执行的内容是获得请求上下文的cookie(string字符串),经过解码和解密,将键值对保存成多个Claim类型的对象,并将这些Claim对象集合保存到ClaimsIdentity类型的对象Claims属性中。

ClaimsIdentity类型中的Claims属性:public virtual IEnumerable<Claim> Claims { get; }

ClaimsIdentity类型的对象又被上下文所引用。

因此可理解:Thread.CurrentPrincipal as ClaimsPrincipal 即是指这个解密出来的ClaimsIdentity对象。

如何将用户信息保存到上下文,并加密到cookie

AuthenticationManager类型

 internal class AuthenticationManager : IAuthenticationManager

为控制器添加如下属性:

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

HttpContext.GetOwinContext().Authentication返回的即是AuthenticationManager类型的对象;

该类型有两个方法,负责登录和登出。

public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities)
public void SignOut(AuthenticationProperties properties, string[] authenticationTypes)

SignIn和SignOut还有分别有一个重载,只是 AuthenticationProperties properties参数是new AuthenticationProperties();

在这里,只要为SignIn方法成功传入一个对象ClaimsIdentity即可能登录成功。但如何构建ClaimsIdentity对象。

微软的Identity实现:

Microsoft.AspNet.Identity.Core程序集下UserManager<TUser, TKey>实现如下方法。

public virtual Task<ClaimsIdentity> CreateIdentityAsync(TUser user, string authenticationType)

TUser需实现IUser<out Tkey>接口,该接口包含Id和UserName两个属性。

    public interface IUser<out TKey>
    {
        TKey Id { get; }
        string UserName { get; set; }
    }

因此可以理解为:用户输入用户名和密码→应用程序向数据库查询该用户,返回一个实现IUser接口的对象→调用UserManager.CreateIdentityAsync方法,将IUser对象传入方法中→该方法返回一个ClaimsIdentity对象→将该对象传给AuthenticationManager.SignIn方法。

SignIn会写入到上下文并在某个“时间地点”加密、编码成cookie. 反过来讲,SignOut会清除上下文中的ClaimsIdentity对象。

自定义构建ClaimsIdentity对象:

仿照UserManager.CreateIdentityAsync内的代码,如下写法:

在这里,创建ClaimsIdentityFactory对象仅仅只为使用其内的常量字符串,并无其它意义,不能修改替换ClaimsIdentityFactory内的常量字符串,否则会出错。(new Claim时构造函数传入的factory的几个属性均是常量字符串)

IdentityProviderClaimType和DefaultIdentityProviderClaimValue 也均为ClaimsIdentityFactory内的internal修饰的常量字符串,因在本处引用不到,所以拿出来了。
user = new Entitys.User { Id = 123, TenantId = 1, Email = "[email protected]", UserName = "admin" };
var factory = new Microsoft.AspNet.Identity.ClaimsIdentityFactory<User, long>();
string IdentityProviderClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
string DefaultIdentityProviderClaimValue = "ASP.NET Identity";
ClaimsIdentity id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie, factory.UserNameClaimType, factory.RoleClaimType);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), ClaimValueTypes.String));
id.AddClaim(new Claim(factory.UserNameClaimType, user.UserName, ClaimValueTypes.String));
id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
id.AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.ToString(CultureInfo.InvariantCulture)));

AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberMe }, id);

如上所示:应用程序就可以实现登录了。登录后就可以访问[Authorize]特性控制的Action。如果调用SignOut方法,即登出,访问[Authorize]特性控制的Action将需要登录。

简要说明的是,在Abp工作单元中也实现了IAbpSession属性,从而得到TenantId的值,以致能在查询中自动添加TenantId条件过滤。

OWIN学习参考博文:

http://www.cnblogs.com/jesse2013/p/aspnet-identity-claims-based-authentication-and-owin.html

分享一个Asp.net 源码网站,同时感谢介绍给我的那位好人。

https://referencesource.microsoft.com/#q=Identity

需要参考的源码:

katana项目,包含Owin相关几个程序集源码。

https://katanaproject.codeplex.com/SourceControl/latest#README

Identity源码:

https://github.com/aspnet/Identity

时间: 2024-11-05 16:30:30

IAbpSession的实现与OWIN的相关文章

如何定义一个有效的OWIN Startup Class

命名约定 Katana在程序集内的程序集名称空间下查找一个叫做Startup的类, 通过属性指定 [assembly: OwinStartup(typeof(OwinConsoleApp.Startup))] 通过配置文件 <add key="owin:appStartup" value="OwinConsoleApp.Startup1" /> 定义友好命名的Startup类 <appSettings> <add key="o

基于OWIN+DotNetOpenOAuth实现OAuth2.0

这几天时间一直在研究怎么实现自己的OAuth2服务器,对于太了解OAuth原理以及想自己从零开始实现的,我建议可以参考<Apress.Pro ASP.NET Web API Security>里面的章节.最后发现其实微软在这方面也已经做了实现,所以文介绍下怎么基于OWIN来实现自己的OAuth2.0授权服务器,,以及怎么使用DotNetOpenAuth作为客户端来访问受保护的资源.  OWIN是一套specification,微软的Katana开源项目是基于OWIN标准开发的,所以本本文更准确

MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN

在Membership系列的最后一篇引入了ASP.NET Identity,看到大家对它还是挺感兴趣的,于是来一篇详解登录原理的文章.本文会涉及到Claims-based(基于声明)的认证,我们会详细介绍什么是Claims-based认证,它与传统认证方式的区别,以及它的特点.同时我们还会介绍OWIN (Open Web Interface for .NET) 它主要定义了Web Server 和Web Application之间的一些行为,然后实现这两个组件的解耦(当然远不止这么点东西,我相信

Nancy之基于Nancy.Owin的小Demo

前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katana 什么是Owin呢? 官网地址:http://owin.org OWIN在.NET Web Servers与Web Application之间定义了一套标准接口,OWIN的目标是用于解耦Web Server和Web Application. 什么是Katana呢? 官网地址:http://kata

OWIN 为WebAPI

OWIN 为WebAPI 宿主 跨平台 OWIN是什么? OWIN的英文全称是Open Web Interface for .NET. 如果仅从名称上解析,可以得出这样的信息:OWIN是针对.NET平台的开放Web接口. 那Web接口是谁和谁之间的接口呢?是Web应用程序与Web服务器之间的接口,OWIN就是.NET Web应用程序与Web服务器之间的接口. 为什么需要这样一个接口呢?因为.NET Web应用程序是运行于Web服务器之中的,.NET Web应用程序需要通过Web服务器接收用户的请

OWIN初探

什么是 OWIN ? OWIN 的全称是 "Open Web Interface for .NET", OWIN 在 .NET Web 服务器和 .NET Web 应用之间定义了一套标准的接口, 其目的是为了实现服务器与应用之间的解耦, 鼓励为 .NET Web 应用开发简单模块. OWIN 是一个开源开放的标准, 有助于建设 .NET 开发的开源生态环境,OWIN 定义了如下几个概念: 服务器 (Server) HTTP 服务器直接与客户端交互, 并用 OWIN 语义处理请求,服务器

Owin

Owin应用程序代理using AppFunc=Func<IDictionary<string,object>,Task>; IDictionary<string,object>环境变量,贯穿整个处理流程 Microsoft.Owin.Host.SystemWeb可以接管IIS请求(App>Middleware>Server>Host)Server层MIcrosoft.Owin.Host.HttpListener(Server层,可以实现控制台寄宿)M

Owin与mvc

学习连接http://www.cnblogs.com/JustRun1983/p/3967757.html Web Form和MVC现在还不能作为一个中间件集成到OWIN管道中,所有要么二选一,要么先后执行.二选一就是根据路由匹配选择执行方式,先后执行就是先执行Owin管道,后执行mvc. 第一部分: 直接表达式方法 [assembly: OwinStartup(typeof(OwinTest.Startup))] namespace OwinTest { public class Startu

OWIN轻量型框架介绍

OWIN轻量型框架介绍 阅读目录 引言 框架的特色 如何启动 各项功能 静态路由的3种写法 伪静态路由的支持 处理Form表单提交的文件 流式处理Post请求的数据 多种请求类型自动识别 响应处理 请求响应上下文 自定义默认处理函数 内置各种便捷函数 复合类型的请求处理 框架的扩展 静态内容的支持 跨域Post的支持 基础类型继承灵活处理 尾声 回到顶部 引言 什么是OWIN,我就不介绍了,请自行搜索,这里主要是介绍自行开发的OWIN框架的特点和用法.由于.NET的web框架都比较庞大,导致性能