【问题标题】:User impersonation through HTTP header通过 HTTP 标头模拟用户
【发布时间】:2020-05-15 23:40:57
【问题描述】:

我需要通过 HTTP 标头实现用户模拟。例如,用户将向/api/something 发送请求,请求头为impersonate=someUser

我尝试了以下过程:

  1. 用户通过多种身份验证方案之一进行身份验证。
  2. 如果通过一些安全检查,经过身份验证的用户将被模拟用户替换。
  3. /api/something 端点被调用

我为此编写了一些自定义中间件,它在内置身份验证中间件之后运行:

if (!context.Request.Headers.TryGetValue("%Impersonation header%", out StringValues subject))
{
    await _next(context);
    return;
}

if (context.User?.Identity?.IsAuthenticated != true)
{
    // return error
}

...

context.User = impersonatedUser
await _next(context);

但是,当它最终到达控制器时,仍然使用初始用户,因为ClaimsPrincipal 已被默认授权替换为具有两个身份的新对象。第一个身份是真实用户,第二个身份是冒充用户。

我可能会使用第二个身份解析用户,但我不确定此过程是否遵循最佳做法?

编辑:这是针对 ASP.NET Core 2.2 / 3.1

【问题讨论】:

    标签: c# asp.net-core impersonation


    【解决方案1】:

    在 Startup.cs 中配置 IISServerOptions

    services.AddAuthentication(IISDefaults.AuthenticationScheme);
            services.AddSimpleRoleAuthorization<CustomWindowsAuthenticationProvider>();
            services.Configure<IISServerOptions>(opt=>{
                opt.AutomaticAuthentication=true;
                opt.AuthenticationDisplayName="SIMS";
            });
    

    然后实现您自己的 IClaimsTransformation 以验证用户并适当地设置声明

    public class CustomWindowsAuthenticationProvider : ISimpleRoleProvider
    {
        public CustomWindowsAuthenticationProvider(UnitOfWork unitOfWork)
        {
            this._unitOfWork = unitOfWork;
        }
        private UnitOfWork _unitOfWork;
    
        public Task<ICollection<string>> GetUserRolesAsync(string userName)
        {
            ICollection<string> result = new string[0];
            string[] user = userName.Split("\\");
            var roles = _unitOfWork.UserMod.GetRolesForUser(user[1]);
            if (roles!=null)
                result = roles.Select(d => d.RoleName).ToArray();
    
    
            return Task.FromResult(result);
        }
    }
    public interface ISimpleRoleProvider
    {
        #region Public Methods
    
        /// <summary>
        /// Loads and returns the role names for a given user name.
        /// </summary>
        /// <param name="userName">The login name of the user for which to return the roles.</param>
        /// <returns>
        /// A collection of <see cref="string" /> that describes the roles assigned to the user;
        /// An empty collection of no roles are assigned to the user.
        /// </returns>
        /// <remarks>
        ///     <para>Beware that this method is called for each controller call. It might impact performance.</para>
        ///     <para>
        ///     If Windows authentication is used, the passed <paramref name="userName" />
        ///     is the full user name including the domain or machine name (e.g "CostroDomain\JohnDoe" or
        ///     "JOHN-WORKSTATION\JohnDoe").
        ///     </para>
        ///     <para>
        ///     The returned roles names can be used to restrict access to controllers using the <see cref="AuthorizeAttribute" />
        ///     (<c>[Authorize(Roles="...")]</c>
        ///     </para>
        /// </remarks>
        Task<ICollection<string>> GetUserRolesAsync(string userName);
    
        #endregion
    }
    public class SimpleRoleAuthorizationTransform : IClaimsTransformation
    {
        #region Private Fields
    
        private static readonly string RoleClaimType = ClaimTypes.Role;//  $"http://{typeof(SimpleRoleAuthorizationTransform).FullName.Replace('.', '/')}/role";
        private readonly ISimpleRoleProvider _roleProvider;
    
        #endregion
    
        #region Public Constructors
    
        public SimpleRoleAuthorizationTransform(ISimpleRoleProvider roleProvider)
        {
            _roleProvider = roleProvider ?? throw new ArgumentNullException(nameof(roleProvider));
        }
    
        #endregion
    
        #region Public Methods
    
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            // Cast the principal identity to a Claims identity to access claims etc...
            var oldIdentity = (ClaimsIdentity)principal.Identity;
    
            // "Clone" the old identity to avoid nasty side effects.
            // NB: We take a chance to replace the claim type used to define the roles with our own.
            var newIdentity = new ClaimsIdentity(
                oldIdentity.Claims,
                oldIdentity.AuthenticationType,
                oldIdentity.NameClaimType,
                RoleClaimType);
    
            // Fetch the roles for the user and add the claims of the correct type so that roles can be recognized.
            var roles = await _roleProvider.GetUserRolesAsync(newIdentity.Name);
            if(roles.Count>0)
                newIdentity.AddClaims(roles.Select(r => new Claim(RoleClaimType, r)));
    
            // Create and return a new claims principal
            return new ClaimsPrincipal(newIdentity);
        }
    
        #endregion
    }
    public static class SimpleRoleAuthorizationServiceCollectionExtensions
    {
        #region Public Static Methods
    
        /// <summary>
        /// Activates simple role authorization for Windows authentication for the ASP.Net Core web site.
        /// </summary>
        /// <typeparam name="TRoleProvider">The <see cref="Type"/> of the <see cref="ISimpleRoleProvider"/> implementation that will provide user roles.</typeparam>
        /// <param name="services">The <see cref="IServiceCollection"/> onto which to register the services.</param>
        public static void AddSimpleRoleAuthorization<TRoleProvider>(this IServiceCollection services)
            where TRoleProvider : class, ISimpleRoleProvider
        {
            services.AddScoped<ISimpleRoleProvider, TRoleProvider>();
            services.AddScoped<IClaimsTransformation, SimpleRoleAuthorizationTransform>();
        }
    
        #endregion
    }
    

    之后,您可以在 iis 中托管应用程序,并使用 iis 身份验证来确定您要使用哪种方法和设置。

    【讨论】:

    • 我不确定这是一个有效的解决方案。这应该如何连接到 HTTP 标头?
    • 您正在设置 Windows 身份验证,这与我的问题无关。此外,我没有看到任何从 HTTP 请求连接模拟标头的解决方案。不过,使用声明转换器可能是一个有趣的想法。
    猜你喜欢
    • 2010-11-17
    • 2020-02-20
    • 1970-01-01
    • 2018-05-22
    • 2021-12-24
    • 2018-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多