2009年11月25日水曜日

.NET ダイアログ画面をコントロールの位置に合わせて表示する

ダイアログ画面をテキストボックスの右上に合わせて表示します。
Using frm As New Form
 frm.StartPosition = FormStartPosition.Manual
 Dim p As Point = Me.TextBox1.PointToScreen(New Point(0, 0))
 p.X += Me.TextBox1.Width
 frm.Location = p
 frm.ShowDialog()
End Using

2009年11月12日木曜日

ASP.NET ハンドルされなかった例外を処理する

Global.asaxファイル

Global.asaxファイルをアプリケーション・ルート直下に追加します。
Application_Errorイベントにハンドルされなかった例外を処理するコードを記述します。

<%@ Application Language="VB" %>

<script runat="server">

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' アプリケーションのスタートアップで実行するコードです
    End Sub
    
    Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
        ' アプリケーションのシャットダウンで実行するコードです
    End Sub
        
    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        ' ハンドルされていないエラーが発生したときに実行するコードです
        
        'ログを出力します。
        Using writer As New System.IO.StreamWriter(Server.MapPath("~/MyError.log"), True)
            Dim err As Exception = Server.GetLastError.InnerException
            If err IsNot Nothing Then
                Dim text As New System.Text.StringBuilder
                text.Append(err.Source & Chr(13))
                text.Append(err.Message & Chr(13))
                text.Append(DateTime.Now.ToString & Chr(13))
                writer.WriteLine(text.ToString)
            End If
        End Using
        
        'カスタムエラーページを表示します。
        Server.Transfer("~/MyError.aspx")
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' 新規セッションを開始したときに実行するコードです
    End Sub

    Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
        ' セッションが終了したときに実行するコードです 
        ' メモ: Session_End イベントは、Web.config ファイル内で sessionstate モードが
        ' InProc に設定されているときのみ発生します。session モードが StateServer か、または 
        ' SQLServer に設定されている場合、イベントは発生しません。
    End Sub
       
</script>

カスタムエラーページを作成

ルート直下にMyError.aspxフォームを追加します。
今回はラベルを2つ配置し、Loadイベントで例外情報を出すようにしてみます。
Partial Class MyError
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles Me.Load
        Dim ex As Exception
        ex = Server.GetLastError
        If TypeOf ex Is HttpUnhandledException AndAlso ex.InnerException IsNot Nothing Then
            ex = ex.InnerException
        End If

        Me.Label1.Text = ex.GetType.ToString
        Me.Label2.Text = ex.Message

    End Sub
End Class

ASP.NET カスタムロールプロバイダを利用したアクセス制御

前回ASP.NETカスタムメンバーシッププロバイダを利用したログイン
Memberフォルダ配下のページはログインしていないユーザのアクセスを拒否するよう設定しました。
今回は新たにAdminフォルダを作成し、Adminフォルダ配下のページはAdminロールを持っているユーザのみアクセスを許可するように設定します。

前回のコードに追加していきます。

カスタム ロール プロバイダーの作成

まずカスタムロールプロバイダを作成します。
App_Codeに「CustomRoleProvider.vb」クラスを追加します。
CustomRoleProviderクラスはRoleProviderクラスを継承します。
抽象メソッドをオーバーラドしたメソッドが追加されますのでGetRolesForUserメソッドにロールを取得するコードを書きます。
その他のメソッドはNotSupportedExceptionをスローするようにしておきます。

Public Class CustomRoleProvider
    Inherits RoleProvider

    Public Overrides Property ApplicationName() As String
        Get
            Throw New NotSupportedException
        End Get
        Set(ByVal value As String)
            Throw New NotSupportedException
        End Set
    End Property

    Public Overrides Function GetRolesForUser(ByVal username As String) As String()
        '入力されたusernameを元に、ユーザのロールを取得します。
        return new String(){"Admin"}
    End Function

    Public Overrides Sub AddUsersToRoles(ByVal usernames() As String, ByVal roleNames() As String)
        Throw New NotSupportedException
    End Sub

    Public Overrides Sub CreateRole(ByVal roleName As String)
        Throw New NotSupportedException
    End Sub

    Public Overrides Function DeleteRole(ByVal roleName As String, ByVal throwOnPopulatedRole As Boolean) As Boolean
        Throw New NotSupportedException
    End Function

    Public Overrides Function FindUsersInRole(ByVal roleName As String, ByVal usernameToMatch As String) As String()
        Throw New NotSupportedException
    End Function

    Public Overrides Function GetAllRoles() As String()
        Throw New NotSupportedException
    End Function

    Public Overrides Function GetUsersInRole(ByVal roleName As String) As String()
        Throw New NotSupportedException
    End Function

    Public Overrides Function IsUserInRole(ByVal username As String, ByVal roleName As String) As Boolean
        Throw New NotSupportedException
    End Function

    Public Overrides Sub RemoveUsersFromRoles(ByVal usernames() As String, ByVal roleNames() As String)
        Throw New NotSupportedException
    End Sub

    Public Overrides Function RoleExists(ByVal roleName As String) As Boolean
        Throw New NotSupportedException
    End Function
End Class


web.configを編集

作成したカスタムロールプロバイダを登録します。
<!--ロールプロバイダ-->
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
 <providers>
  <add name="CustomRoleProvider" type="CustomRoleProvider"/>
 </providers>
</roleManager>

Adminフォルダ配下のページは非認証ユーザを拒否し、Adminロールのみ許可を与えます。
<!--adminフォルダ-->
<location path="admin">
 <system.web>
  <authorization>
   <deny users="?" />
   <allow roles="Admin" />
  </authorization>
 </system.web>
</location>

<?xml version="1.0"?>
 ・・・省略
 <system.web>
  ・・・省略
  <!--認証方法 Form認証-->
  <!--<authentication mode="Windows"/>-->
  <authentication mode="Forms">
   <forms loginUrl="Default.aspx"/>
  </authentication>
  <!--メンバシッププロバイダ-->
  <membership defaultProvider="CustomMembershipProvider">
   <providers>
    <add name="CustomMembershipProvider" type="CustomMembershipProvider"/>
   </providers>
  </membership>
  <!--ロールプロバイダ-->
  <roleManager enabled="true" defaultProvider="CustomRoleProvider">
   <providers>
    <add name="CustomRoleProvider" type="CustomRoleProvider"/>
   </providers>
  </roleManager>
     <!--すべてのユーザーにアクセス許可を与える-->
    <authorization>
   <allow users="*"/>
  </authorization>
 </system.web>
 <!--**個別フォルダ・ページのセキュリティ**-->
 <!--Memberフォルダ-->
 <location path="Member">
  <system.web>
   <authorization>
    <deny users="?" />
   </authorization>
  </system.web>
 </location>
 <!--adminフォルダ-->
 <location path="Admin">
  <system.web>
   <authorization>
    <deny users="?" />
    <allow roles="Admin" />
   </authorization>
  </system.web>
 </location>
</configuration>

2009年11月6日金曜日

ASP.NET カスタムメンバーシッププロバイダを利用したログイン

カスタム メンバーシップ プロバイダーの作成

まずカスタムメンバーシッププロバイダを作成します。
App_Codeに「CustomMembershipProvider.vb」クラスを追加します。
CustomMembershipProviderクラスはMembershipProviderクラスを継承します。
抽象メソッドをオーバライドしたメソッドが追加されますのでValidateUserメソッドに認証のコードを書きます。
その他のメソッドはNotSupportedExceptionをスローするようにしておきます。
Public Class CustomMembershipProvider
    Inherits MembershipProvider

    '-----Overridesプロパティ-----

    Public Overrides Property ApplicationName() As String
        Get
            Throw New NotImplementedException
        End Get
        Set(ByVal value As String)
            Throw New NotImplementedException
        End Set
    End Property

    Public Overrides ReadOnly Property EnablePasswordReset() As Boolean
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property EnablePasswordRetrieval() As Boolean
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property RequiresQuestionAndAnswer() As Boolean
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property RequiresUniqueEmail() As Boolean
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property MinRequiredNonAlphanumericCharacters() As Integer
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property MinRequiredPasswordLength() As Integer
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property PasswordAttemptWindow() As Integer
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property PasswordFormat() As System.Web.Security.MembershipPasswordFormat
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property PasswordStrengthRegularExpression() As String
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property MaxInvalidPasswordAttempts() As Integer
        Get
            Throw New NotImplementedException
        End Get
    End Property

    '-----Overrides メソッド-----
    ''' <summary>
    ''' ここでログイン認証のチェックを行う
    ''' </summary>
    ''' <param name="username"></param>
    ''' <param name="password"></param>
    Public Overrides Function ValidateUser(ByVal username As String, ByVal password As String) As Boolean
        'とりあえずTrueを返す。
        Return True
    End Function

    Public Overrides Function ResetPassword(ByVal username As String, ByVal answer As String) As String
        Throw New NotImplementedException
    End Function

    Public Overrides Function ChangePassword(ByVal username As String, ByVal oldPassword As String, ByVal newPassword As String) As Boolean
        Throw New NotImplementedException
    End Function

    Public Overrides Function ChangePasswordQuestionAndAnswer(ByVal username As String, ByVal password As String, ByVal newPasswordQuestion As String, ByVal newPasswordAnswer As String) As Boolean
        Throw New NotImplementedException
    End Function

    Public Overrides Function CreateUser(ByVal username As String, ByVal password As String, ByVal email As String, ByVal passwordQuestion As String, ByVal passwordAnswer As String, ByVal isApproved As Boolean, ByVal providerUserKey As Object, ByRef status As System.Web.Security.MembershipCreateStatus) As System.Web.Security.MembershipUser
        Throw New NotImplementedException
    End Function

    Public Overrides Function DeleteUser(ByVal username As String, ByVal deleteAllRelatedData As Boolean) As Boolean
        Throw New NotImplementedException
    End Function

    Public Overrides Function FindUsersByEmail(ByVal emailToMatch As String, ByVal pageIndex As Integer, ByVal pageSize As Integer, ByRef totalRecords As Integer) As System.Web.Security.MembershipUserCollection
        Throw New NotImplementedException
    End Function

    Public Overrides Function FindUsersByName(ByVal usernameToMatch As String, ByVal pageIndex As Integer, ByVal pageSize As Integer, ByRef totalRecords As Integer) As System.Web.Security.MembershipUserCollection
        Throw New NotImplementedException
    End Function

    Public Overrides Function GetAllUsers(ByVal pageIndex As Integer, ByVal pageSize As Integer, ByRef totalRecords As Integer) As System.Web.Security.MembershipUserCollection
        Throw New NotImplementedException
    End Function

    Public Overrides Function GetNumberOfUsersOnline() As Integer
        Throw New NotImplementedException
    End Function

    Public Overrides Function GetPassword(ByVal username As String, ByVal answer As String) As String
        Throw New NotImplementedException
    End Function

    Public Overloads Overrides Function GetUser(ByVal providerUserKey As Object, ByVal userIsOnline As Boolean) As System.Web.Security.MembershipUser
        Throw New NotImplementedException
    End Function

    Public Overloads Overrides Function GetUser(ByVal username As String, ByVal userIsOnline As Boolean) As System.Web.Security.MembershipUser
        Throw New NotImplementedException
    End Function

    Public Overrides Function GetUserNameByEmail(ByVal email As String) As String
        Throw New NotImplementedException
    End Function

    Public Overrides Function UnlockUser(ByVal userName As String) As Boolean
        Throw New NotImplementedException
    End Function

    Public Overrides Sub UpdateUser(ByVal user As System.Web.Security.MembershipUser)
        Throw New NotImplementedException
    End Sub

    
End Class

認証後に表示するページの作成

Memberフォルダを追加し、Top.aspxファイルを追加します。

ログインコントロールの配置

Default.aspxにログインコントロールを配置します。
ログインコントロールのDestinationPageUrlプロパティに「~/Member/Top.aspx」を指定します。
これで正しくログインできたときにMember/Top.aspxが表示されます。

web.configを編集

認証方法をフォーム認証に設定し、loginUrl要素にログインコントロールを配置したDefault.aspxを指定します。
<!--<authentication mode="Windows"/>-->
<authentication mode="Forms">
    <forms loginUrl="Default.aspx"/>
</authentication>

作成したカスタムメンバーシッププロバイダを登録します。
<!--メンバシッププロバイダ-->
<membership defaultProvider="CustomMembershipProvider">
 <providers>
  <add name="CustomMembershipProvider" type="CustomMembershipProvider"/>
 </providers>
</membership>

すべてのユーザにアクセス許可を与えるように設定します。
<!--すべてのユーザーにアクセス許可を与える-->
<authorization>
 <allow users="*"/>
</authorization>

Memberフォルダ配下のページは認証していないユーザのアクセスは拒否します。
<!--Memberフォルダ 非認証ユーザを拒否する-->
<location path="Member">
 <system.web>
  <authorization>
            <deny users="?"/>
     </authorization>
 </system.web>
</location>
authorization要素の
allow要素は「許可を与える」
deny要素は「拒否する」
users="*"は「すべてのユーザ」
users="?"は「認証していないユーザ」
となります。
<allow users="*"/>で、すべてのユーザにページアクセスの許可を与え
<deny users="?"/>で、非認証ユーザを拒否します。
user1に許可を与え、非認証ユーザを拒否するようにするには以下のように記述します。
<authorization>
    <allow user="user1"/>
    <deny user="?"/>
</authorization>

<?xml version="1.0"?>
    …省略
    <system.web>
        …省略
  <!-- 
            <authentication> セクションは、ユーザーを識別するため
            に、ASP.NET で使用されるセキュリティ認証モードの構成
            を有効にします。-->
  <!--認証方法 Form認証-->
  <!--<authentication mode="Windows"/>-->
  <authentication mode="Forms">
   <forms loginUrl="Default.aspx"/>
  </authentication>
  <!--メンバシッププロバイダ-->
  <membership defaultProvider="CustomMembershipProvider">
   <providers>
    <add name="CustomMembershipProvider" type="CustomMembershipProvider"/>
   </providers>
  </membership>
  <!--すべてのユーザーにアクセス許可を与える-->
  <authorization>
   <allow users="*"/>
  </authorization>
 </system.web>
 <!--**個別フォルダ・ページのセキュリティ**-->
 <!--Memberフォルダ 非認証ユーザを拒否する-->
 <location path="Member">
  <system.web>
   <authorization>
        <deny users="?"/>
   </authorization>
  </system.web>
 </location>
</configuration>

以上で設定は終了です。
IEでMember/Top.aspxにアクセスするとログインページにリダイレクトされます。
ログインページでユーザー名、パスワードを入力するとMember/Top.aspxに遷移します。