Sayfalar

11 Aralık 2013 Çarşamba

ASP.NET MVC - KOMPLE BİR PROJE YAPISI OLUŞTURMAK - 4

Bu makalede üyelik işlemlerini yapacağız. Üyelik işlemlerini, kendi yazdığımızı üyelik ve rol sağlayıcılar ile yapacağız. Ayrıca üye olan kullanıcılara bir onay linki yollayıp, bu onay linkini tıkladıktan sonra üyeliklerini aktif etmesini sağlayacağız.

Öncelikle kullanıcı kayıt ve giriş işlemlerinde kullanacağımız ViewModellerimizi tanımlayalım.

RegisterModel.cs
using System.ComponentModel.DataAnnotations;
 
namespace MvcProject.Website.Models.AccountModels
{
    public class RegisterModel
    {
        [StringLength(150, ErrorMessage = "{0} alanı en fazla {1}, en az {2} karakter uzunluğunda olmalıdır!", MinimumLength = 3)]
        [Required(ErrorMessage = "{0} alanı gereklidir!")]
        [Display(Name = "Kullanıcı Adı")]
        [System.Web.Mvc.Remote("ValidateUserName""Account")]
        public string UserName { getset; }
 
        [StringLength(50, ErrorMessage = "{0} alanı en fazla {1}, en az {2} karakter uzunluğunda olmalıdır!", MinimumLength = 3)]
        [Required(ErrorMessage = "{0} alanı gereklidir!")]
        [DataType(DataType.Password)]
        [Display(Name = "Şifre")]
        public string Password { getset; }
 
        [System.Web.Mvc.Compare("Password", ErrorMessage = "İki şifre eşleşmiyor!")]
        [Display(Name = "Şifre Tekrar")]
        [DataType(DataType.Password)]
        public string ConfirmPassword { getset; }
 
        [StringLength(150, ErrorMessage = "{0} alanı en fazla {1} karakter uzunluğunda olmalıdır!")]
        [Required(ErrorMessage = "{0} alanı gereklidir!")]
        [EmailAddress(ErrorMessage = "{0} geçersiz!")]
        [Display(Name = "E-Posta")]
        [System.Web.Mvc.Remote("ValidateEmail""Account")]
        public string Email { getset; }
    }
}

LoginModel.cs
using System.ComponentModel.DataAnnotations;
 
namespace MvcProject.Website.Models.AccountModels
{
    public class LoginModel
    {
        [StringLength(150, ErrorMessage = "{0} alanı en fazla {1} karakter uzunluğunda olmalıdır!")]
        [Required(ErrorMessage = "{0} alanı gereklidir!")]
        [Display(Name = "Kullanıcı Adı")]
        public string UserName { getset; }
 
        [StringLength(50, ErrorMessage = "{0} alanı en fazla {1}, en az {2} karakter uzunluğunda olmalıdır!", MinimumLength = 3)]
        [Required(ErrorMessage = "{0} alanı gereklidir!")]
        [DataType(DataType.Password)]
        [Display(Name = "Şifre")]
        public string Password { getset; }
 
        [Display(Name = "Beni Hatırla?")]
        public bool RememberMe { getset; }
    }
}

Yine BaseController sınıfından kalıtım alan bir AccountController sınıfı ekliyoruz. AccountController sınıfımızın içeriği aşağıdaki gibidir. Şimdilik kodların hepsini yazıyorum, tek tek açıklamasını yazacağım. Ayrıca User sınıfına bazı özelliklerde ekledim (LastLoginDate, LastIp, ...). Bunlar çok önemli değil, projemizi etkileyecek değişikliler değil.
using System;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using System.Web.Security;
using MvcProject.Core.Domain.Entity;
using MvcProject.Data.UnitOfWork;
using MvcProject.Service.Users;
using MvcProject.Web.Models.AccountModel;
 
namespace MvcProject.Web.Controllers
{
    public class AccountController : BaseController
    {
        private readonly IUserService _userService;
 
        public AccountController(IUserService userService, IUnitOfWork uow)
            : base(uow)
        {
            _userService = userService;
        }
 
        public ActionResult Register()
        {
            return View();
        }
 
        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    User user = new User
                    {
                        ConfirmationId = Guid.NewGuid(),
                        DisplayName = model.UserName,
                        IsConfirmed = false,
                        LastLoginDate = DateTime.Now,
                        LastLoginIp = Request.UserHostAddress,
                        Password = model.Password,
                        ProfileImageUrl = "Content/Images/no_profile_image.png",
                        Email = model.Email,
                        UserName = model.UserName,
                        IsActive = false,
                        IsEditable = true,
                        IsDeletable = true
                    };
 
                    _userService.Insert(user);
                    _uow.SaveChanges();
                    _userService.SendConfirmationMail(user.Id, user.Email, Request.Url.GetLeftPart(UriPartial.Authority));
 
                    return RedirectToAction("Index""Home");
                }
                catch (Exception ex)
                {
                    ModelState.AddModelError("""Kullanıcı oluşturma başarısız!");
                }
            }
 
            return View(model);
        }
 
        public ActionResult Login(string ReturnUrl)
        {
            ViewBag.ReturnUrl = ReturnUrl;
            return View();
        }
 
        [HttpPost]
        public ActionResult Login(LoginModel model, string ReturnUrl)
        {
            var user = _userService.ValidateUser(model.UserName, model.Password);
            if (ModelState.IsValid && user != null)
            {
                if (!user.IsConfirmed)
                {
                    TempData["EpostaOnayMesaj"] = "E-posta adresiniz onaylı değildir. Lütfen e-posta adresinizdeki linki kullanarak e-posta adresinizi onaylayınız.";
 
                    return View();
                }
                FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                return RedirectToLocal(ReturnUrl);
            }
            else
            {
                ModelState.AddModelError("""Kullanıcı adı ve ya şifre geçersiz!");
            }
 
            return View(model);
        }
 
        // RegisterModel içerisindeki Email alanını
        // RemoteAttribute ile kontrol eder
        public JsonResult ValidateEmail(string Email)
        {
            var result = _userService.ValidateEmail(Email);
 
            if (result)
            {
                return Json("Girdiğiniz e-posta adresi sistemde zaten mevcut!"JsonRequestBehavior.AllowGet);
            }
 
            return Json(!result, JsonRequestBehavior.AllowGet);
        }
 
        // RegisterModel içerisindeki UserName alanını
        // RemoteAttribute ile kontrol eder
        public JsonResult ValidateUserName(string UserName)
        {
            var result = _userService.ValidateUserName(UserName);
 
            if (result)
            {
                return Json("Girdiğiniz kullanıcı adı sistemde zaten mevcut!"JsonRequestBehavior.AllowGet);
            }
 
            return Json(!result, JsonRequestBehavior.AllowGet);
        }
 
        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Index""Home");
        }
 
        public ActionResult ConfirmUser(Guid confirmationId)
        {
            if (string.IsNullOrEmpty(confirmationId.ToString()) || (!Regex.IsMatch(confirmationId.ToString(),
                   @"[0-9a-f]{8}\-([0-9a-f]{4}\-){3}[0-9a-f]{12}")))
            {
                TempData["EpostaOnayMesaj"] = "Hesap geçerli değil. Lütfen e-posta adresinizdeki linke tekrar tıklayınız.";
 
                return View();
            }
            else
            {
                var user = _userService.Find(confirmationId);
 
                if (!user.IsConfirmed)
                {
                    user.IsConfirmed = true;
                    _userService.Update(user);
                    _uow.SaveChanges();
 
                    FormsAuthentication.SetAuthCookie(user.UserName, true);
                    TempData["EpostaOnayMesaj"] = "E-posta adresinizi onayladığınız için teşekkürler. Artık sitemize üyesiniz.";
 
                    return RedirectToAction("Wellcome");
                }
                else
                {
                    TempData["EpostaOnayMesaj"] = "E-posta adresiniz zaten onaylı. Giriş yapabilirsiniz.";
 
                    return RedirectToAction("Login");
                }
            }
        }
 
        public ActionResult Wellcome()
        {
            return View();
        }
 
        #region private methods
        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index""Home");
            }
        }
        #endregion
 
        protected override void Dispose(bool disposing)
        {
            _uow.Dispose();
            base.Dispose(disposing);
        }
    }
}

AccountController içerisindeki bazı metodlar, önceki yazılarda paylaştığım userService içerisinde yok. Örneğin, _userService.ValidateUserName, userService.ValidateEmail gibi. Gerekli metodları kendiniz ekleyebilirsiniz. Ben sadece e-posta gönderen metodu yazayım;
        /// <summary>
        /// Yeni üye olan kullanıcıya onay mesajı gönder.
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        public void SendConfirmationMail(int userId, string email, string ConfirmationUrl)
        {
            var user = Find(userId);
            string confirmationId = user.ConfirmationId.ToString();
            ConfirmationUrl += "/Account/ConfirmUser?confirmationId=" + confirmationId;
 
            var message = new MailMessage("info@belleksizintisi.com", email)
            {
                Subject = "Lütfen e-posta adresinizi onaylayınız.",
                Body = ConfirmationUrl
            };
 
            var client = new SmtpClient();
 
            client.Send(message);
        }

Gerekli web.config ayarı:
<configuration>
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network">
        <network host="smtp.gmail.com"
                 port="587"
                 userName="xxx@gmail.com"
                 password="sifre"
                 enableSsl="false"/>
      </smtp>
    </mailSettings>
  </system.net>
...
</configuration>

Login.cshtml
@model MvcProject.Website.Models.AccountModels.LoginModel
 
@{
    ViewBag.Title = "Kullanıcı Girişi";
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
 
<div class="row">
    <div class="col-md-12">
        @using (Html.BeginForm())
        {
            @Html.AntiForgeryToken()
 
            <div class="form-horizontal">
                <h4>Kullanıcı Girişi</h4>
                <hr />
                @Html.ValidationSummary(true)
                @TempData["EpostaOnayMesaj"]
 
                <div class="form-group">
                    @Html.LabelFor(model => model.UserName, new { @class = "col-md-2" })
                    <div class="col-md-10">
                        @Html.TextBoxFor(model => model.UserName, new { @class = "form-control" })
                        @Html.ValidationMessageFor(model => model.UserName)
                    </div>
                </div>
 
                <div class="form-group">
                    @Html.LabelFor(model => model.Password, new { @class = "col-md-2" })
                    <div class="col-md-10">
                        @Html.PasswordFor(model => model.Password, new { @class = "form-control" })
                        @Html.ValidationMessageFor(model => model.Password)
                    </div>
                </div>
 
                <div class="form-group">
                    @Html.LabelFor(model => model.RememberMe, new { @class = "col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.RememberMe)
                        @Html.ValidationMessageFor(model => model.RememberMe)
                    </div>
                </div>
 
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Giriş Yap" class="btn btn-default" />
                    </div>
                </div>
            </div>
        }
 
        <div>
            Hesabınız yoksa lütfen @Html.ActionLink("Kayıt Olunuz""Register")
        </div>
    </div>
</div>

Register.cshtml
@model MvcProject.Website.Models.AccountModels.RegisterModel
 
@{
    ViewBag.Title = "Kullanıcı Kaydı";
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>Kullanıcı Kaydı</h4>
        <hr />
        @Html.ValidationSummary(true)
 
        <div class="form-group">
            @Html.LabelFor(model => model.UserName, new { @class = "col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.UserName, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.UserName)
            </div>
        </div>
 
        <div class="form-group">
            @Html.LabelFor(model => model.Password, new { @class = "col-md-2" })
            <div class="col-md-10">
                @Html.PasswordFor(model => model.Password, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Password)
            </div>
        </div>
 
        <div class="form-group">
            @Html.LabelFor(model => model.ConfirmPassword, new { @class = "col-md-2" })
            <div class="col-md-10">
                @Html.PasswordFor(model => model.ConfirmPassword, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.ConfirmPassword)
            </div>
        </div>
 
        <div class="form-group">
            @Html.LabelFor(model => model.Email, new { @class = "col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.Email, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.Email)
            </div>
        </div>
 
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Kayıt Ol" class="btn btn-default" />
            </div>
        </div>
    </div>
}
 
<div>
    Hesabınız varsa lütfen @Html.ActionLink("Giriş Yapınız""Login")
</div>

Wellcome.cshtml
@{
    ViewBag.Title = "Hoş Geldiniz";
}
 
<h2>Hoşgeldiniz</h2>
<hr />
<div>
    @TempData["EpostaOnayMesaj"]
</div>
 

Üyelik için gerekli olan controllerlar ve view sayfalarımız hazır. Bu hali ile kullanıcı kaydı yapılır, kullanıcıya onay mesajı gönderilir. Kullanıcı onay mesajına tıklayınca üyeliği aktif olur. Buraya kadar herşey tamam gibi görünüyor.

Peki, kullanıcını için AuthorizeAttribute nesnesini nasıl kontrol edeceğiz. Bu hali ile giriş yapan kullanıcının yetkili oldugu controller metodları belirlenebilir. Ama kullanıcının rollerini, o rolde olup olmadıgını nasıl kullanacagız. Makalelerin başında da söylemiştim, kendi üyelik ve rol sistemimizi kullanacagız.

Bunun için MembershipProvider ve RoleProvider sınıflarından kalıtım alarak üyelik sistemimizi yazacağız. Ben projenin kök dizini içerisinde Application/Membership diye bir klasör, açtım ve sınıfları buraya yazacağım.

CustomMembershipProvider.cs
public class CustomMembershipProvider : MembershipProvider
    {
        public static IUserService _userService
        {
            get
            {
                return DependencyResolver.Current.GetService<UserService>();
            }
        }
 
        public override bool ValidateUser(string userName, string password)
        {
            return _userService.FindByUserNameAndPassword(userName, password) == null ? false : true;
        }
 
        #region not imlemented
        public override string ApplicationName
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
                throw new System.NotImplementedException();
            }
        }
 
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            throw new System.NotImplementedException();
        }
 
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool EnablePasswordReset
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override bool EnablePasswordRetrieval
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new System.NotImplementedException();
        }
 
        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new System.NotImplementedException();
        }
 
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            throw new System.NotImplementedException();
        }
 
        public override int GetNumberOfUsersOnline()
        {
            throw new System.NotImplementedException();
        }
 
        public override string GetPassword(string username, string answer)
        {
            throw new System.NotImplementedException();
        }
 
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            throw new System.NotImplementedException();
        }
 
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            throw new System.NotImplementedException();
        }
 
        public override string GetUserNameByEmail(string email)
        {
            throw new System.NotImplementedException();
        }
 
        public override int MaxInvalidPasswordAttempts
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override int MinRequiredNonAlphanumericCharacters
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override int MinRequiredPasswordLength
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override int PasswordAttemptWindow
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override MembershipPasswordFormat PasswordFormat
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override string PasswordStrengthRegularExpression
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override bool RequiresQuestionAndAnswer
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override bool RequiresUniqueEmail
        {
            get { throw new System.NotImplementedException(); }
        }
 
        public override string ResetPassword(string username, string answer)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool UnlockUser(string userName)
        {
            throw new System.NotImplementedException();
        }
 
        public override void UpdateUser(MembershipUser user)
        {
            throw new System.NotImplementedException();
        }
        #endregion
    }

CustomRoleProvider.cs
public class CustomRoleProvider : RoleProvider
    {
        public static IRoleService _roleService
        {
            get
            {
                return DependencyResolver.Current.GetService<RoleService>();
            }
        }
 
        public override bool IsUserInRole(string userName, string roleName)
        {
            return _roleService.IsUserInRole(userName, roleName);
        }
 
        public override string[] GetRolesForUser(string username)
        {
            return _roleService.GetRolesByUserName(username).Select(x => x.RoleName).ToArray<string>();
        }
 
        #region not implemented
        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            throw new System.NotImplementedException();
        }
 
        public override string ApplicationName
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
                throw new System.NotImplementedException();
            }
        }
 
        public override void CreateRole(string roleName)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            throw new System.NotImplementedException();
        }
 
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new System.NotImplementedException();
        }
 
        public override string[] GetAllRoles()
        {
            throw new System.NotImplementedException();
        }
 
        public override string[] GetUsersInRole(string roleName)
        {
            throw new System.NotImplementedException();
        }
 
        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            throw new System.NotImplementedException();
        }
 
        public override bool RoleExists(string roleName)
        {
            throw new System.NotImplementedException();
        }
        #endregion
    }

Sadece AuthorizeAttributte nesnesinin kullandıgı metodların içerisini doldurdum. Şimdi bu iki sınıfı üyelik sistemi olarak kullanabilmek için web.config ayarlarını yapalım.
<configuration>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>
    <profile defaultProvider="DefaultProfileProvider">
      <providers>
        <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </profile>
    <membership defaultProvider="CustomMembershipProvider">
      <providers>
        <add name="CustomMembershipProvider" type="MvcProject.Website.Application.Membership.CustomMembershipProvider" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    <roleManager defaultProvider="CustomRoleProvider" enabled="true">
      <providers>
        <add name="CustomRoleProvider" type="MvcProject.Website.Application.Membership.CustomRoleProvider" connectionStringName="DefaultConnection" applicationName="/" />
      </providers>
    </roleManager>
....


Uygulamanın üyelik sistemide artık bitmiştir. Kodları elimden geldiği kadar anlaşılır yazmaya çalıştım. Umarım eksik bir yer kalmamıştır. Eksik ve fazlaları geri dönüşlere göre değiştirmeye çalışıyorum. Bende çoğu şeyi yazarken öğreniyorum. Yakalayamadığım yerler olabiliyor. Bunlarıda beraber çözmeye çalışıyoruz.

Bu arada, proje başladığı gibi gitmiyor, bazen değiştiriyorum. Gereksiz bir şeyler varsa çıkarıyorum, eklemem gereken bir şeyler ve ya yenilikler olursa ekliyorum. Kritik değişimler değil bunlar. Projeye devam ederken kendinizin yakalayacağı ve değişikliği kendinizin yapacağı değişiklikler. Çok yapısal bir değişiklik olursa onu da belirtirim.

Sonraki makalelerde, kullanıcının profilini düzenleme, fileupload gibi konuları anlatmaya çalışacağım. Makale uzamazsa, aynı makalede area kavramına ve admin area kısmına girişte yapabiliriz.

Makalelerin geç kalmasından dolayı kusura bakmayın.

İyi çalışmalar...

75 yorum:

  1. çalışmalarınızı çok başarılı buluyorum.. çalışmalarınız sayesinde mvc'nin inceliklerini kavramaya çalıyorum...
    MembershipProvider tasarımında Web.Config yapılandırmasını bir türlü yapamadım.. siszin haber portal örneğiniz ve bu makalenizden takip ederek yaptım fakat çalıştıramıyorum... öncelikli yüklenmesi gereken eklentiler var diyor. stackoverflow dan da baktığımda type namespace hatası olabileceğini anladım fakat bir türlü çalıştıramadım... sizin bir öneriniz varmıdır. şimdiden teşlekür ederim...

    YanıtlaSil
    Yanıtlar
    1. Nasıl bir hata alıyorsunuz? Hata mesajları var mı? Varsa nedir? Hatanızla alakalı stacoverflow da buldugunuz sorunların linkini yazar mısınız...

      Sil
  2. Öncelikle ilginize teşekür ederim.. sizin kullandığınız yapıda aşağıdaki gibi bir tanımlama vardı. bende kullanmak istedim. xxxxx ile ifade edilen yer üyelik sağlayıcımızı iifade ediyor. fakat bir türlü çalışmadı .






    Stackoverflow ve benzeri siitelerde araştırmamda aşağıdaki gibi bir tanımlama yapmam gerektiğini söylüyorlardı. ayrıca üyelik sağlayıcıyı namespace tanımlaması yapmadan kullandım. UyelikSaglayici sınıfını kök dizine koydum ayrıca type kısmında sınıf, proje ismi şeklinde yapınca çalıştı. proje ismi Guvenlik. aşağıdaki yapı ile çalıştı. İlginize tekrar teşekür ederim.. Çalışmalarınızın devamını dilerim..





    YanıtlaSil
    Yanıtlar
    1. Yazılar cıkmamıs ama, sorunun cözülmüş olması iyi. Teşekkürler...

      Sil
  3. Gerçekten başarılı validationlar için FluentValidation kullanılabilir ben çok beğeniyorum. Projede dil yapısıda olacak mı ? Diğer makale için sabırsızlanıyorum.

    YanıtlaSil
    Yanıtlar
    1. Multi Language bir proje olacak

      Sil
    2. Hocam Baştan sona Kadar Video olarak da çekme ve yayınlama sansımız var mı ?

      Sil
  4. Elinize saglik hocam cok iyi gidiyorsunuz benim konu disi bir sorum olucak kullanici yetkilendirme ile ilgili, membership yapisini kullanmiyorum kendi kullancilari bir tabloda tutup session kontrolu ile yetkilendirme yapiyorum merak ettigim yanlis mi yapiyorum sadece session kontrolu yapmak guvenlimi membership kontrolleri nasil calisiyor session dan extra guvenlik kontrolu varmi bu konu hakkinda goruslerinizi alabilirsem cok sevinicem.

    YanıtlaSil
    Yanıtlar
    1. Session da bir yöntem, kullanılabilir. Fakat yönetimi nasıl olur onu bilmiyorum. Yani her yerde ayrı ayrı Session kontrolü yapmak, projenin modülerliği açısından sanki yönetimi zorlaştırıyor gibi. Mvc de session kullanan hiç örnek (açık kaynak proje) görmedim. FormAuthentication kullanıldıgında ve benim anlattığım örnekteki, membership metodlarını override edince, uygulama boyunca, hazır yetkilendirme metodlarından (Authorize gibi) tam olarak faydalanabilirsiniz.

      Sil
    2. Aslinda cok kolay hocam Authorize attribute'u yazinca. Membership daha guvenliymis sanirim biraz arastirdim simdi kafami kurcalayan bisey var. Admin login sayfasini nasil normal login sayfasindan farkli olarak redirect ederim, yani admin area var admin login sayfasi farkli olmasini istiyorum. web.config yada attribute'mu nasil olur daha once karsilastinizmi bu durumla tesekkur ederim.

      Sil
    3. Ayrı yapmak istiyorum derken ne demek istediginizi anlamadım. Kullanıcı giriş yapınca, eğer admin ise ana sayfada bir admin linki cıkarıp, admin sayfasına yonlendirebilirsiniz.

      Sil
    4. Kullanici login sayfasi farkli admin login sayfasi farkli sekilde yapmak. Areas ekliyorum admin islemlerini admin area sindan yapiyorum. SimpleMembership kullandigimda Authroize Admin diyecem ama bu sefer gidicek Account/Login 'e iste oraya gitmesinde Admin/Acciunt/Login e gitsin admin ordan giris yapsin mumkunmu boyle birsey.

      Sil
    5. giriş yapmayan bir kullanıcının hangi rolde oldugunu nasıl anlayacaksınız?

      Sil
    6. onu anlayamayiz da role'e gore login page olayi olmali bunu gelistirmeliler bence eksik bi ozellik

      Sil
    7. Hocam role göre login page diye birşey olmaz. Kullanıcının rolünü nasıl almayı düşünüyorsunuz kullanıcı giriş yapmadan? Rol almak için kullanıcıyı tanımanız lazım. Kullanıcıyı tanımak için kullanıcı girş yapmış olmalı. Sizin admin oldugunuzu nasıl anlayıp sizi admin login e yönlendirecek?

      Sil
    8. Bu yorum yazar tarafından silindi.

      Sil
    9. web.config e eklenebilir nasil account/login varsa, Authroize("Roles=Admin") yaptiginda authorize olan role un login sayfasina yonelndirecek bunuda ayarlamak icin web.config e eklenbilir role login gibi alan acilabilir

      Sil
    10. admin login sayfasi ile normal kullanici login sayfasi arasinda nasil bir fark olacak?

      Sil
    11. Burada kullanıcının admin olup olmadığını kontrol ederek istediğin gibi bir alan gösterimi, buton.. gibi istediğin herşeyi gösterebilirsin sadece Admin'e özel
      @if (Request.IsAuthenticated && User.IsInRole("Admin"))
      {
      istediğin buton ya da html kodları
      }

      Sil
    12. Tasarim farkli olucak tabiki.
      Berdan o lazim degil.

      Sil
  5. Merhaba Hocam,
    Benim sorum biraz daha genel olucak. Mvc 4 örnek bir uygulama açtığımızda hali hazırda gelen bir Authentication sistemi var. Şu iki duruma göre kendi Authentication classımımızı mı yazmak mantıklı yoksa halihazırda var olanı mı kullanmak?
    - Kişisel blog yazarken sadece tek kullanıcı giriş yapacağı zaman,
    - Birden çok kullanıcının bu bloga girip veri girişi yapacağı zaman.
    Teşekkürler

    YanıtlaSil
    Yanıtlar
    1. Makalelerde hazır kullanmamamın sebebi, her zaman kendim yazmaktan yana olmamdır. Kendiniz yazarsanız, veri tabanı tabloları ve uyelik sınıfları üzerinde tam hakim olursunuz. Ben her zaman kendim yazmak taraftarıyım. Kendi oluşturacağınız diğer sınıflarında bu üyelik sınıfları ile olan illişkilerini daha iyi yönetirsiniz.

      Sil
    2. Anladım hocam, peki güvenlik açısından ele aldığımızda hangisi daha güvenli olur?(Temel kullanıcı seviyesini düşünerek yani sizin yazdığınız gibi bir authentication yazabiliyorum.) Kendi Sessionlarımın kontrolü için bir SesionFilter oluşturdum ve bütün Controllerlarımın başına [SessionFilter] yazarak kullanıcıya ait olan Session'ı benim oluşturduğum olup olmadığını kontrol ediyorum. Elimden gelen güvenlik kontrolü bu kadar.
      Bunları ele alarak hazır olan authentication'ının ek olarak sağladığı güvenlik falan var mı?

      Sil
    3. Bunların güvenlik acısından karşılaştıracak kadar bilgim yok. Sanırım biraz araştırmak lazım.Eğer siz bu konuda araştırma yaparda burada paylaşırsanız güzel olur.

      Sil
    4. Ayrım olarak birşey bulamadım daha doğrusu söylenen eğer biliyorsan kendi kodlarını yazmak daha mantıklı tarzında yorumlar var ama bilmekten kasıt nedir bunu anlamak biraz zor. Çünkü güvenlik konusuna girdiğimde Authentication dan çok ilk önce Xsrf/Csrf saldırılarının üzerinde durmuşlar. Bunlardan sonra okuduğum yerlerde Authentication için filter ile ilgili yazılardı. Yani sayfalar arasında gezerken sessionı kontrol etmek ya da Login yaptığın Controller senin AuthenticationController'ından mı olmuş tarzı filtreleme seçenekleri getiriyorlardı.
      Dilim döndüğünce anlatmaya çalıştım umarım yardımcı olur başkalarına.

      Sil
  6. Merhaba,
    sürekli takip ediyorum kardeşim herşey için sağol ama bir eleştirim olacak. Ya bazan haftalar aylar bekletiyon. Her gün açıyom yok yok. Tamam yoğunsun bizede zaman ayırıyon teşekkür ederiz. Ama biraz daha ilgi bekliyoruz :)

    YanıtlaSil
    Yanıtlar
    1. Hocam sağol ama gerçekten zaman olmuyor. Forum sitelerini bile takip edemiyorum. Makale yazmak birazda zor iş. Paylaşacak çok sey var ama işte... Kusura bakmayın, elimden geldigi kadar hızlı olmaya çalışırım :)

      Sil
  7. Makale için teşekkürler. Daha önceden yazmıştım ama hata oldu heralde çıkmadı forumda. Benim iki sorum olacak birincisi notimplemented methodlar için bir örnek verebilirmisiniz mesela changepassword için. 2. soruda normal mvc internet varsaılan bir proje oluşturduğumuzda ordaki membership şifreleri kaydederken SHA ve password salt ile kriptolayıp saklıyor buların örneklerine baktım md5 ile yapabildim SHA ve password salt ile nasıl yapılabilir çözemedim? teşekkürler.

    YanıtlaSil
    Yanıtlar
    1. .net MD5 kullanıyor diye biliyorum. Ben daha önce hiç kullanmadım. Sizin bildiklerinizle aynı şeyi biliyorum bu konuda. NotImplemented metodları doldurmak için ekstra birşey bilmenize gerek yok. Kendi yazdığınız metodları bu metodların içerisinde çağırın sadece. implement edilen metodlar gibi

      Sil
  8. Hocam şuan kadar olan Projeyi paylaşa bilir misiniz?

    YanıtlaSil
    Yanıtlar
    1. Proje ile alakalı tüm kodları makalelerde yazdım. Birkaç proje üzerinde çalışarak, kodları o projelerden alarak tek projeymiş gibi, yazıyorum, bundan dolayıda şu an bir proje paylaşımı zor görünüyor.

      Sil
  9. Merhaba Ali Rıza bey, Elinize sağlık çok yararlı bir siteniz var.
    Bir sorum olacaktı; MVC 4.0 kullanarak oluşturduğum projemde her isteği HomeController daki index ActionResultuna yönlendirip gelen url den tıklanan sayfanın url alıyorum ve ilgili View yönlendiriyorum bu işlemleri projeyi Empty olarak açarsam yapıyorum fakat proje Basic açarsam controllerda olmayan bir action olduğu için hata veriyor "Server Error in '/' Application." sizce nerde hata yapıyorum.

    YanıtlaSil
    Yanıtlar
    1. Sorunuzu pek anlamadım. Bir kaç satır kod görmek lazım. Routing ayarlarından olabilir.

      Sil
  10. Merhaba,
    iki projedede aşağıdaki veriler aynı, Basic oluşturduğum projede her isteğin home controller + Index ActionREsult metoduna yönlendirmek istiyorum.

    RouteCongig.cs sınıfındaki url şablonum
    routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );


    Şöyle bir contoller var ve içinde bir ActionResult var

    public class HomeController : Controller
    {
    public ActionResult Index()
    {
    return View(ilgili_View_adı);
    }
    }

    YanıtlaSil
    Yanıtlar
    1. Problem yok gibi görünüyor. routeconfig sınıfının tam görüntüsü aşağıdaki gibi bu arada değil mi: (ve global.asax ta tanımlı)

      public static void RegisterRoutes(RouteCollection routes)
      {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new { controller = "hom", action = "Index", id = UrlParameter.Optional }
      );
      }

      Kodlarınızın sorunsuz çalışması lazım.

      Sil
  11. Merhaba,
    Evet routeConfig.cs görüntüsü aynı... ve global asax aşağıdaki gibi tanımlı

    RouteConfig.RegisterRoutes(RouteTable.Routes);

    url adresim "/home/index/ " veya "/" olunca çalışıyor fakat "/home/list/" olunca sayfa bulunamadı hatası veriyor list View mevcut sadece home controller içerisinde methodu yok.

    YanıtlaSil
    Yanıtlar
    1. MVC mimarisinin yapısı view sayfalarını metodlar ile döndürmektir. Metodu olmayan bi sayfa zaten, tanımlı değildir. O view sayfasını döndürecek bir metod yoktur.

      Bence öncelikle MVC mimarisi nedir? nasıl çalışır model, view, controller nedir? gibi soruların cevaplarını tam olarak anlamak için araştırma yapsanız yararlı olur.

      Sil
  12. Merhaba,
    Aslında MVC'deki eksiklerimi gidermek için sürekli çalışıyorum ve denemeler yapıyorum tavsiyeniz için Gerçekten Teşekkürler. sadece anlamadığım bir view 'in metodunun var olup olmadığı nerede kontrol edilir ve müdahale edemezmiyiz.

    YanıtlaSil
  13. Hocam Bur da Kullanıcıları Ldap(Zimbra) doğrulama yapsak nasıl bir yol izleriz? Örnek olarak yapabilir miyiz?

    YanıtlaSil
  14. Keşke yüz milyon satır makale yazacağınıza dersleri video olarak kayıt etseydiniz. Takibi daha kolay olur ve gözlerimiz bozulmazdı. :)

    YanıtlaSil
  15. ninject Ioc kullanıyorum, savechanges yöntemi için nasıl bir strateji izlemem gerek
    unitofwork gereklimi, başka şekilde save changes yapılabilirmi, unit of work desinin bu ninjectli projeye uygulayabilimiyim. sadece generic repository kullanmıyorum, özel olanlarda da var.
    Erkan yürek

    YanıtlaSil
    Yanıtlar
    1. - unitofowrk gerekli mi?
      - Hayır

      - başka savechanges yapılabilir mi?
      - Evet

      -unit of work desinin bu ninjectli projeye uygulayabilimiyim?
      - Evet

      Unit of work yazmazsanız her repository içerisinde savechanges yazmanız gerekecektir. ilişkili nesneleri eklerken her nesne eklendikten sonra savechanges calısır.

      Sil
  16. Hocam merhabalar
    public ActionResult ConfirmUser(Guid confirmationId)
    fonksiyonunda
    _userService.Find(confirmationId);
    hata alıyorum ondan dolayıda fonksiyon çalışmıyor

    Error 1 The best overloaded method match for 'MvcProject.Services.Users.IUserService.Find(int)' has some invalid arguments MvcProject.WebSite

    ve
    Error 2 Argument 1: cannot convert from 'System.Guid' to 'int'

    Hatalarını alıyorum çözüm bulan var mı ?

    YanıtlaSil
    Yanıtlar
    1. Hocam bazı metodlar service te yok. Bu eksik metdoları eklemeniz gerektigini soylemiştim yukarıda. ConfirmationId ile kullanıcı bulan metod da bunlardan biri...

      Sil
  17. public ActionResult Login(LoginModel model, string ReturnUrl)
    {
    var userr = _userService.Find(model.UserId);
    var user = _userService.ValidateUser(model.UserName, model.Password);
    if (ModelState.IsValid && user != null)
    {
    if (!user.IsConfirmed) bu kısımda da hata almaktayım nasıl bir yol izleyelim fikri olan var mı

    YanıtlaSil
  18. hocam yeni makale ne zaman gelir? :)

    YanıtlaSil
  19. Devami ne zaman serinin?

    YanıtlaSil
  20. Makalenin devamını bekliyoruz?

    YanıtlaSil
  21. Bu gidişle pek devamı gelmeyecek gibi:)

    YanıtlaSil
    Yanıtlar
    1. Makalelerin devamı inş. gelecek. Bu aralar işler yoğun, ayrıca proje de değişiklikler yapıyorum (yapısal olarak değişiklik yaptığım için biraz zaman alacak). Ayrıca projeyi baştan sona kadar video olarak cekme gibi bir fikrim var. Zamanım olursa öyle yapacağım ve projeyi de github dan paylaşacağım. Kusura bakmayın. Bu konudaki isteğiniz ve sabrınız için de ayrıca sağolun.

      Sil
    2. Sevgili Yazarımız yaşıyormuş :) Teşekkürler bu açıklama için..

      Sil
    3. Ali Rıza bey merhaba,

      Makalenizdeki aşağıdaki IsConfirmed özelliğinde hata alıyorum. Projede belirttiğiniz eksik metodları falan halledebildim. Ancak bu kısımda user nesnesinin LoginModel sınıfından örneklenen model ın böyle bir özelliği yok. bu özelliği siz haber sitesinde farklı bir şekilde uygulamışsınız. Bu makaledeki yapıya göre bunu nasıl uygulayabiliriz? yani sorunu nasıl çözebiliriz?
      <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      public ActionResult Login(LoginModel model, string ReturnUrl)
      {
      var user = _userService.ValidateUser(model.UserName, model.Password);
      if (ModelState.IsValid && user != null)
      {
      if (!user.IsConfirmed)
      {
      TempData["EpostaOnayMesaj"] = "E-posta adresiniz onaylı değildir. Lütfen e-posta adresinizdeki linki kullanarak e-posta adresinizi onaylayınız.";
      <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>><
      Cevabınız için şimdiden teşekkürler.

      Kolay gelsin.

      Sil
    4. "IsConfirmed özelliğinde hata alıyorum" ne demek? nasıl bir hata? hata mesajı falan? pek anlayamadım sorunuzu.

      Sil
    5. Sorunu şu şekilde çözdüm.
      <<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>
      public ActionResult Login(LoginModel model, string ReturnUrl)
      {
      if(ModelState.IsValid && _userService.ValidateUser(model.UserName, model.Password) )
      {
      var user = _userService.Find(model.UserName);
      if(!user.IsConfirmed)
      {
      .........................
      burdan sonrası sizin kodlarınızla aynı.
      <<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>
      IsConfirmed özelliğini kabul etmiyordu ve ilk if ile başlayan parantez içindeki sizin yazmış olduğunuz kod kısmında da hata veriyordu aynı şekilde. ben çözümü bu şekilde buldum.

      Kolay gelsin.

      Sil
  22. ali rıza kardeşim bir sonraki yazıyı bekleye bekleye saçlarım ağardı. :)
    walla bekliyoruz..

    YanıtlaSil
  23. ali rıza kardeşim bir sonraki yazıyı bekleye bekleye saçlarım ağardı. :)
    walla bekliyoruz..

    YanıtlaSil
  24. Ali Rıza Bey,

    projenizdeki "FindByUserNameAndPassword(userName, password)" bu methodun kodlarını paylaşabilir misiniz?

    Teşekkürler

    YanıtlaSil
    Yanıtlar
    1. Bu metodu yazabiliyor olmanız gerekiyor.

      Sil
  25. İyi çalışmalar.. Konuyla alakalı değil fakat bir sorum olacaktı size...
    runtime sırasında mvc4 ile partial bir sayfa eklemesi yapabilirmiyiz.. aspnet ile textbox nesnesini add.control gibi kodla ekleyebiliyorduk.. aslında yapmak istediğim farklı amaçlar için hazırlanmış partial dosyalarım var bunları veritabanı ile sayfaya ekleme çıkarma yapmak istiyorum. kusura bakmayın saçma bir soru da olabilir ..
    vereceğiniz cevap için şimdiden teşekkür ederim..

    YanıtlaSil
    Yanıtlar
    1. Gercek senaryoyu yazarsanız daha net cevap verebilirim. Dinamik partial view yuklemek mumkun (ajax ile). Sayfanıza dinamik kontroller akleyebilir siniz

      Sil
  26. merhaba hocam Kulanıcı Şifre Hatırlatma ile ilgili bi kaynağınız varmı

    YanıtlaSil
    Yanıtlar
    1. su an kod blogu yok elimde, ama kendiniz cok rahat yazabilirsiniz. Eğer şifreleri kriptolama kullanmıyorsanız, kullanıcıdan aldıgınız mail adresine şifreyi yollarsınız. Şifreler kriptolu ise, kullanıcının şifresini sıfırlarsınız direk.

      Sil
  27. Mvc'de yeni view oluşturdugumda bu hatayı alıyorum(hatanın resmi linkte) bunun sebebi hakkında bir bilginiz varmı?
    http://3.bp.blogspot.com/-DzEh2swwvZ0/UOAI7v0ewiI/AAAAAAAAAbw/l4NJM7h0SgA/s1600/ke.PNG

    YanıtlaSil
    Yanıtlar
    1. Bu hatayı vermemesi lazım. asp.net temprory files dosyasını bulup silin. Cache de tuttugu bilgilerden kaynaklanıyor. visual studio ile alaklı yani

      Sil
  28. "ASP.NET MVC - KOMPLE BİR PROJE YAPISI OLUŞTURMAK - 5" isimli makaleyi yayımlayacak mısınız ?

    YanıtlaSil
  29. Merhaba hocam, ben mvc 4'de blog sitesi oluşturmaya çalışıyorum aklıma bi soru takıldı, şimdi sizin üyelik sisteminizi mi kullansam yoksa mvc 4 ile beraber gelen membership mi ? hangisi daha güvenli kafam burda biraz karıştı çünkü mesela membershipte üye olan kişiye email gönderme gelmiyor ama sizin yapınızda var sanki sizin yapınız membership'e göre daha detaylı ?

    YanıtlaSil
  30. Hocam, ilk sayfadan itibaren harfiyen projenizi takip ettim öğrenmek için ama bazı yerlerde küçük küçük hatalar var :s

    neyse acaba mvc 4'da blog sitesi nasıl yapılır bunun hakkında makale yazmayı düşünüyormusunuz ?

    YanıtlaSil
  31. Yazılara devam edin nooolur :)

    YanıtlaSil
  32. Ali Rıza Selam,
    Anlatımın için teşekkürler,faydalı bir yazı olmuş.Devamını bekliyoruz.Eklemek istediğim bir kaç nokta var;
    _userService.ValidateUserName, userService.ValidateEmail gibi metodları kullanmak için bunları Generic Repository'nin içine yazmamız gerekiyor.O zaman da bunun generic olmasının anlamı kalmıyor.Dolayısıyla direk generic repository'leri kullanmak yerine onları inherit eden concrete repository'ler yazılması gerekiyor. Aşağıdaki gibi :

    http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle

    YanıtlaSil
  33. Ali Rıza bey tebrikler güzel bir yazı dizisi başlatmış ve yıllar sonra bile size dönüş yapan takipçiler edinmişsiniz.

    Bence de "Yine Yeniden Yeni Yapısıyla ASP.NET MVC - KOMPLE BİR PROJE YAPISI OLUŞTURMAK" diye bir yazı dizisi bekliyoruz sizden..

    Başarılar..

    YanıtlaSil
  34. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]

    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
    if (filterContext.HttpContext.User.Identity.IsAuthenticated)
    {
    base.HandleUnauthorizedRequest(filterContext);
    }
    else
    {
    var areaName = filterContext.RouteData.DataTokens["area"];
    if (areaName.Equals("Admin"))
    {
    filterContext.Result = new RedirectToRouteResult(new
    RouteValueDictionary(new { controller = "Admin", action = "AdminLogin", area = "Admin" }));
    }
    else if (areaName.Equals("Public"))
    {
    filterContext.Result = new RedirectToRouteResult(new
    RouteValueDictionary(new { controller = "Account", action = "Login" }));
    }
    // other conditions...

    }
    }

    YanıtlaSil
  35. Merhaba,
    Makaledeki repository deseni kullandım, ModelState.AddModelError işlemi ile silmede hata alırsam (örneğin Foreign Key hatası) her yerde aynı hata mesajını alıyorum.


    örneğin, rol tablosunda silme hatası alırsam, kullanıcı eklerken veya başka bir tabloya ekleme yaparken de Rol tablosunda silme yapıyormuş gibi aynı hatayı alıyorum.



    [HttpPost]
    public ActionResult AddRole(RoleModel model)
    {
    if (ModelState.IsValid)
    {
    try
    {
    Role role = new Role
    {
    //Id = Guid.NewGuid(),
    Name = model.Name,
    Description = model.Description
    };


    roleService.Insert(role);
    unitOfWork.SaveChanges();

    return RedirectToAction("ListRole", "Role");
    }
    catch (Exception ei)
    {
    ModelState.AddModelError(String.Empty, "Insert: " + (ei.InnerException != null ? ei.InnerException.InnerException.Message : ""));
    }
    }
    else
    {
    ModelState.AddModelError("", "Rol bilgileri eksik veya hatalı!");

    }

    return View(model);
    }

    YanıtlaSil