• WCF安全之ASP.NET兼容模式


    本文是利用ASP.NET兼容模式实现WCF安全的一个完整示例,其中用到了ASP.NET的Forms身份验证及Membership,并启用了角色管理。

    由于整套安全方案完全利用ASP.NET相关功能实现,而未用到WCF安全策略相关的包括WCF身份验证、WCF授权及WCF传输安全等元素,所以严格的说,这种模式不能算是WCF的安全模式,但该方案确实实现了特定应用场景下的WCF安全。

    相比而言,该方案提供的安全程度比WCF的安全策要低一些(例如,未提供全过程的数据传输安全),因此,本方案适应对安全性要求不高的,以IIS为宿主的WCF应用。

    本方案中的WCF服务需要以IIS为宿主,可以通过添加“启用Silverlight功能的WCF服务”的方式建立WCF服务。客户端为Silverlight,并在访问WCF服务时使用了Visual Studio 2008自动生成的代理类。

    1、建立项目

    通过创建“Silverlight应用程序”建立新的项目WcfSecSample,并建立承载该Silverlight的网站WcfSecSample.Web。

    2、在Web项目中建立Service目录,并在该目录下添加WCF服务WeatherService,服务类的完整代码如下:

    using System;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.Collections.Generic;
    using System.Text;
    using System.Web;
    using System.Web.Security;
    
    namespace WcfSecSample.Web
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class WeatherService
        {
            private static string s_weather = "Sunny";
    
            [OperationContract]
            public void SetWeather(string weather)
            {
                if (!HttpContext.Current.User.IsInRole("Admin")) throw new ApplicationException("无权限。");
    
                s_weather = weather;
            }
    
            [OperationContract]
            public string GetWeather()
            {
                if (!HttpContext.Current.User.IsInRole("Guest")) throw new ApplicationException("无权限。");
    
                return s_weather;
            }
        }
    }

    3、在Web项目中添加用于登录的WCF服务LoginService,该服务类的完整代码如下:

    using System;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.Collections.Generic;
    using System.Text;
    using System.Web.Security;
    
    namespace WcfSecSample.Web
    {
        [ServiceContract(Namespace = "")]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class LoginService
        {
            [OperationContract]
            public bool Login(string userName, string password)
            {
                bool isValid = Membership.ValidateUser(userName, password);
    
                if (isValid) FormsAuthentication.SetAuthCookie(userName, false);
    
                return isValid;
            }
    
            [OperationContract]
            public void SignOut()
            {
                FormsAuthentication.SignOut();
            }
        }
    }

    4、在Web项目中添加继承自MembershipProvider类的CustomerMembershipProvider类,暂时只实现了本方案所需要的ValidateUser方法。ValidateUser方法的代码如下:

            public override bool ValidateUser(string username, string password)
            {
                return username == "admin" && password == "123456" || username == "guest";
            }

    5、在Web项目中添加继承自RoleProvider类的CustomRoleProvider类,暂时只实现了本方案所需要的GetRolesForUser方法。GetRolesForUser方法的代码如下:

            public override string[] GetRolesForUser(string username)
            {
                if (username == "admin") return new []{"Admin", "Guest"};
                return new[] { "Guest" };
            }

    需要注意的是,虽然凭感觉HttpContext.Current.User.IsInRole方法应该最终调用RoleProvider类的IsUserInRole方法,但事实却是最终调用了RoleProvider类的GetRolesForUser方法完成的。if (!HttpContext.Current.User.IsInRole("Admin"))还可以换成if (Roles.IsUserInRole(HttpContext.Current.User.Identity.Name, "Admin")),同样是最终调用了RoleProvider类的GetRolesForUser方法。

    6、配置Web.config文件

    在system.web节内添加如下内容:

        <authentication mode="Forms" >
          <forms name=".sec" loginUrl="LoginService.svc"></forms>
        </authentication>
    
        <membership defaultProvider="default">
          <providers>
            <add name="default" type="WcfSecSample.Web.CustomerMembershipProvider, WcfSecSample.Web"/>
          </providers>
        </membership>
    
        <roleManager defaultProvider="default" enabled="true">
          <providers>
            <add name="default" type="WcfSecSample.Web.CustomRoleProvider, WcfSecSample.Web"/>
          </providers>
        </roleManager>

    以上三节内容分别配置了身份验证模式、MembershipProvider及RoleProvider。

    然后在示例服务所在的Service目录下添加Web.config文件,禁止对Service目录的匿名访问。该文件的内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.web>
          <authorization>
            <deny users="?"/>
          </authorization>
        </system.web>
    </configuration>

    至此,服务端的工作就完成了,接下来建立客户端测试示例。

    7、添加服务引用。

    在Silverlight项目中添加对WeatherService的服务引用WeatherServiceRef。需注意的是进行该操作时需要暂时允许对Service目录的匿名访问。

    8、在MainPage中添加测试代码。完成之后的代码如下:

    MainPage.xaml文件:

    <UserControl x:Class="WcfSecSample.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
        <Grid x:Name="LayoutRoot" Width="200" Margin="100" >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" ></ColumnDefinition>
                <ColumnDefinition Width="*" ></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" Margin="5">UserName:</TextBlock>
            <TextBox Grid.Row="0" Grid.Column="1" Margin="5" Name="txtUserName" Text="admin" ></TextBox>
            <TextBlock Grid.Row="1" Grid.Column="0" Margin="5">Password:</TextBlock>
            <TextBox  Grid.Row="1" Grid.Column="1" Margin="5" Name="txtPassword" Text="123456"></TextBox>
            <Button Name="btnLogin"  Grid.Row="2" Grid.ColumnSpan="2" Margin="5" Content="Login" Click="Login"></Button>
            <Button Name="btnSignOut" Grid.Row="3" Grid.ColumnSpan="2" Margin="5" Content="SignOut" Click="SignOut"></Button>
            <Button Name="btnSetWeather" Grid.Row="4" Grid.ColumnSpan="2" Margin="5" Content="SetWeather" Click="SetWeather"></Button>
            <Button Name="btnGetWeather" Grid.Row="5" Grid.ColumnSpan="2" Margin="5" Content="GetWeather" Click="GetWeather"></Button>
        </Grid>
    </UserControl>
        

    MainPage.xaml.cs文件:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using WcfSecSample.LoginServiceRef;
    using WcfSecSample.WeatherServiceRef;
    
    namespace WcfSecSample
    {
        public partial class MainPage : UserControl
        {
            public MainPage()
            {
                InitializeComponent();
    
                this.IsLogin = false;
    
                this.client.SetWeatherCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_SetWeatherCompleted);
                this.client.GetWeatherCompleted += new EventHandler<GetWeatherCompletedEventArgs>(client_GetWeatherCompleted);
    
                this.loginClient.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(loginClient_LoginCompleted);
                this.loginClient.SignOutCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(loginClient_SignOutCompleted);
            }
    
            private bool isLogin;
            private WeatherServiceClient client = new WeatherServiceClient();
            private LoginServiceClient loginClient = new LoginServiceClient();
    
            private bool IsLogin
            {
                get { return isLogin; }
                set
                {
                    isLogin = value;
                    this.btnLogin.IsEnabled = !isLogin;
                    this.btnSignOut.IsEnabled = isLogin;
                }
            }
    
            private void Login(object sender, RoutedEventArgs e)
            {
                this.loginClient.LoginAsync(this.txtUserName.Text, this.txtPassword.Text);
            }
    
            private void SignOut(object sender, RoutedEventArgs e)
            {
                this.loginClient.SignOutAsync();
            }
    
            private void GetWeather(object sender, RoutedEventArgs e)
            {
                this.client.GetWeatherAsync();
            }
    
            private void SetWeather(object sender, RoutedEventArgs e)
            {
                this.client.SetWeatherAsync("Cloudy");
            }
    
            void loginClient_LoginCompleted(object sender, LoginCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    MessageBox.Show(e.Result ? "Login succeed." : "Login faild.");
                    this.IsLogin = e.Result;
                }
                else
                {
                    MessageBox.Show(e.Error.Message);
                }
            }
    
            void loginClient_SignOutCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    MessageBox.Show("SignOut.");
                    this.IsLogin = false;
                }
                else
                {
                    MessageBox.Show(e.Error.Message);
                }
            }
    
            void client_GetWeatherCompleted(object sender, GetWeatherCompletedEventArgs e)
            {
                if (e.Error == null) MessageBox.Show(e.Result);
                else MessageBox.Show(e.Error.Message);
            }
    
            void client_SetWeatherCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    MessageBox.Show("Set weather succeed.");
                }
                else
                {
                    MessageBox.Show(e.Error.Message);
                }
            }
        }
    }

    为了测试登录对访问服务的影响,以上代码并未根据登录状态对SetWeather、GetWeather按钮的可用性进行控制。

    运行示例,可以看到在登录之前访问WeatherService是不成功的,如果用Admin角色的账号登录之后可以SetWeather或GetWeather,如果用Guest角色的账号登录则只能GetWeather。登录并调用GetWeather的效果图如下:

    image

    示例测试环境:

    操作系统:Windows7

    开发环境:Visual Studio 2008 + Silverlight 3

    IIS:7.5

    浏览器:IE8

  • 相关阅读:
    【NXOpen.UF扩展】修改表达式
    NX二次开发 克隆
    C++手动加载CLR运行托管程序(CLR Hosting)
    C++/CLR 使用(VS2012,VS2013,VS2015)编写
    解决VS2015安装后stdio.h ucrtd.lib等文件无法识别问题,即include+lib环境变量配置
    NX 图标
    【错误分析】NX error status: 32
    NX CAM 读取加工参数
    CAM 模板样式表
    锁定NX原生界面 & 锁定界面更新
  • 原文地址:https://www.cnblogs.com/chinadhf/p/1724388.html
Copyright © 2020-2023  润新知