티스토리 뷰

반응형

지난번에 만들었던 WP7Host 프로젝트를 업그레이드해서 실버라이트에서 아이디와 비밀번호를 입력 받아서 SQL CE 4.0 sdf파일로 asp.net membership 인증을 하고, 인증이 되었을 때 WCF RIA Service에서 인증 확인을 해서 실행여부를 판단하고, 에러 메시지 처리를 어떻게 하는지 종합적으로 적어 보도록 하겠다.

다루고자하는 내용이 좀 방대한 내용이라, 아주 자세한 설명은 생략 하도록 하겠다. 대신, 참고 포스트들을 잘 참고하면 충분히 만들 수 있을 것이라 생각한다.

0. 연결 포스트
http://kaki104.tistory.com/entry/WCF-RIA-Service-hosting-SOAP-endpoint
http://kaki104.tistory.com/entry/Silverlight-5-WCF-RIA-Service-SQL-CE-40

1. 참고 포스트

1) Authentication in Business Application in Silverlight 4
http://www.c-sharpcorner.com/uploadfile/dpatra/authentication-in-business-application-in-silverlight-4/
비지니스 어플리케이션을 이용해서 인증을 하는 기본적인 방법에 대해서 설명해 놓았다. 한번 해보면 어떤 것인지 이해할 수 있다.

2) ASP.NET Universal Providers 1.0.1
http://nuget.org/packages/System.Web.Providers
ASP.NET 4.0에서 SQL Server 2005부터 SQL Azure까지 모든 공급자를 제공. 즉, SQL CE 4.0 공급자도 포함을 하고 있다. 이 것을 먼저 설치 해야한다. (단, Entity Framework 4.1, Entity Framework 4.2 June CTP 버전을 사용 한다면, 이 공급자 없이도 직접 접근이 가능하나, 만들어서 제공해 주는 것이니 부담 가지지 말고 그냥 설치해서 사용한다.)

3) How to: Enable Authentication in RIA Services
http://msdn.microsoft.com/en-us/library/ee707353%28VS.91%29.aspx
WCF RIA Service를 이용해서 인증 서비스를 사용하는 방법에 대한 자세한 설명이 나온다.

4) Silverlight and WCF RIA Services (5-Authentication)
http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2010/07/02/silverlight-and-wcf-ria-services-5-authentication.aspx
진짜 자세하게 설명해 놓았다. 너무 자세한 설명이라..약간 길지만..내용은 좋다.

5) ASP.NET SQL Server Registration Tool (Aspnet_regsql.exe)
http://msdn.microsoft.com/en-us/library/ms229862(v=vs.80).aspx
membership 데이터 베이스를 생성해주는 툴에 대한 사용법이다.(SQL Server 필요)


2. .Net Framework 4.0에서 SQL CE 4.0 파일 바로 접근하기
Entity Model을 사용한 접근 방법이 아닌 직접 SQL CE 4.0 파일에 접근을 해야하기 때문에, 이 부분을 먼저 해결해야 한다.

3. WP7Host.Web

1) Web.config
설치를 하면 Web.config에 몇가지가 추가된다.(자동으로 추가가 되는 부분도 있지만, 수동으로 추가해 주어야 하는 부분도 있으니 참고하기 바란다)

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <!--configSection-->
  <configSections>
    <sectionGroup name="system.serviceModel">
      <section name="domainServices" type="System.ServiceModel.DomainServices.Hosting.DomainServicesSection, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <!--connectionStrings-->
  <connectionStrings>
    <add name="WP7ModelContainer" connectionString="metadata=res://*/WP7Model.csdl|res://*/WP7Model.ssdl|res://*/WP7Model.msl;provider=System.Data.SqlServerCe.4.0;provider connection string=&quot;Data Source=|DataDirectory|\WP7CE40.sdf&quot;" providerName="System.Data.EntityClient" />
    <add name="DefaultConnection"
         connectionString="Data Source=|DataDirectory|\SqlCeAspnetdb.sdf;"
         providerName="System.Data.SqlServerCe.4.0" />

  </connectionStrings>
  <!--system.web-->
  <system.web>
    <!--httpModules-->
    <httpModules>
      <add name="DomainServiceModule" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </httpModules>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </assemblies>
    </compilation>
    <!--membership-->
    <membership defaultProvider="DefaultMembershipProvider">
      <providers>
        <clear />
        <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>

    <!--rolmanager-->
    <roleManager enabled="true" defaultProvider="DefaultRoleProvider">
      <providers>
        <clear />
        <add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </roleManager>
    <!--profile-->
    <profile defaultProvider="DefaultProfileProvider">
      <providers>
        <clear />
        <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
      <properties>
        <add name="FriendlyName" />
      </properties>
    </profile>
    <!--authentication 인증관련 부분으로 forms name은 직접 수정해 주어야 한다. -->
    <authentication mode="Forms">
      <forms name=".WP7Host_ASPXAUTH" timeout="2880" />
    </authentication>

    <sessionState mode="InProc" customProvider="DefaultSessionProvider">
      <providers>
        <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </sessionState>
  </system.web>
  <!--system.webServer-->
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="DomainServiceModule" preCondition="managedHandler" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </modules>
    <validation validateIntegratedModeConfiguration="false" />
  </system.webServer>
  <!--system.serviceModel-->
  <system.serviceModel>
    <domainServices>
      <endpoints>
        <add name="OData" type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add name="Soap" type="Microsoft.ServiceModel.DomainServices.Hosting.SoapXmlEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
        <add name="Json" type="Microsoft.ServiceModel.DomainServices.Hosting.JsonEndpointFactory, Microsoft.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </endpoints>
    </domainServices>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <!--system.data-->
  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SqlServerCe.4.0"/>
      <add name="Microsoft SQL Server Compact Edition Client Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact Edition Client 4.0" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"/>
    </DbProviderFactories>
  </system.data>
</configuration>

2) 인증 관련 추가 사항
여기에서 추가한 파일들은 New Project를 했을 때 BusinessApplication Template 프로젝트를 만들면 생성되는 파일들이다. 단, SqlCeAspnetdb.sdf 파일은 다른 프로젝트에서 사용했던 것을 복사해 왔다.

Services 폴더에
AuthenticationService.cs                          //인증 서비스
UserRegistrationService.cs                      //사용자 등록 서비스

Resources 폴더에
RegisterationDataResources.resx             //사용자 등록시 사용하는 리소스
RegisterationDataResources.Designer.cs 
ValidationErrorResources.Designer.cs      //사용자 등록시 사용하는 데이터 체크 리소스
ValidationErrorResources.resx

Models 폴더에
User.cs                                                //사용자 클래스
RegistrationData.cs                               //등록 데이터 클래스
Shared 폴더에
User.Shared.cs

App_Data 폴더에
SqlCeAspnetdb.sdf                               //인증 db

서버쪽 작업은 이정도면 완료된 것 같다. 각 파일의 내용은 아래 소스를 받은 후 열어서 보기 바란다.

4. WP7Host

1) App.xaml.cs

  public App()
  {
      this.Startup += this.Application_Startup;
      this.Exit += this.Application_Exit;
      this.UnhandledException += this.Application_UnhandledException;

      InitializeComponent();
     
      //웹컨텍스트를 이용해서 인증을 한다.
      WebContext webContext = new WebContext();
      //폼인증 방식 사용
      webContext.Authentication = new FormsAuthentication();
      this.ApplicationLifetimeObjects.Add(webContext);

  }

  private void Application_Startup(object sender, StartupEventArgs e)
  {
      //웹컨텍스트를 리소스로 추가
      this.Resources.Add("WebContext", WebContext.Current);
      //자동으로 로그인 - 이 페이지에 다시 들어왔을 경우 로그인 유저가 있으면 로드함
      WebContext.Current.Authentication.LoadUser(this.Application_UserLoaded, null);

      this.RootVisual = new MainPage();
  }

  private void Application_UserLoaded(LoadUserOperation operation)
  {
      if (operation.HasError == false)
      {}
      else
      {}
  }


....

  private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
  {
      // If the app is running outside of the debugger then report the exception using
      // the browser's exception mechanism. On IE this will display it a yellow alert
      // icon in the status bar and Firefox will display a script error.
      if (!System.Diagnostics.Debugger.IsAttached)
      {

          // NOTE: This will allow the application to continue running after an exception has been thrown
          // but not handled.
          // For production applications this error handling should be replaced with something that will
          // report the error to the website and stop the application.
          e.Handled = true;
          //예외 처리
          switch (e.ExceptionObject.GetType().ToString())
          {
              case "DomainOperationException":
              case "DomainException":
                  break;
              default:
                  Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); });
                  break;
          }

      }
  }

2) MainPageViewModel.cs

  //생성자
  public MainPageViewModel()
  {
      Model = new WP7Model();

      if (!DesignerProperties.IsInDesignTool)
      {
          Observable.FromEventPattern<PropertyChangedEventArgs>(Model, "PropertyChanged")
                    .ObserveOn(Scheduler.ThreadPool)
                    .ObserveOnDispatcher()
                    .Subscribe(prop =>
                    {
                        switch (prop.EventArgs.PropertyName)
                        {
                            case "MessageData":
                                this.MessageData = ((WP7Model)prop.Sender).MessageData;
                                break;
                        }
                    });

          WebContext.Current.Authentication.LoggedIn += new EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
          WebContext.Current.Authentication.LoggedOut += new EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);

      }

      IsLogin = false;
  }

  void Authentication_LoggedOut(object sender, System.ServiceModel.DomainServices.Client.ApplicationServices.AuthenticationEventArgs e)
  {
      Display();
  }

  void Authentication_LoggedIn(object sender, System.ServiceModel.DomainServices.Client.ApplicationServices.AuthenticationEventArgs e)
  {
      Display();
  }


  private ICommand loginCommand;
  /// <summary>
  /// 로그인 커맨드
  /// </summary>
  public ICommand LoginCommand
  {
      get
      {
          if (loginCommand == null)
          {
              loginCommand = new ActionCommand(() =>
              {
                  // 파라미터로 넘겨줄 아이디와 패스워드
                  LoginParameters lp = new LoginParameters(UserId, UserPW, false, null);

                  WebContext.Current.Authentication.Login(lp, load =>
                  {
                      if (load.HasError == false && load.LoginSuccess == true)
                      {
                      }
                      else
                      {
                          MessageBox.Show("로그인 실패");
                      }
                  }, null);   
                    
              }
              );
          }
          return loginCommand;
      }
  }

  private ICommand logoutCommand;
  /// <summary>
  /// 로그아웃 커맨드
  /// </summary>
  public ICommand LogoutCommand
  {
      get
      {
          if (logoutCommand == null)
          {
              logoutCommand = new ActionCommand(() =>
              {
                  WebContext.Current.Authentication.Logout(load =>
                  {
                      //Display();
                  }, null);

              }
              );
          }
          return logoutCommand;
      }
  }

  private void Display()
  {
      if (WebContext.Current.User.IsAuthenticated == true)
      {
          IsLogin = true;
          UserName = WebContext.Current.User.DisplayName;
          MessageData = "로그인에 성공 했습니다.";
      }
      else
      {
          IsLogin = false;
          MessageData = "로그인이 필요 합니다.";
      }

  }

5. 실행 결과 확인
1) 로그인 전

2) 로그인 후


6. 인증 데이터로 WCF RIA Service 제한과 에러 메시지 출력

WP7DomainService.cs

  [RequiresAuthentication(ErrorMessage="로그인이 필요한 서비스 입니다.")]
  [RequiresRole("Administrator", ErrorMessage="관리자만 삭제가 가능합니다.")]

  public void DeleteWP7UriList(WP7UriList wP7UriList)
  {
      if ((wP7UriList.EntityState != EntityState.Detached))
      {
          this.ObjectContext.ObjectStateManager.ChangeObjectState(wP7UriList, EntityState.Deleted);
      }
      else
      {
          this.ObjectContext.WP7UriListSet.Attach(wP7UriList);
          this.ObjectContext.WP7UriListSet.DeleteObject(wP7UriList);
      }

      //히스토리 삭제
      this.ObjectContext.WP7HistorySet
              .Where(p => p.WP7UriListId == wP7UriList.Id)
              .ToList().ForEach(p =>
              {
                  this.ObjectContext.WP7HistorySet.Attach(p);
                  this.ObjectContext.WP7HistorySet.DeleteObject(p);
              });
  }

로그인 하지 않은 상태에서 삭제 버튼 클릭

kaki104 (Basic 권한)로 로그인 한 후에 삭제 버튼 클릭

administrator로 로그인 한 후 삭제 버튼 클릭


6. 중요한 내용은 모두 작성한 것 같다. 더 자세한 사항은 소스에 있는 주석을 참고 하기 바란다.


** 소스 크기 제한 때문에 WP7Host Silverlight 프로젝트의 Bin 폴더의 내용은 모두 삭제한 상태임
반응형
댓글