小さな余談:実際、asp.net mvcでは、すべてのチュートリアルで、AspNetMembershipProviderと呼ばれる既に発明された承認システムを使用することを推奨しています。これは「プッシュして内部の内容を理解しない」という観点からです。 asp.net mvcに初めて会ったとき、私は混乱しました。 さらに、この記事http://habrahabr.ru/post/143024/では、このプロバイダーを使用できないと言われています。 そしてそれに同意します。 ここでは、あらゆる種類のcなasp.net mvc標準トリックをかなり深く研究しているため、これは主要なレッスンの1つです。
クッキー
Cookieは、サーバーからブラウザーに送信される情報の一部であり、ブラウザーは各(ほぼすべての)リクエストとともにサーバーに返します。
応答ヘッダー内のサーバーは次を書き込みます。
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
例:
HTTP/1.1 200 OK Content-type: text/html Set-Cookie: name=value Set-Cookie: name2=value2; Expires=Wed, 09-Jun-2021 10:18:14 GMT
各リクエストのブラウザ(Cookieの有効期限が切れていない場合):
GET /spec.html HTTP/1.1 Host: www.example.org Cookie: name=value; name2=value2 Accept: */*
Cookieの設定(/Areas/Default/Controllers/HomeController.cs):
public ActionResult Index() { var cookie = new HttpCookie() { Name ="test_cookie", Value = DateTime.Now.ToString("dd.MM.yyyy"), Expires = DateTime.Now.AddMinutes(10), }; Response.SetCookie(cookie); return View(); }
Chromeでは、インストールを確認します。
クッキーを受け取るには:
var cookie = Request.Cookies["test_cookie"];
ブレークポイントを作成して確認します。
注:次のリンクでCookieの詳細を確認できます。
http://www.nczonline.net/blog/2009/05/05/http-cookies-explained/
ログイン
私たちの場合、認可はクッキーの使用に基づきます。 これを行うには、次の規定を検討します。
- FormsAuthenticationTicket-このクラスを使用して、認証データを暗号化された形式で保存します
- IPrincipalインターフェイスを実装し、
HttpContext.User
にインストールして、インターフェイスの役割とIIdentity
を確認するIIdentity
ます。 - IIdentityインターフェースの実装
- CurrentControllerプロパティのBaseControllerに、現在ログインしているユーザーの値を表示します。
始めましょう。
IAuthenticationインターフェイスとその実装CustomAuthentication(/Global/Auth/IAuthentication.cs)を作成します。
public interface IAuthentication { /// <summary> /// ( ) /// </summary> HttpContext HttpContext { get; set; } User Login(string login, string password, bool isPersistent); User Login(string login); void LogOut(); IPrincipal CurrentUser { get; } }
実装(/Global/Auth/CustomAuthentication.cs):
public class CustomAuthentication : IAuthentication { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private const string cookieName = "__AUTH_COOKIE"; public HttpContext HttpContext { get; set; } [Inject] public IRepository Repository { get; set; } #region IAuthentication Members public User Login(string userName, string Password, bool isPersistent) { User retUser = Repository.Login(userName, Password); if (retUser != null) { CreateCookie(userName, isPersistent); } return retUser; } public User Login(string userName) { User retUser = Repository.Users.FirstOrDefault(p => string.Compare(p.Email, userName, true) == 0); if (retUser != null) { CreateCookie(userName); } return retUser; } private void CreateCookie(string userName, bool isPersistent = false) { var ticket = new FormsAuthenticationTicket( 1, userName, DateTime.Now, DateTime.Now.Add(FormsAuthentication.Timeout), isPersistent, string.Empty, FormsAuthentication.FormsCookiePath); // Encrypt the ticket. var encTicket = FormsAuthentication.Encrypt(ticket); // Create the cookie. var AuthCookie = new HttpCookie(cookieName) { Value = encTicket, Expires = DateTime.Now.Add(FormsAuthentication.Timeout) }; HttpContext.Response.Cookies.Set(AuthCookie); } public void LogOut() { var httpCookie = HttpContext.Response.Cookies[cookieName]; if (httpCookie != null) { httpCookie.Value = string.Empty; } } private IPrincipal _currentUser; public IPrincipal CurrentUser { get { if (_currentUser == null) { try { HttpCookie authCookie = HttpContext.Request.Cookies.Get(cookieName); if (authCookie != null && !string.IsNullOrEmpty(authCookie.Value)) { var ticket = FormsAuthentication.Decrypt(authCookie.Value); _currentUser = new UserProvider(ticket.Name, Repository); } else { _currentUser = new UserProvider(null, null); } } catch (Exception ex) { logger.Error("Failed authentication: " + ex.Message); _currentUser = new UserProvider(null, null); } } return _currentUser; } } #endregion }
一番下の行はこれです。リクエストを初期化するとき、
HttpContext.Request.Cookies
にアクセスして
UserProvider
を初期化し
UserProvider
。
var ticket = FormsAuthentication.Decrypt(authCookie.Value); _currentUser = new UserProvider(ticket.Name, Repository);
IRepositoryでの承認のために、新しいIRepository.Loginメソッドが追加されました。 SqlRepositoryでの実装:
public User Login(string email, string password) { return Db.Users.FirstOrDefault(p => string.Compare(p.Email, email, true) == 0 && p.Password == password); }
実際、UserProviderはIPrincipalインターフェイスを実装しています(ロールチェックとIIdentityへのアクセスがあります)。
UserProviderクラス(/Global/Auth/UserProvider.cs)を検討してください。
public class UserProvider : IPrincipal { private UserIndentity userIdentity { get; set; } #region IPrincipal Members public IIdentity Identity { get { return userIdentity; } } public bool IsInRole(string role) { if (userIdentity.User == null) { return false; } return userIdentity.User.InRoles(role); } #endregion public UserProvider(string name, IRepository repository) { userIdentity = new UserIndentity(); userIdentity.Init(name, repository); } public override string ToString() { return userIdentity.Name; }
UserProvider
、その
IIdentity
クラスが
UserIdentity
であることを知っているため、
InRoles(role)
メソッドを実装する
User
クラスについて知ってい
User
。
public bool InRoles(string roles) { if (string.IsNullOrWhiteSpace(roles)) { return false; } var rolesArray = roles.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); foreach (var role in rolesArray) { var hasRole = UserRoles.Any(p => string.Compare(p.Role.Code, role, true) == 0); if (hasRole) { return true; } } return false; }
InRoles
メソッド
InRoles
は、リソースに許可されているロールについて、コンマで区切られたリクエストが発生することが予想されます。 たとえば、「admin、moderator、editor」の場合、ロールの少なくとも1つが
User
にある場合、概念「true」を返します(アクセスがあります)。 Role.Nameではなく、Role.Codeフィールドで比較します。
UserIdentityクラス(/Global/Auth/UserIdentity.cs)を検討してください。
public class UserIndentity : IIdentity { public User User { get; set; } public string AuthenticationType { get { return typeof(User).ToString(); } } public bool IsAuthenticated { get { return User != null; } } public string Name { get { if (User != null) { return User.Email; } // return "anonym"; } } public void Init(string email, IRepository repository) { if (!string.IsNullOrEmpty(email)) { User = repository.GetUser(email); } } }
IRepository
、新しい
GetUser(email)
メソッドを追加し
GetUser(email)
。
SqlRepository.GetUser()
実装(LessonProject.Model:/SqlRepository/User.cs):
public User GetUser(string email) { return Db.Users.FirstOrDefault(p => string.Compare(p.Email, email, true) == 0); }
ほとんどすべての準備が整いました。 BaseControllerでCurrentUserを取得:
[Inject] public IAuthentication Auth { get; set; } public User CurrentUser { get { return ((UserIndentity)Auth.CurrentUser.Identity).User; } }
はい、強力なバインディングがあるため、これはあまり正確ではありません。 したがって、これを行って、許可された
User
返却を要求する別のインターフェイス
IUserProvider
導入しましょう。
public interface IUserProvider { User User { get; set; } } … public class UserIndentity : IIdentity, IUserProvider { /// <summary> /// /// </summary> public User User { get; set; } … [Inject] public IAuthentication Auth { get; set; } public User CurrentUser { get { return ((IUserProvider)Auth.CurrentUser.Identity).User; } }
それでは、すべてを初期化してみましょう。
最初に、Ninjectの登録にIAuthentication + CustomAuthenticationを追加します(/App_Start/NinjectWebCommon.cs):
kernel.Bind<IAuthentication>().To<CustomAuthentication>().InRequestScope();
次に、AuthenticateRequestイベントで承認アクションを実行するモジュールを作成します。
public class AuthHttpModule : IHttpModule { public void Init(HttpApplication context) { context.AuthenticateRequest += new EventHandler(this.Authenticate); } private void Authenticate(Object source, EventArgs e) { HttpApplication app = (HttpApplication)source; HttpContext context = app.Context; var auth = DependencyResolver.Current.GetService<IAuthentication>(); auth.HttpContext = context; context.User = auth.CurrentUser; } public void Dispose() { } }
行のすべてのソルト:
auth.HttpContext = context context.User = auth.CurrentUser
。 承認モジュールがコンテキストとそれに含まれるCookieを検出するとすぐに、名前にすぐにアクセスし、リポジトリ内のユーザーデータを受信してBaseControllerに返します。 しかし、一度にすべてではなく、オンデマンドで。
Web.configでモジュールを接続します。
<system.web> … <httpModules> <add name="AuthHttpModule" type="LessonProject.Global.Auth.AuthHttpModule"/> </httpModules> </system.web>
計画は次のとおりです。
- 上部には、ユーザーが承認されているかどうかが表示されます。 承認された場合、彼の電子メールと終了するためのリンク、そうでない場合は、ログインと登録へのリンク
- ログインフォームを作成する
- ユーザーがデータを正しく入力した場合、ユーザーを認証してメインページに送信します
- ユーザーがログアウトした場合は、承認を強制終了します
行こう
Html.Action(“UserLogin”, “Home”)
を追加し
Html.Action(“UserLogin”, “Home”)
-これは部分的なビュー(つまり、レイアウトを持たないコードの一部)です-つまり RenderBody()ではなく、登録されている場所に表示されます。
_Layout.cshtml(/Areas/Default/Views/Shared/_Layout.cshtml):
<body> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <ul class="nav nav-pills pull-right"> @Html.Action("UserLogin", "Home") </ul> </div> </div> </div> @RenderBody() HomeController.cs: public ActionResult UserLogin() { return View(CurrentUser); }
UserLogin.cshtml(/Areas/Default/Views/Home/UserLogin.cshtml):
@model LessonProject.Model.User @if (Model != null) { <li>@Model.Email</li> <li>@Html.ActionLink("", "Logout", "Login")</li> } else { <li>@Html.ActionLink("", "Index", "Login")</li> <li>@Html.ActionLink("", "Register", "User")</li> }
LoginController出口コントローラー(/Areas/Default/Controllers/LoginController.cs):
public class LoginController : DefaultController { [HttpGet] public ActionResult Index() { return View(new LoginView()); } [HttpPost] public ActionResult Index(LoginView loginView) { if (ModelState.IsValid) { var user = Auth.Login(loginView.Email, loginView.Password, loginView.IsPersistent); if (user != null) { return RedirectToAction("Index", "Home"); } ModelState["Password"].Errors.Add(" "); } return View(loginView); } public ActionResult Logout() { Auth.LogOut(); return RedirectToAction("Index", "Home"); } }
LoginView.cs(/Models/ViewModels/LoginView.cs):
public class LoginView { [Required(ErrorMessage = " email")] public string Email { get; set; } [Required(ErrorMessage = " ")] public string Password { get; set; } public bool IsPersistent { get; set; } }
ログインページIndex.cshtml(/Areas/Default/Views/Index.cshtml):
@model LessonProject.Models.ViewModels.LoginView @{ ViewBag.Title = ""; Layout = "~/Areas/Default/Views/Shared/_Layout.cshtml"; } <h2></h2> @using (Html.BeginForm("Index", "Login", FormMethod.Post, new { @class = "form-horizontal" })) { <fieldset> <legend></legend> <div class="control-group"> <label class="control-label" for="Email"> Email</label> <div class="controls"> @Html.TextBox("Email", Model.Email, new { @class = "input-xlarge" }) <p class="help-block"> Email</p> @Html.ValidationMessage("Email") </div> </div> <div class="control-group"> <label class="control-label" for="Password"> </label> <div class="controls"> @Html.Password("Password", Model.Password, new { @class = "input-xlarge" }) @Html.ValidationMessage("Password") </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary"> </button> </div> </fieldset> }
実行して確認:
承認後:
すべてのソースはhttps://bitbucket.org/chernikov/lessonsにあります