• 【已解决】谁动了我的CurrentPrincipal?求助我在给Artech的wcf petshop增加授权机制的时候遇到的问题。


    这个问题已解决,是绑定设置的问题,主要还是因为我自己没有深入理解WCF绑定的安全机制。在这篇博客里面我来说说怎么解决的。

    下载了Artech的wcf petshop源码(博文链接)并调试运行成功后,打算在其之上增加授权功能,但是遇到了问题。

    环境:windows8.1 企业版,VS2012, SQLExpress2008, IIS Express

    首先我在原文中求助A大关于通过RoleManager获取角色是否可行,得到了A大的答复可行,于是我就在原来的项目的Common中增加了一个静态类用于获取角色并设置到当前线程的CurrentPrincipal,和还原CurrentPrincipal。代码如下:

     1 namespace Artech.PetShop.Common
     2 {
     3     /// <summary>
     4     /// 根据当前用户名设置当前线程的Principal的类
     5     /// </summary>
     6     public static class SetThreadPrincipal
     7     {
     8         /// <summary>
     9         /// 用于保存修改之前的Principal
    10         /// </summary>
    11         private static IPrincipal _oldPrincipal;
    12 
    13         /// <summary>
    14         /// 设置当前线程的Principal,根据ApplicationContext里面的UserName是否存在来判断。
    15         /// 通过Roles来获取对应UserName的角色。并将其设置到当前线程的CurrentPrincipal
    16         /// </summary>
    17         public static void SetThreadPrincipalToCurrentUser()
    18         {
    19             string username = ApplicationContext.Current.UserName;
    20             if (!string.IsNullOrEmpty(username))
    21             {
    22                 // 如果用户名为空,则说明不可能有用户信息,那就不用去取角色并放到线程中。
    23                 IIdentity identity;
    24                 identity = new GenericIdentity(username, Membership.Provider.Name);
    25 
    26                 // TODO: Get Roles
    27                 string[] roles = Roles.GetRolesForUser(identity.Name);
    28                 IPrincipal principal = new GenericPrincipal(identity, roles);
    29 
    30                 // Place user's principal on the thread
    31                 _oldPrincipal = Thread.CurrentPrincipal;
    32                 Thread.CurrentPrincipal = principal;
    33             }
    34             else
    35             {// 如果用户名为空,则说明不可能有用户信息,那就不用去取角色并放到线程中。
    36                 // 这时候就不用设置oldPrincipal的值了。
    37                 _oldPrincipal = null;
    38             }
    39         }
    40 
    41         /// <summary>
    42         /// 恢复线程的Principal
    43         /// </summary>
    44         public static void ResumeThreadPrincipal()
    45         {
    46             if (_oldPrincipal != null)
    47                 Thread.CurrentPrincipal = _oldPrincipal;
    48         }
    49     }
    50 }

    从代码中可以看到,如果取到上下文中的用户名之后就可以用户名对应的角色(关于Roles类的使用请微软搜索“角色提供”)。

    然后我在ContextReceivalCallContextInitializer类的BeforeInvoke方法和AfterInvoke方法里面增加了对上面类的使用,代码如下:

    ContextReceivalCallContextInitializer类在Infrastructure项目的WCF Extensions文件夹下。

     1     public class ContextReceivalCallContextInitializer : ICallContextInitializer
     2     {
     3         private IPrincipal _oldPrincipal;
     4         #region ICallContextInitializer Members
     5 
     6         public void AfterInvoke(object correlationState)
     7         {
     8             // 恢复Principal
     9             SetThreadPrincipal.ResumeThreadPrincipal();
    10         }
    11 
    12         public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
    13         {
    14             ApplicationContext.Current = message.Headers.GetHeader<ApplicationContext>(
    ApplicationContext.ContextHeaderLocalName,
    ApplicationContext.ContextHeaderNamespace);
    15 // 设置Principal 16 SetThreadPrincipal.SetThreadPrincipalToCurrentUser(); 17 return null; 18 } 19 20 #endregion 21 }

    以上代码添加完成后,就可以见证奇迹了。点击启动调试(前提是已经配置成功可以运行)

    登录、浏览pet信息、放入购物车略过,不用说了。看下面的图,已经放入购物车。

    图:已经放入购物车

    下一步,点击结账后,页面调用wcf,会进入ContextReceivalCallContextInitializer类的BeforeInvoke方法:

    图:进入BeforeInvoke断点

    按下F10,单步执行到return。

    图:设置当前Principal结束

    从监视窗口可以看到,线程的CurrentPrincipal已经成功设置。用户名和角色都OK的。

    图:设置成功

    这时,我们按下F5让程序继续进行,来到业务处理的断点处,奇迹就发生了:

    图:来到业务处理

    查看监视窗口,现成的当前Principal变了!

    图:他竟然变成了我的windowsPrincipal!

    然后继续执行,来到ContextReceivalCallContextInitializer的AfterInvoke方法时,又变回来了。

    图:Principal回来了。

    我反复尝试了多次都是这样!抓狂了。

    如果我加上权限限制的代码,就会告诉我权限不对。

    图:加上限制

    再次执行就会在AuditCallHandler的Invoke方法里面看到异常:

    图:异常

    页面上也会告诉你,访问失败。

    我无法理解这个问题到底是为什么,只好找个变通的方法来解决,把设置线程Principal的代码放到了OrderService里面:

            [OperationBehavior(TransactionScopeRequired= true)]
            [AuditCallHandler("提交订单")]
            public void Submit(Order order)
            {
                // 设置Principal   
                SetThreadPrincipal.SetThreadPrincipalToCurrentUser();
                this.BusinessComponent.Submit(order);
                // 恢复Principal
                SetThreadPrincipal.ResumeThreadPrincipal();
            }    

    并将限制代码放到BusinessComponent的Submit里面:

            // 限制权限
            [PrincipalPermission(SecurityAction.Demand, Role = "User")]
            public void Submit(Order order)
            {
                this.ValidateInventory(order);
                this.DataAccess.Submit(order);
            }

    这样,再次进行订单结账操作就能成功了。

    这个问题到底是为什么呢。我真的百思不得其解啊。

  • 相关阅读:
    给View设置多个Tag
    Android studio 编译报错 Error:Error converting bytecode to dex:
    ViewPager设置自适应
    华为手机播放视频时seekBar拖不动
    Android studio 导入Eclipse项目 Vitamio播放器报错
    多线程Bug
    时间选择器,不同系统版本差异的解决办法
    Django测试pytest
    Nginx
    docker
  • 原文地址:https://www.cnblogs.com/Arnu/p/PrincipalSetFail.html
Copyright © 2020-2023  润新知