Browse Source

follow example project

HOME 2 years ago
parent
commit
fe647373e4

+ 32 - 2
VCommon.Ioc.Tests/ChildrenContainerTest.cs

@@ -1,4 +1,5 @@
-using Xunit;
+using Unity;
+using Xunit;
 
 namespace VCommon.Ioc.Tests
 {
@@ -14,6 +15,20 @@ namespace VCommon.Ioc.Tests
             public string Value { get; set; } = "Keep";
         }
 
+        public abstract class AbstractContext
+        {
+
+        }
+
+        private class ContextImpl : AbstractContext
+        {
+        }
+
+        public class Service : ITransientIocClass
+        {
+            [Dependency] public AbstractContext Context { get; set; }
+        }
+
         [Fact]
         public void ChildContainerTest()
         {
@@ -21,13 +36,16 @@ namespace VCommon.Ioc.Tests
 
             var child1 = rootContainer.CreateChildren();
             var child2 = rootContainer.CreateChildren();
+            var child3 = rootContainer.CreateChildren();
 
             child1.RegisterInstanceToContainer(new Session { UserId = 1 });
             child2.RegisterInstanceToContainer(new Session { UserId = 2 });
-
+            child3.RegisterInstanceToContainer<AbstractContext>(new ContextImpl());
+            
             Assert.Same(rootContainer, rootContainer.Resolve<IIocManager>());
             Assert.Same(child1, child1.Resolve<IIocManager>());
             Assert.Same(child2, child2.Resolve<IIocManager>());
+            Assert.Same(child3, child3.Resolve<IIocManager>());
 
             Assert.True(rootContainer.IsRegistered<Setting>());
             Assert.True(child1.IsRegistered<Setting>());
@@ -39,9 +57,21 @@ namespace VCommon.Ioc.Tests
             Assert.False(rootContainer.IsRegistered<Session>());
             Assert.True(child1.IsRegistered<Session>());
             Assert.True(child2.IsRegistered<Session>());
+            Assert.False(child3.IsRegistered<Session>());
 
             Assert.Equal(1, child1.Resolve<Session>().UserId);
             Assert.Equal(2, child2.Resolve<Session>().UserId);
+
+            Assert.True(rootContainer.IsRegistered<Service>());
+            Assert.True(child1.IsRegistered<Service>());
+            Assert.True(child2.IsRegistered<Service>());
+            Assert.True(child3.IsRegistered<Service>());
+
+
+            Assert.Throws<ResolutionFailedException>(() => rootContainer.Resolve<Service>());
+            Assert.Throws<ResolutionFailedException>(() => child1.Resolve<Service>());
+            Assert.Throws<ResolutionFailedException>(() => child2.Resolve<Service>());
+            child3.Resolve<Service>();
         }
     }
 }

+ 2 - 5
VCommon.Ioc.Tests/InterceptorTest.cs

@@ -5,19 +5,16 @@ using Xunit;
 
 namespace VCommon.Ioc.Tests
 {
-
     public class InterceptorTest
     {
         private class Interceptor : IInterceptor, ITransientIocClass
         {
-            public void BeforeInvoke(DateTime begin, Type svcClass, MethodBase svcMethod, IReadOnlyDictionary<string, object> paramDic)
+            public void BeforeInvoke(Type svcClass, MethodBase svcMethod, IReadOnlyDictionary<string, object> paramDic)
             {
-
             }
 
-            public void AfterInvoke(DateTime begin, long msUsed, Type svcClass, MethodBase svcMethod, IReadOnlyDictionary<string, object> paramDic, object resultReturnValue, Exception resultException)
+            public void AfterInvoke(Type svcClass, MethodBase svcMethod, IReadOnlyDictionary<string, object> paramDic, object resultReturnValue, Exception resultException)
             {
-
             }
         }
 

+ 2 - 2
VCommon.Ioc/Interfaces.cs

@@ -8,10 +8,10 @@ using System.Threading.Tasks;
 namespace VCommon.Ioc
 {
     /// <summary> 单例注入 </summary>
-    public interface IContainerInstanceIocClass { }
+    public interface ISingletonIocClass { }
 
     /// <summary> 单例注入, 带拦截器 </summary>
-    public interface IContainerInstanceIocClass<TInterceptor> where TInterceptor : IInterceptor { }
+    public interface ISingletonIocClass<TInterceptor> where TInterceptor : IInterceptor { }
 
     /// <summary> 每次实例注入 </summary>
     public interface ITransientIocClass { }

+ 4 - 4
VCommon.Ioc/IocManager.cs

@@ -35,8 +35,8 @@ namespace VCommon.Ioc
             var interfaces = type.FilterInterfaces(
                 new[]
                 {
-                    typeof(IContainerInstanceIocClass),
-                    typeof(IContainerInstanceIocClass<>),
+                    typeof(ISingletonIocClass),
+                    typeof(ISingletonIocClass<>),
                     typeof(ITransientIocClass),
                     typeof(ITransientIocClass<>)
                 }
@@ -66,7 +66,7 @@ namespace VCommon.Ioc
                         , new InterceptionBehavior(interceptorClass));
                 }
             }
-            else if (iocDefine == typeof(IContainerInstanceIocClass<>))
+            else if (iocDefine == typeof(ISingletonIocClass<>))
             {
                 var interceptorClassTarget = iocInterface.GetGenericArguments()[0];
 
@@ -84,7 +84,7 @@ namespace VCommon.Ioc
                 Container.RegisterType(type, type, new TransientLifetimeManager());
                 foreach (var ri in remainInterfaces) Container.RegisterType(ri, type, new TransientLifetimeManager());
             }
-            else if (iocDefine == typeof(IContainerInstanceIocClass))
+            else if (iocDefine == typeof(ISingletonIocClass))
             {
                 Container.RegisterType(type, type, new ContainerControlledLifetimeManager());
                 foreach (var ri in remainInterfaces) Container.RegisterType(ri, type, new ContainerControlledLifetimeManager());

+ 15 - 0
VCommon.VApplication/Authorization/MultiTenancySides.cs

@@ -0,0 +1,15 @@
+using System;
+
+namespace VCommon.VApplication.Authorization
+{
+    /// <summary>Represents sides in a multi tenancy application.</summary>
+
+    public enum MultiTenancySides
+    {
+        Both = 0,
+        /// <summary>Tenant side.</summary>
+        Tenant = 1,
+        /// <summary>Host (tenancy owner) side.</summary>
+        Host = 2,
+    }
+}

+ 6 - 3
VCommon.VApplication/Authorization/PermissionNode.cs

@@ -33,9 +33,11 @@ namespace VCommon.VApplication.Authorization
         /// </summary>
         public string DescriptionForOperator { get; }
 
+        public MultiTenancySides TenancySide { get; }
+
         public IReadOnlyList<PermissionNode> Children => _children;
 
-        internal PermissionNode(PermissionTree tree, string code, string name, string description, string nameForOperator, string descriptionForOperator)
+        internal PermissionNode(PermissionTree tree, string code, string name, string description, string nameForOperator, string descriptionForOperator, MultiTenancySides tenancySide = MultiTenancySides.Both)
         {
             _tree = tree;
             Code = code;
@@ -43,13 +45,14 @@ namespace VCommon.VApplication.Authorization
             Description = description;
             NameForOperator = nameForOperator;
             DescriptionForOperator = descriptionForOperator;
+            TenancySide = tenancySide;
         }
 
-        public PermissionNode CreateChildNode(string code, string name, string description, string nameForOperator, string descriptionForOperator)
+        public PermissionNode CreateChildNode(string code, string name, string description, string nameForOperator, string descriptionForOperator, MultiTenancySides tenancySide = MultiTenancySides.Both)
         {
             if (_tree.CodeHashSet.Contains(code)) throw new ArgumentException($"重复的权限代码:{code},名称:{name}");
 
-            var node = new PermissionNode(_tree, code, name, description, nameForOperator, descriptionForOperator);
+            var node = new PermissionNode(_tree, code, name, description, nameForOperator, descriptionForOperator, tenancySide);
             _children.Add(node);
             _tree.CodeHashSet.Add(code);
             return node;

+ 13 - 5
VCommon.VApplication/IVSession.cs

@@ -4,14 +4,22 @@ namespace VCommon.VApplication
 {
     public interface IVSession
     {
-        Guid GetTenantId();
-
-        Guid GetUserId();
-
-        string Token { get; set; }
         Guid? UserId { get; }
         Guid? TenantId { get; }
 
         void DemandAuth();
+
+        public Guid GetUserId()
+        {
+            if (!UserId.HasValue) throw new VApplicationAuthException("Session.UserId is null! Probably, user is not logged in.", AuthReason.AuthRequired);
+            return UserId.Value;
+        }
+
+        public Guid GetTenantId()
+        {
+            if (!TenantId.HasValue)
+                throw new VApplicationAuthException("Session.TenantId is null! Possible problems: No user logged in or current logged in user in a host user (TenantId is always null for host users).", AuthReason.AuthRequired);
+            return TenantId.Value;
+        }
     }
 }

+ 4 - 3
VCommon.VOpenApi.VAspNetCore/ApiMiddleware.cs

@@ -107,8 +107,11 @@ namespace VCommon.VOpenApi.VAspNetCore
                 //create child container
                 using var child = _iocManager.CreateChildren();
                 child.RegisterInstanceToContainer(new ServiceInvokeTiming());
+
                 //put session dependency
                 child.RegisterInstanceToContainer(context);
+                //override session to child container from root
+                child.RegisterManually<VSession>();
 
                 try
                 {
@@ -116,8 +119,6 @@ namespace VCommon.VOpenApi.VAspNetCore
                 }
                 catch (Exception exception)
                 {
-                    if (exception is TargetInvocationException) exception = exception.InnerException;
-
                     await ErrorHandle(context, exception);
                 }
             }
@@ -170,7 +171,7 @@ namespace VCommon.VOpenApi.VAspNetCore
                     }
                     catch (Exception exception)
                     {
-                        Logger.Error("ApiModule:Exception json serializer fail", exception);
+                        Logger.Error("ApiMiddleware:Exception json serializer fail", exception);
                         response = VJsonSerializer.Serialize(new { Message = error.ToString() });
                     }
                 }

+ 12 - 0
VCommon.VOpenApi.VAspNetCore/IUserTokenStore.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace VCommon.VOpenApi.VAspNetCore
+{
+    public interface IUserTokenStore
+    {
+        void Validate(string token, out Guid? tenantId, out Guid? userId);
+    }
+}

+ 74 - 0
VCommon.VOpenApi.VAspNetCore/VSession.cs

@@ -0,0 +1,74 @@
+using Microsoft.AspNetCore.Http;
+using System;
+using VCommon.Ioc;
+using VCommon.VApplication;
+
+namespace VCommon.VOpenApi.VAspNetCore
+{
+    public class VSession : IVSession, ISingletonIocClass
+    {
+        private const string AuthorizationHeader = "Authorization";
+        private const string TokenHeaderPart = "Token";
+
+        private readonly HttpContext _context;
+        private readonly IUserTokenStore _tokenStore;
+
+        private bool _isTokenChecked;
+
+        private Guid? _tenantId;
+        private Guid? _userId;
+
+        internal VSession(HttpContext context, IUserTokenStore tokenStore)
+        {
+            _context = context;
+            _tokenStore = tokenStore;
+        }
+
+        private void EnsureTokenCheck()
+        {
+            if (_isTokenChecked) return;
+
+            string token = null;
+
+            //从请求头获取 Token
+            string rawToken;
+            if (false == string.IsNullOrWhiteSpace(rawToken = _context.Request.Headers[AuthorizationHeader]))
+            {
+                var parts = rawToken.Split(' ');
+                if (parts.Length == 2 && parts[0] == TokenHeaderPart && false == string.IsNullOrWhiteSpace(parts[1]))
+                {
+                    token = parts[1];
+                }
+            }
+
+            //验证Token
+            if (null != token) _tokenStore.Validate(token, out _tenantId, out _userId);
+
+            _isTokenChecked = true;
+        }
+
+        public Guid? UserId
+        {
+            get
+            {
+                EnsureTokenCheck();
+                return _userId;
+            }
+        }
+
+        public Guid? TenantId
+        {
+            get
+            {
+                EnsureTokenCheck();
+                return _tenantId;
+            }
+        }
+
+        public void DemandAuth()
+        {
+            EnsureTokenCheck();
+            if (false == UserId.HasValue) throw new VApplicationAuthException("需要验证身份", AuthReason.AuthRequired);
+        }
+    }
+}