[asp.net] ASP.NET MVC-사용자 지정 IIdentity 또는 IPrincipal 설정

ASP.NET MVC 응용 프로그램에서 사용자 지정 IIdentity / IPrincipal을 설정하고 싶습니다. 더 쉽고 더 적합한 방법입니다. User.Identity.Idand과 같은 것을 호출 할 수 있도록 기본값을 확장하고 싶습니다 User.Identity.Role. 멋진 것은 아니며 몇 가지 추가 속성입니다.

나는 많은 기사와 질문을 읽었지만 실제로보다 어렵게 만들고 있다고 생각합니다. 나는 그것이 쉬울 것이라고 생각했다. 사용자가 로그온하면 사용자 지정 IIdentity를 설정하고 싶습니다. 그래서 나는 Application_PostAuthenticateRequest내 global.asax에서 구현 할 것이라고 생각했습니다 . 그러나 모든 요청에 ​​대해 호출되며 데이터베이스의 모든 데이터를 요청하고 사용자 정의 IPrincipal 객체를 넣는 모든 요청에 ​​대해 데이터베이스를 호출하고 싶지 않습니다. 그것은 또한 매우 불필요하고 느리고 잘못된 위치에 있습니다 (데이터베이스 호출). 그러나 나는 틀릴 수 있습니다. 아니면 그 데이터의 다른 곳은 어디입니까?

그래서 사용자가 로그인 할 때마다 세션에 필요한 변수를 추가 할 수 있다고 생각했는데 Application_PostAuthenticateRequest이벤트 처리기 의 사용자 정의 IIdentity에 추가했습니다 . 하지만, 내는 Context.Session것입니다 null그 길을 가야하는 것 또한되지 않도록,이.

나는 오늘 하루 동안이 일을하고 있는데 뭔가 빠진 것 같습니다. 너무 어렵지 않아야합니까? 나는 또한 이것과 함께 제공되는 모든 (반) 관련 것들에 약간 혼란스러워합니다. MembershipProvider, MembershipUser, RoleProvider, ProfileProvider, IPrincipal, IIdentity, FormsAuthentication… 암 i를 단 하나의 사람은 모두이 매우 혼란 발견?

누군가가 모든 추가 퍼지없이 IIdentity에 추가 데이터를 저장하는 간단하고 우아하고 효율적인 솔루션을 말해 줄 수 있다면 .. 그것은 좋을 것입니다! 나는 비슷한 질문이 있다는 것을 알고 있지만 필요한 대답이 거기에 있다면 간과해야합니다.



답변

내가하는 방법은 다음과 같습니다.

IIdentity 대신 IPrincipal을 사용하기로 결정했습니다. IIdentity와 IPrincipal을 모두 구현할 필요가 없기 때문입니다.

  1. 인터페이스 만들기

    interface ICustomPrincipal : IPrincipal
    {
        int Id { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
    }
  2. CustomPrincipal

    public class CustomPrincipal : ICustomPrincipal
    {
        public IIdentity Identity { get; private set; }
        public bool IsInRole(string role) { return false; }
    
        public CustomPrincipal(string email)
        {
            this.Identity = new GenericIdentity(email);
        }
    
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
  3. CustomPrincipalSerializeModel-사용자 정의 정보를 FormsAuthenticationTicket 오브젝트의 userdata 필드에 직렬화하는 데 사용됩니다.

    public class CustomPrincipalSerializeModel
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
  4. 로그인 방법-사용자 정의 정보로 쿠키 설정

    if (Membership.ValidateUser(viewModel.Email, viewModel.Password))
    {
        var user = userRepository.Users.Where(u => u.Email == viewModel.Email).First();
    
        CustomPrincipalSerializeModel serializeModel = new CustomPrincipalSerializeModel();
        serializeModel.Id = user.Id;
        serializeModel.FirstName = user.FirstName;
        serializeModel.LastName = user.LastName;
    
        JavaScriptSerializer serializer = new JavaScriptSerializer();
    
        string userData = serializer.Serialize(serializeModel);
    
        FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                 1,
                 viewModel.Email,
                 DateTime.Now,
                 DateTime.Now.AddMinutes(15),
                 false,
                 userData);
    
        string encTicket = FormsAuthentication.Encrypt(authTicket);
        HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
        Response.Cookies.Add(faCookie);
    
        return RedirectToAction("Index", "Home");
    }
  5. Global.asax.cs-쿠키를 읽고 HttpContext.User 객체를 대체합니다. PostAuthenticateRequest를 재정의하면됩니다.

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    
        if (authCookie != null)
        {
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    
            JavaScriptSerializer serializer = new JavaScriptSerializer();
    
            CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
    
            CustomPrincipal newUser = new CustomPrincipal(authTicket.Name);
            newUser.Id = serializeModel.Id;
            newUser.FirstName = serializeModel.FirstName;
            newUser.LastName = serializeModel.LastName;
    
            HttpContext.Current.User = newUser;
        }
    }
  6. 면도기 뷰에서 액세스

    @((User as CustomPrincipal).Id)
    @((User as CustomPrincipal).FirstName)
    @((User as CustomPrincipal).LastName)

그리고 코드에서 :

    (User as CustomPrincipal).Id
    (User as CustomPrincipal).FirstName
    (User as CustomPrincipal).LastName

코드가 자명하다고 생각합니다. 그렇지 않은 경우 알려주십시오.

또한보다 쉽게 ​​액세스 할 수 있도록 기본 컨트롤러를 만들고 반환 된 User 개체 (HttpContext.User)를 재정의 할 수 있습니다.

public class BaseController : Controller
{
    protected virtual new CustomPrincipal User
    {
        get { return HttpContext.User as CustomPrincipal; }
    }
}

그런 다음 각 컨트롤러에 대해

public class AccountController : BaseController
{
    // ...
}

다음과 같이 코드에서 사용자 정의 필드에 액세스 할 수 있습니다.

User.Id
User.FirstName
User.LastName

그러나 이것은 내부보기에서는 작동하지 않습니다. 이를 위해 사용자 정의 WebViewPage 구현을 작성해야합니다.

public abstract class BaseViewPage : WebViewPage
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
    public virtual new CustomPrincipal User
    {
        get { return base.User as CustomPrincipal; }
    }
}

Views / web.config에서 기본 페이지 유형으로 설정하십시오.

<pages pageBaseType="Your.Namespace.BaseViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Routing" />
  </namespaces>
</pages>

뷰에서 다음과 같이 액세스 할 수 있습니다.

@User.FirstName
@User.LastName


답변

ASP.NET MVC에 대해서는 직접 말할 수 없지만 ASP.NET Web Forms의 FormsAuthenticationTicket경우 사용자가 인증되면 쿠키를 만들어 쿠키로 암호화하는 것이 요령입니다 . 이런 식으로 데이터베이스를 한 번만 호출하면 (또는 AD 또는 인증을 수행하는 데 사용하는 모든 것), 각 후속 요청은 쿠키에 저장된 티켓을 기반으로 인증됩니다.

이것에 대한 좋은 기사 : http://www.ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html (깨진 링크)

편집하다:

위의 링크가 끊어 졌으므로 위의 답변에서 LukeP의 솔루션을 추천합니다. https : //.com/a/10524305- 또한 허용 된 답변이 해당 답변으로 변경되도록 제안합니다.

편집 2 :
깨진 링크의 대안 : https://web.archive.org/web/20120422011422/http://ondotnet.com/pub/a/dotnet/2004/02/02/effectiveformsauth.html


답변

다음은 작업을 수행하는 예제입니다. bool isValid는 일부 데이터 저장소 (사용자 데이터베이스라고 함)를보고 설정됩니다. UserID는 내가 관리하는 ID입니다. 이메일 주소와 같은 추가 정보를 사용자 데이터에 추가 할 수 있습니다.

protected void btnLogin_Click(object sender, EventArgs e)
{
    //Hard Coded for the moment
    bool isValid=true;
    if (isValid)
    {
         string userData = String.Empty;
         userData = userData + "UserID=" + userID;
         FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(30), true, userData);
         string encTicket = FormsAuthentication.Encrypt(ticket);
         HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
         Response.Cookies.Add(faCookie);
         //And send the user where they were heading
         string redirectUrl = FormsAuthentication.GetRedirectUrl(username, false);
         Response.Redirect(redirectUrl);
     }
}

golbal asax에서 다음 코드를 추가하여 정보를 검색하십시오.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket =
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

나중에 정보를 사용하려는 경우 다음과 같이 사용자 지정 주체에 액세스 할 수 있습니다.

(CustomPrincipal)this.User
or
(CustomPrincipal)this.Context.User

이를 통해 사용자 정의 사용자 정보에 액세스 할 수 있습니다.


답변

MVC는 컨트롤러 클래스에서 중단되는 OnAuthorize 메소드를 제공합니다. 또는 사용자 정의 조치 필터를 사용하여 권한 부여를 수행 할 수 있습니다. MVC를 사용하면 쉽게 할 수 있습니다. 여기에 대한 블로그 게시물을 게시했습니다. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0


답변

뷰에서 사용하기 위해 일부 메소드를 @User에 연결 해야하는 경우 해결책이 있습니다. 심각한 멤버쉽 사용자 정의에 대한 솔루션은 없지만 원래 질문이 뷰에만 필요한 경우에는 충분할 것입니다. 아래는 authorizefilter에서 반환 된 변수를 확인하는 데 사용되었으며 일부 링크가 표시 될지 여부를 확인하는 데 사용되었습니다 (모든 종류의 권한 부여 논리 또는 액세스 권한 부여가 아님).

using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Security.Principal;

    namespace SomeSite.Web.Helpers
    {
        public static class UserHelpers
        {
            public static bool IsEditor(this IPrincipal user)
            {
                return null; //Do some stuff
            }
        }
    }

그런 다음 web.config 영역에 참조를 추가하고보기에서 아래와 같이 호출하십시오.

@User.IsEditor()


답변

LukeP의 답변을 기반으로 하고 설정 timeout하고 requireSSL협력 할 방법을 추가하십시오 Web.config.

참조 링크

LukeP의 수정 된 코드

1을 timeout기준으로 설정 합니다 Web.Config. FormsAuthentication.Timeout은 Web.config의에 정의 된 시간 초과 값을 얻을 것이다. 다음을 함수로 감싸서 ticket다시 반환합니다 .

int version = 1;
DateTime now = DateTime.Now;

// respect to the `timeout` in Web.config.
TimeSpan timeout = FormsAuthentication.Timeout;
DateTime expire = now.Add(timeout);
bool isPersist = false;

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
     version,
     name,
     now,
     expire,
     isPersist,
     userData);

2, 구성에 따라 쿠키를 보안 또는 비보안으로 RequireSSL구성하십시오.

HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
// respect to `RequreSSL` in `Web.Config`
bool bSSL = FormsAuthentication.RequireSSL;
faCookie.Secure = bSSL;


답변

좋아, 그래서 나는이 아주 오래된 질문을 끌어서 심각한 암호 보관 인입니다. 그러나 이것에 대한 훨씬 간단한 접근 방식이 있습니다. 위의 @Baserz가 다루었습니다. 그리고 그것은 C # 확장 방법과 캐싱 (세션을 사용하지 마십시오)의 조합을 사용하는 것입니다.

실제로 Microsoft는 이미 Microsoft.AspNet.Identity.IdentityExtensions네임 스페이스 에 여러 가지 확장을 제공했습니다 . 예를 들어, GetUserId()사용자 ID를 반환하는 확장 메서드입니다. IPrincipal을 기반으로 클레임을 반환하는 GetUserName()및 도 있습니다 FindFirstValue().

따라서 네임 스페이스 만 포함하고 User.Identity.GetUserName()ASP.NET Identity에서 구성한대로 사용자 이름을 얻기 위해 호출 하면됩니다.

이전 ASP.NET ID가 오픈 소스가 아니기 때문에 캐시되어 있는지 확실하지 않으며 리버스 엔지니어링하지 않아도됩니다. 그러나 그렇지 않은 경우 고유 한 확장 방법을 작성하면 특정 시간 동안이 결과가 캐시됩니다.