WebMatrix.WebData.WebSecurity を利用したユーザー認証(その2)

WebMatrix.WebData.WebSecurity を利用したユーザー認証(その1)」の続きです。翌日にでも書こうかと思っていたのですが、一週間後になってしまいました :mrgreen:

前回の記事で「利用者登録」と「ログイン」まで作成済みなので、今回は「パスワードの再発行」と「パスワードの変更」部分を作ります。

「パスワードの再発行」は次のような動きになります。

メールアドレスの入力
メール送付の通知画面
メールの内容
新しいパスワードの入力画面

送られてきたメールの本文にある「パスワード・リセットを行うページ」へのリンクをクリックするとブラウザに「新しいパスワードの入力画面」が開き、新しいパスワードをセットすることができます。

プログラムは、まずモデル AccountModels.cs から。

namespace TestAuthorize002.Models
{
 ~略~
    public class ForgotPasswordModel
    {
        [Required]
        [DataType(DataType.EmailAddress)]
        [Display(Name = "登録したメールアドレスを入力してください。")]
        public string Email { get; set; }
    }

    public class ResetPasswordModel
    {
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "パスワードと確認のパスワードが一致しません。")]
        public string ConfirmPassword { get; set; }

        public string ResetToken { get; set; }
    }
}

次に、コントローラー AccountController.cs です。

namespace TestAuthorize002.Controllers
{
    public class AccountController : Controller
    {
~略~
        //
        // GET: /Account/ForgotPassword

        public ActionResult ForgotPassword()
        {
            return View();
        }

        //
        // POST: /Account/ForgotPassword

        [HttpPost]
        public ActionResult ForgotPassword(ForgotPasswordModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            var resetToken = "";

            if (WebSecurity.GetUserId(model.Email) > -1 && WebSecurity.IsConfirmed(model.Email))
                resetToken = WebSecurity.GeneratePasswordResetToken(model.Email);
            else
            {
                ModelState.AddModelError("Email", "このメールアドレスは登録されていないか、未確認状態のためパスワードのリセットはできません。");
                return View();
            }

            var hostUrl = Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped);
            var resetUrl = hostUrl + VirtualPathUtility.ToAbsolute("~/Account/PasswordReset?resetToken=" +
                HttpUtility.UrlEncode(resetToken));
            try
            {
                var mail = new SmtpMailMessage
                {
                    From = new System.Net.Mail.MailAddress("送信元のメールアドレスを記入"),
                    To = new System.Net.Mail.MailAddress(model.Email),
                    Subject = "パスワード・リセットに必要な情報",
                    Body = "パスワード・リセットのトークン: " + resetToken + "\r\n" +
                        "パスワード・リセットを行うページ <" + resetUrl + "> " +
                        "を訪問して、パスワードのリセットを行ってください。"
                };
                SmtpMail.Send(mail);
            }
            catch (ApplicationException ex)
            {
                ModelState.AddModelError("", "メールが送れませんでした。エラー内容: " + ex.Message);
                return View();
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                ModelState.AddModelError("", "メール送信サーバーと接続ができませんでした。エラー内容: " + ex.Message);
                return View();
            }
            catch (System.Security.Authentication.AuthenticationException ex)
            {
                ModelState.AddModelError("", "メールサーバーと SSL 接続ができませんでした。エラー内容: " + ex.Message);
                return View();
            }
            catch (System.Web.Security.MembershipCreateUserException e)
            {
                ModelState.AddModelError("", e.ToString());
                return View();
            }

            TempData["title"] = "メールを送りました";
            TempData["message"] = "パスワードリセットに必要な情報をメールで送りました。";
            return RedirectToAction("Done");
        }

        //
        // GET: /Account/Done

        public ActionResult Done()
        {
            return View();
        }

        //
        // GET: /Account/PasswordReset

        public ActionResult PasswordReset(string resetToken)
        {
            if (string.IsNullOrEmpty(resetToken))
            {
                TempData["message"] = "パスワードリセットが要求されましたが、パスワード・リセット・トークンが不正です。";
                return RedirectToAction("Error");
            }

            var model = new ResetPasswordModel
            {
                ResetToken = resetToken
            };

            return View(model);
        }

        //
        // POST: /Account/PasswordReset

        [HttpPost]
        public ActionResult PasswordReset(ResetPasswordModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            if (!WebSecurity.ResetPassword(model.ResetToken, model.Password))
            {
                TempData["message"] = "パスワードリセットが要求されましたが、" +
                    "パスワード・リセット・トークンが誤っているため、リセット出来ませんでした。";
                return RedirectToAction("Error");
            }

            TempData["title"] = "パスワードをリセットしました";
            TempData["message"] = "新しいパスワードでログオンしてください。";
            return RedirectToAction("Done");
        }

        //
        // GET: /Account/Error

        public ActionResult Error()
        {
            return View();
        }

View は新規作成が4つです。

Done.cshtml(厳密に型指定されたビューを作成する のチェックなし)

@{
    ViewBag.Title = TempData["title"];
}

<h2>@TempData["title"]</h2>

<p>@Html.Encode(TempData["message"])</p>

Error.cshtml(厳密に型指定されたビューを作成する のチェックなし)

@{
    ViewBag.Title = "要求は処理できませんでした";
}

<h2>要求は処理できませんでした</h2>

<p>@TempData["message"]</p>

ForgotPassword.cshtml(厳密に型指定されたビューを作成する のチェックあり:ForgotPasswordModel (TestAuthorize002.Models), Create)

@model TestAuthorize002.Models.ForgotPasswordModel

@{
    ViewBag.Title = "パスワードのリセット";
}

<h2>パスワードのリセット</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>パスワードのリセット</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Email)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Email)
            @Html.ValidationMessageFor(model => model.Email)
        </div>

        <p>
            <input type="submit" value="リセット" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

PasswordReset.cshtml(厳密に型指定されたビューを作成する のチェックあり:ResetPasswordModel (TestAuthorize002.Models), Create)

@model TestAuthorize002.Models.ResetPasswordModel

@{
    ViewBag.Title = "PasswordReset";
}

<h2>PasswordReset</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>ResetPasswordModel</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Password)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Password)
            @Html.ValidationMessageFor(model => model.Password)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ConfirmPassword)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ConfirmPassword)
            @Html.ValidationMessageFor(model => model.ConfirmPassword)
        </div>

        <div class="editor-field">
            @Html.HiddenFor(model => model.ResetToken)
        </div>

        <p>
            <input type="submit" value="リセット" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

これで前回と併せて WebMatrix を利用した「利用者登録」「ログイン」「パスワードのリセット」「パスワードの変更」ができるようになりました。


WebMatrix.WebData.WebSecurity を利用したユーザー認証(その2)」への3件のフィードバック

  1. 貴重な情報ありがとうございました。SmtpOverSslもダウンロードさせてもらいました。上記のウェブサイトはMVC4のテスト用なのですがばっちり動作してます。ありがとうございました。

    1. コメントありがとうございます。
      この情報が役に立ったということで、コチラとしても嬉しいです 🙂

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です