在WinForms中验证用户(与ASP.Net无关) [英] Authenticate user in WinForms (Nothing to do with ASP.Net)

查看:167
本文介绍了在WinForms中验证用户(与ASP.Net无关)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



简介



我需要密码保护我应用程序中的一些操作,如加载/保存文件,点击复选框等。这是一个标准的C#



我将要滚动我自己的非常基本的系统(读取obfuscation与大开后门).Net 4.0,WinForms应用程序将运行在Windows 7在公司网络。与用户/密码/权限的文本文件(哈希和盐化)直到一些搜索后,我发现看起来像一个
诱人简单方法,但我无法在不是有关ASP.NET的角色找到一个很好的教程。



问题



因此,任何人都知道一个或多个教程,告诉我如何:


  1. 创建Windows用户/组,并为该用户/组授予角色或权限。

    • 请注意,我是从我公司的网络笔记本电脑进行测试,但会将其部署在客户的公司网络上(不确定这是否是一个问题,


  2. 创建winforms / console应用程序示例,即使只有一个方法打印Hello World

  3. 我从来没有做过网络管理员或任何相关的事情,我一直在阅读关于Active Directory和本地用户与网络用户...我希望一个方法,其中我可以构建一个接口,只要问Windows,如果当前用户有权限 ABC ,不要太在乎Windows如何认为出来。然后我可以为每个Local / Network / ActiveDirectory / etc做一个具体的实现。


  4. 背景



       - 如果感兴趣则阅读,但不需要回答问题

    只是为了确保我在这里正确的方向,基本上我需要/想测试这个在我的开发电脑,以确保它会为我的客户提供良好的最终用户体验。问题是,目前他们为运行我的应用程序的每台计算机运行一个自动登录脚本,有几个不同的运算符在一天内使用我的应用程序。客户希望我的应用程序的某些功能的密码保护,只提供给某些操作员。我没有问题适合这个,因为我预计的请求了一段时间,我只是以前没有编程认证。



    我认为这是值得说服我的客户给每个运营商自己的网络帐户,并分配任何他们想要的操作员或组的权限,以防他们需要触发某人,更改权限等。这也意味着我只为他们打开几个选项,他们可以分组权限,但是他们认为合适的基于内部的公司策略,我真的不应该担心(但如果我必须自己滚动,因为他们的IT部门几乎没有什么我的应用程序)。

    从我可以告诉它,也使我的生活更容易,不必处理哈希密码和加密等,只需处理哪个角色需要点击这个或该按钮。

    解决方案

    首先,你必须确定,如果你真的想要一个简单的基于角色的身份验证(您可能想阅读: http://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-authorization-checks-do-activity-based-checks/



    如果你确定它是绝对足够,你已经在正确的方式与您提供的SO链接在您的问题。这是有点混乱,默认情况下不支持角色在Windows中,但有组。组可以是本地或远程(例如ActiveDirectory),因此管理员可以将用户分配到特定于您的应用程序的特定组(示例如下: http://msdn.microsoft.com/en-us/library/ms731200(v = vs.110).aspx < a>)



    一个关键是:你必须准备你的应用程序的中心主体,因此用当前用户支持的角色填充它。



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

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

    命名空间WindowsPrincipalTrial
    {
    public class Program
    {
    //你也可以将这些定义移动到配置文件
    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(必须有Windows用户登录。
    }
    }

    私人静态字符串[] getGroupCorrespondingRoles(WindowsIdentity id)
    {
    //你也可以使用LINQ
    var allMappedRoleNames = new List< string>();

    string roleName;

    foreach(id.Groups中的var grp)
    {
    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;
    }
    }

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

      public void DoSomethingSpecial()
    {
    if(Thread.CurrentPrincipal.IsInRole(MyRoles.Extended))
    {
    //做你的东西
    }
    else
    {
    / /可能显示错误
    }
    }

    p>

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

    // do stuff
    }

    正如从ASP.NET中所知:

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

    后面两个例子的丑陋的事情是,他们抛出异常,当正确的角色没有打。



    所以角色和组之间的映射你必须根据您要使用的系统(本地组,AD组,LDAP组等)在您的应用程序开始时进行。



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



    UPDATE:



    ,我将尝试简而言之,如何通过使用Thinktecture的IdentityModel实现它。



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



    首先你需要一个 ClaimAuthorizationManager

      public class MyClaimsAuthorizationManager:ClaimAuthorizationManager 
    {
    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))));

    返回结果;

    }

    //我在这里忽略资源,修改这个,如果你需要
    私有字符串getActionNameFromAuthorizationContext(AuthorizationContext context)
    {
    return context.Action
    .Where(claim => claim.ClaimType.Equals(ClaimPermission.ActionType))
    .Select(claim => claim.Value)
    。 FirstOrDefault();
    }
    }

    正如您可能已经猜到的, IActivityRoleMapper 是类的接口,返回所有角色的名称,包括给定操作的权限。
    这个类是非常个人的,我想你会找到你的方式实现它,因为这不是这里的点。你可以通过硬编码,从xml或从数据库加载。如果你想为action @资源对提供权限请求,你还得改变/扩展它。



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

     使用Thinktecture.IdentityModel; 
    使用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();

    //只是你想要的,记住用户名
    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(); //你必须实现

    //注册你自己的授权管理器,因此IdentityModel将使用它默认
    FederatedAuthentication.ServiceConfiguration.ClaimsAuthorizationManager = new MyClaimsAuthorizationManager(roleMapper);
    }
    else
    {
    throw new NotSupportedException(必须有Windows用户登录。
    }
    }

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

      public const string EmptyResource =myapplication; 

    public void DoSomethingRestricted()
    {
    if(!claimPermission.CheckAccess(something_restricted,EmptyResource))
    {
    //错误b $ b}
    else
    {
    //你真正的phat的东西在这里
    }
    }

    或者,除非:

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

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

    //播放,从这里开始!
    }

    声明性:



    <$
    public void DoSomethingRestrictedDemand2()
    {
    // dostuff
    }

    希望这有帮助。


    Note: Cross-posted to ServerFault, based on comments.

    Intro

    I need to password protect some actions in my application, such as loading/saving files, clicking check-boxes, etc. This is a standard C# .Net 4.0, WinForms application which will run on Windows 7 in a corporate network.

    I was about to roll my own very basic system (read obfuscation with wide open backdoors) with a text file of users/passwords/permissions (hashed and salted) until after some searching I found what looks like a tantalizingly simple approach , but I'm having trouble finding a good tutorial on Roles that isn't about ASP.NET.

    Question

    So does anyone know of one or more tutorials that show me how to:

    1. Create a Windows User/Group and give that User/Group a Role or Permission.
      • Note that I'm testing this from my company's networked laptop, but will deploy it on the customer's corporate network (Not sure if this is an issue, or how tricky this will get).
    2. Create winforms/console app sample with even just a single method that prints "Hello World" if I'm authenticated or throws an exception if I'm not?
    3. I've never done Network Admin or anything related and I keep reading about Active Directory and Local Users Vs Networked Users... I was hoping for an approach where I could build to an Interface and just ask Windows if the current user has permission ABC and not care too much about how Windows figured that out. Then I can make a concrete implementation for each Local/Network/ActiveDirectory/etc. use case as required (or if required... as I don't even know that right now).

    Background

    - read if interested, but not required to answer question
    

    Just to make sure I'm going in the right direction here, basically I need/want to test this on my development PC to make sure it's going to have a good end-user experience for my customer. The problem is that currently they run an Auto-login script for each computer that runs my application and there are several different operators that use my application throughout the day. The customer wants password protection on certain features of my app and only provide that to certain operators. I have no problem fitting this in, as I've expected the request for a while, I just haven't ever programmed authentication before.

    I think it's worthwhile to convince my customer to give each operator their own network account and assign whatever permissions they want to that operator or group, in case they need to fire somebody, change permissions, etc. It also means I just open several options for them and they can group those permissions however they see fit based on internal corporate policies, which I really shouldn't have to be worried about (but will be if I have to roll my own, as they're IT department knows almost nothing of my application).

    From what I can tell it also makes my life a lot easier by not having to deal with hashing passwords and encryption, etc. and just handle which Role is required to click this or that button.

    解决方案

    First of all, you'd have to determine, if you really want a simple role-based-authentication (you may want to read: http://lostechies.com/derickbailey/2011/05/24/dont-do-role-based-authorization-checks-do-activity-based-checks/)

    If you're sure it's absolutely sufficient, you're already on the right way with the SO link you provided in your question. It's kind of confusing that there is no support of 'roles' by default in Windows, but there are groups. Groups can be local or remote (e.g. ActiveDirectory), so an admin could assign users to certain groups, that are specific for your application (for an example look here: http://msdn.microsoft.com/en-us/library/ms731200(v=vs.110).aspx)

    One key is: You have to prepare your application's central principal, hence fill it with roles, supported for the current user.

    Therefore, On the very startup of your application you then check the current active user and set your application wide principal and role(s). This may look like this (just a very simple example):

    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";
        }
    }
    

    Then your Application-Principal is set up. Now you could check access in your code like this:

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

    Or more drastically:

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

    what is possible even declarative, as known from ASP.NET:

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

    The ugly thing with the latter two examples is, that they throw exceptions, when the right role isn't hit.

    So the mapping between roles and groups you have to do quite at the start of your app, according to the systems you want to use (local groups, AD groups, LDAP groups etc.).

    If you, however, prefer authentication with actions and roles, after all, have a look at Windows Identity Foundation and Claims Based Authorization! There are already some ready-to-use frameworks out there (e.g. https://github.com/thinktecture/Thinktecture.IdentityModel).

    UPDATE:

    When it comes to activity based and thereby claims based authorization, I will try in short, how you could achieve it, by using Thinktecture's IdentityModel.

    Generally that approach still uses roles internally, but has a kind of translation layer in between. Thinktecture already encapsulates many things needed. Authorization checks in code are then done via claim permissions. They are technically kind of request for an access to a certain resource. For the sake of simplicity I limit my example for actions only, by using one single default resource (since ClaimPermission doesn't accept an empty resource). If you want to use action@resource pairs, you'd have to modify the code respectively.

    At first you need a 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();
            }
        }
    

    As you may have guessed, IActivityRoleMapper is an interface for a class, that returns the names of all roles, that include permission for a given action. This class is very individual and I guess you'll find your way implementing it, because it's not the point here. You could do it by hardcoding, loading from xml or from a database. Also you would have to change/extend it, if you wanted to you action@resource pairs for permission requests.

    Then you'd have to change the code in main() method to:

    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.");
            }
        }
    

    Finally you can check access this way:

        public const string EmptyResource = "myapplication";
    
        public void DoSomethingRestricted()
        {
            if (!ClaimPermission.CheckAccess("something_restricted", EmptyResource))
            {
                // error here
            }
            else
            {
                // do your really phat stuff here
            }
        }
    

    Or again, with exceptions:

    private static ClaimPermission RestrictedActionPermission = new ClaimPermission(EmptyResource, "something_restricted");
    
    public void DoSomethingRestrictedDemand()
    {
        RestrictedActionPermission.Demand();
    
        // play up, from here!
    }
    

    Declarative:

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

    Hope this helps.

    这篇关于在WinForms中验证用户(与ASP.Net无关)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆