在WinForms中验证用户(与ASP.Net无关)

Hod*_*won 5 authentication winforms c#-4.0

注意:根据评论交叉发布到ServerFault.

介绍

我需要密码保护我的应用程序中的某些操作,例如加载/保存文件,单击复选框等.这是一个标准的C#.Net 4.0,WinForms应用程序,它将在企业网络中的Windows 7上运行.

我正准备推出我自己的非常基本的系统(用宽敞的后门阅读混淆)和一个用户/密码/权限(哈希和盐渍)的文本文件,直到经过一些搜索我发现看起来像一个 诱人的简单方法,但我'我很难找到关于与ASP.NET 有关的角色的好教程.

所以有人知道一个或多个教程,告诉我如何:

  1. 创建Windows用户/组并为该用户/组授予Role或权限.
    • 请注意,我正在从我公司的联网笔记本电脑上测试它,但是会将其部署在客户的公司网络上(不确定这是否是一个问题,或者这将是多么棘手).
  2. 创建winforms/console app示例甚至只有一个方法打印"Hello World"如果我已经过身份验证,或者如果我不是则会抛出异常?
  3. 我从来没有做过网络管理员或任何相关的事情,我一直在阅读有关Active Directory和本地用户与网络用户的信息...我希望有一种方法,我可以构建到一个Interface只是询问Windows,如果当前用户有权限ABC和不太关心Windows如何解决这个问题.然后,我可以为每个Local/Network/ActiveDirectory /等进行具体实现.根据需要使用用例(或者如果需要的话......因为我现在甚至都不知道).

背景

- read if interested, but not required to answer question
Run Code Online (Sandbox Code Playgroud)

为了确保我在这里朝着正确的方向前进,基本上我需要/想要在我的开发PC上测试它,以确保它能为我的客户提供良好的最终用户体验.问题是,目前他们为运行我的应用程序的每台计算机运行自动登录脚本,并且有几个不同的运算符在一天中使用我的应用程序.客户希望我的应用程序的某些功能受到密码保护,并且只向某些运营商提供.我没有问题,因为我已经预料到请求一段时间了,我以前从未编程过身份验证.

我认为说服我的客户为每个运营商提供他们自己的网络帐户并为他们分配他们想要的任何权限是值得的,以防他们需要解雇某人,更改权限等.这也意味着我只需打开几个选项他们可以根据内部公司政策对这些权限进行分组,但我真的不应该担心这些权限(但如果我必须自己动手,因为他们的IT部门几乎一无所知)我的应用程序).

从我所知道的,它还可以通过不必处理散列密码和加密等使我的生活变得更加轻松,并且只需处理单击此按钮或该按钮所需的角色.

And*_* H. 8

首先,您必须确定,如果您真的想要一个简单的基于角色的身份验证(您可能需要阅读:http://lostechies.com/derickbailey/2011/05/24/dont-do-role基于授权检查 - 基于活动的检查/)

如果您确定这绝对足够,那么您已经使用您在问题中提供的SO链接以正确的方式进行了操作.有点令人困惑的是,Windows中默认不支持"角色",但有些组.组可以是本地的或远程的(例如ActiveDirectory),因此管理员可以将用户分配给特定于您的应用程序的特定组(例如:http://msdn.microsoft.com/en-us/library/ ms731200(v = vs.110).aspx)

一个关键是:您必须准备应用程序的中心主体,因此需要为当前用户支持角色.

因此,在应用程序启动时,您可以检查当前活动用户并设置应用程序范围内的主体和角色.这可能看起来像这样(只是一个非常简单的例子):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;

namespace WindowsPrincipalTrial
{
    public class Program
    {
        // you could also move these definitions to a config file
        private static IDictionary<string, string> _groupRoleMappings = new Dictionary<string, string>()
        {
        {"MYAPPUSERGRP", MyRoles.Standard},
        {"MYAPPSUPPORTGRP", MyRoles.Extended},
        {"MYAPPADMINGRP", MyRoles.Admin},
        };

        private static void Main(string[] args)
        {
            var windowsId = WindowsIdentity.GetCurrent();
            if (windowsId != null)
            {
                var allRoleNames = getGroupCorrespondingRoles(windowsId);
                var newPrincipal = new GenericPrincipal(windowsId, allRoleNames);
                Thread.CurrentPrincipal = newPrincipal;
            }
            else
            {
                throw new NotSupportedException("There must be a logged on Windows User.");
            }
        }

        private static string[] getGroupCorrespondingRoles(WindowsIdentity id)
        {
            // you also could do this more elegant with LINQ
            var allMappedRoleNames = new List<string>();

            string roleName;

            foreach (var grp in id.Groups)
            {
                var groupName = grp.Translate(typeof(NTAccount)).Value.ToUpper();
                if (_groupRoleMappings.TryGetValue(groupName, out roleName))
                {
                    allMappedRoleNames.Add(roleName);
                }
            }

            return allMappedRoleNames.ToArray();
        }
    }


    public static class MyRoles
    {
        public const string Standard = "standard_role";
        public const string Extended = "extended_role";
        public const string Admin = "admin_role";
    }
}
Run Code Online (Sandbox Code Playgroud)

然后设置您的Application-Principal.现在您可以检查代码中的访问权限,如下所示:

public void DoSomethingSpecial()
{
    if (Thread.CurrentPrincipal.IsInRole(MyRoles.Extended))
    {
        // do your stuff
    }
    else
    {
        // maybe display an error
    }
}
Run Code Online (Sandbox Code Playgroud)

或者更彻底地:

public void DoSomethingCritical()
{
    var adminPermission = new PrincipalPermission(null, MyRoles.Admin);
    adminPermission.Demand();

    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

从ASP.NET中可以看出,即使是声明也是可能的:

[PrincipalPermission(SecurityAction.Demand, Role=MyRoles.Admin)]
public void DoSomethingMoreCritical()
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

后两个例子的丑陋之处在于,当正确的角色没有被击中时,它们会抛出异常.

因此,根据您要使用的系统(本地组,AD组,LDAP组等),您必须在应用程序开始时完成角色和组之间的映射.

但是,如果您更喜欢使用操作和角色进行身份验证,那么请查看Windows Identity Foundation和基于声明的授权!已经有一些现成的框架(例如https://github.com/thinktecture/Thinktecture.IdentityModel).

更新:

当涉及基于活动并因此基于声明的授权时,我将通过使用Thinktecture的IdentityModel来尝试如何实现它.

通常,该方法仍然在内部使用角色,但在它们之间有一种转换层.Thinktecture已经封装了许多所需的东西.然后通过声明权限完成代码中的授权检查.它们在技术上是对访问某种资源的请求.为了简单起见,我通过使用一个默认资源限制了我的动作示例(因为ClaimPermission不接受空资源).如果要使用action @ resource对,则必须分别修改代码.

起初你需要一个 ClaimsAuthorizationManager

    public class MyClaimsAuthorizationManager : ClaimsAuthorizationManager
    {
        private IActivityRoleMapper _actionToRolesMapper;

        public MyClaimsAuthorizationManager(IActivityRoleMapper mapper)
        {
            _actionToRolesMapper = mapper;
        }

        public override bool CheckAccess(AuthorizationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            try
            {
                var action = getActionNameFromAuthorizationContext(context);

                var sufficientRoles = _actionToRolesMapper.GetRolesForAction(action)
                    .Select(roleName => roleName.ToUpper());

                var principal = context.Principal;

                return CheckAccessInternal(sufficientRoles, principal);
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        protected virtual bool CheckAccessInternal(IEnumerable<string> roleNamesInUpperCase, IClaimsPrincipal principal)
        {
            var result = principal.Identities.Any(identity =>
                    identity.Claims
                        .Where(claim => claim.ClaimType.Equals(identity.RoleClaimType))
                        .Select(roleClaim => roleClaim.Value.ToUpper())
                        .Any(roleName => roleNamesInUpperCase.Contains(roleName)));

            return result;

        }

        // I'm ignoring resources here, modify this, if you need'em
        private string getActionNameFromAuthorizationContext(AuthorizationContext context)
        {
            return context.Action
                .Where(claim => claim.ClaimType.Equals(ClaimPermission.ActionType))
                .Select(claim => claim.Value)
                .FirstOrDefault();
        }
    }
Run Code Online (Sandbox Code Playgroud)

您可能已经猜到,IActivityRoleMapper是一个类的接口,它返回所有角色的名称,包括给定操作的权限.这个课程非常个性化,我想你会找到实现它的方式,因为这不是重点.您可以通过硬编码,从xml或从数据库加载来完成.如果您想要为权限请求操作@资源对,您还必须更改/扩展它.

然后你必须将main()方法中的代码更改为:

using Thinktecture.IdentityModel;
using Thinktecture.IdentityModel.Claims;
using Microsoft.IdentityModel.Web;



private static void Main(string[] args)

    {
        var windowsId = WindowsIdentity.GetCurrent();
        if (windowsId != null)
        {
            var rolesAsClaims = getGroupCorrespondingRoles(windowsId)
                .Select(role => new Claim(ClaimTypes.Role, role))
                .ToList();

            // just if you want, remember the username
            rolesAsClaims.Add(new Claim(ClaimTypes.Name, windowsId.Name));

            var newId = new ClaimsIdentity(rolesAsClaims, null, ClaimTypes.Name, ClaimTypes.Role);

            var newPrincipal = new ClaimsPrincipal(new ClaimsIdentity[] { newId });
            AppDomain.CurrentDomain.SetThreadPrincipal(newPrincipal);

            var roleMapper = new ActivityRoleMapper(); // you have to implement

            // register your own authorization manager, so IdentityModel will use it per default
            FederatedAuthentication.ServiceConfiguration.ClaimsAuthorizationManager = new MyClaimsAuthorizationManager(roleMapper);
        }
        else
        {
            throw new NotSupportedException("There must be a logged on Windows User.");
        }
    }
Run Code Online (Sandbox Code Playgroud)

最后你可以通过这种方式检查访问:

    public const string EmptyResource = "myapplication";

    public void DoSomethingRestricted()
    {
        if (!ClaimPermission.CheckAccess("something_restricted", EmptyResource))
        {
            // error here
        }
        else
        {
            // do your really phat stuff here
        }
    }
Run Code Online (Sandbox Code Playgroud)

或者,除了例外:

private static ClaimPermission RestrictedActionPermission = new ClaimPermission(EmptyResource, "something_restricted");

public void DoSomethingRestrictedDemand()
{
    RestrictedActionPermission.Demand();

    // play up, from here!
}
Run Code Online (Sandbox Code Playgroud)

声明:

    [ClaimPermission(SecurityAction.Demand, Operation = "something_restricted", Resource = EmptyResource)]
    public void DoSomethingRestrictedDemand2()
    {
        // dostuff
    }
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • @Josh W:您可以使用AppDomain.CurrentDomain.SetThreadPrincipal(newPrincipal)设置您的主体,因此从那时起您的主体将用于每个新创建的线程,您不必担心,只需使用Thread.CurrentPrincipal.IsInRole( ). (3认同)