programing

ASP.NET MVC - 역할 제공자의 대안?

skycolor 2023. 8. 10. 18:39
반응형

ASP.NET MVC - 역할 제공자의 대안?

저는 역할 제공자와 멤버십 제공자의 방식이 너무 서툴기 때문에 사용을 피하려고 노력하고 있으며, 따라서 덜 서툴고 관리하기 쉽고 유연한 저만의 "버전"을 만들려고 노력하고 있습니다.이제 제 질문입니다.괜찮은 역할 제공자에 대한 대안이 있습니까?(사용자 지정 역할 공급자, 구성원 자격 공급자 등을 수행할 수 있다는 것을 알고 있습니다.

관리성과 유연성이 뛰어나다는 것은 역할 정적 클래스를 사용하는 데 제한이 있고 데이터베이스 컨텍스트와 상호 작용하는 서비스 계층에 직접 구현하지 않는다는 것을 의미합니다. 대신 고유한 데이터베이스 컨텍스트 등이 있는 역할 정적 클래스를 사용해야 하고 테이블 이름도 형편없습니다.

잘 부탁드립니다.

저는 당신과 같은 처지입니다. 저는 항상 역할 제공자들을 싫어했습니다., 작은 웹사이트를 위해 일을 시작하고 실행하고 싶다면 그것들은 훌륭하지만, 그것들은 그다지 현실적이지 않습니다.제가 항상 발견한 주요 단점은 ASP와 직접 연결된다는 것입니다.그물.

제가 최근 프로젝트에 참여한 방법은 서비스 계층의 일부인 몇 가지 인터페이스를 정의하는 것이었습니다(참고: 이러한 인터페이스를 상당히 단순화했지만 쉽게 추가할 수 있었습니다)

public interface IAuthenticationService
{
    bool Login(string username, string password);
    void Logout(User user);
}

public interface IAuthorizationService
{
    bool Authorize(User user, Roles requiredRoles);
}

그러면 사용자가 다음을 수행할 수 있습니다.Roles열거형:

public enum Roles
{
    Accounting = 1,
    Scheduling = 2,
    Prescriptions = 4
    // What ever else you need to define here.
    // Notice all powers of 2 so we can OR them to combine role permissions.
}

public class User
{
    bool IsAdministrator { get; set; }
    Roles Permissions { get; set; }
}

당신을 위해IAuthenticationService할 수 . 그런 " " " " " " " " " " " " " " " " " " 을 사용할 수 .FormsAuthenticationService쿠키를 세팅하는 등 조금 더 많은 일을 합니다.당신을 위해AuthorizationService다음과 같은 것이 필요합니다.

public class AuthorizationService : IAuthorizationService
{
    public bool Authorize(User userSession, Roles requiredRoles)
    {
        if (userSession.IsAdministrator)
        {
            return true;
        }
        else
        {
            // Check if the roles enum has the specific role bit set.
            return (requiredRoles & user.Roles) == requiredRoles;
        }
    }
}

이러한 기본 서비스 위에 암호 등을 재설정하는 서비스를 쉽게 추가할 수 있습니다.

MVC를에 MVC를 하여 작업 할 수 .ActionFilter:

public class RequirePermissionFilter : IAuthorizationFilter
{
    private readonly IAuthorizationService authorizationService;
    private readonly Roles permissions;

    public RequirePermissionFilter(IAuthorizationService authorizationService, Roles requiredRoles)
    {
        this.authorizationService = authorizationService;
        this.permissions = requiredRoles;
        this.isAdministrator = isAdministrator;
    }

    private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
    {
        return this.authorizationService ?? new FormsAuthorizationService(httpContext);
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var authSvc = this.CreateAuthorizationService(filterContext.HttpContext);
        // Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
        var userSession = (User)filterContext.HttpContext.Session["CurrentUser"];

        var success = authSvc.Authorize(userSession, this.permissions);

        if (success)
        {
            // Since authorization is performed at the action level, the authorization code runs
            // after the output caching module. In the worst case this could allow an authorized user
            // to cause the page to be cached, then an unauthorized user would later be served the
            // cached page. We work around this by telling proxies not to cache the sensitive page,
            // then we hook our custom authorization code into the caching mechanism so that we have
            // the final say on whether or not a page should be served from the cache.
            var cache = filterContext.HttpContext.Response.Cache;
            cache.SetProxyMaxAge(new TimeSpan(0));
            cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
            {
                validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
            }, null);
        }
        else
        {
            this.HandleUnauthorizedRequest(filterContext);
        }
    }

    private void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Ajax requests will return status code 500 because we don't want to return the result of the
        // redirect to the login page.
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new HttpStatusCodeResult(500);
        }
        else
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }

    public HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
    {
        var authSvc = this.CreateAuthorizationService(httpContext);
        var userSession = (User)httpContext.Session["CurrentUser"];

        var success = authSvc.Authorize(userSession, this.permissions);

        if (success)
        {
            return HttpValidationStatus.Valid;
        }
        else
        {
            return HttpValidationStatus.IgnoreThisRequest;
        }
    }
}

그런 다음 컨트롤러 작업에 대해 다음과 같이 장식할 수 있습니다.

[RequirePermission(Roles.Accounting)]
public ViewResult Index()
{
   // ...
}

이 접근 방식의 장점은 의존성 주입과 IoC 컨테이너를 사용하여 연결할 수 있다는 것입니다.또한 ASP뿐만 아니라 여러 응용프로그램에서도 사용할 수 있습니다.NET 1).ORM을 사용하여 적절한 스키마를 정의할 수 있습니다.

더 자세한 정보가 필요한 경우FormsAuthorization/Authentication서비스나 여기서 어디로 가야 하는지 알려주세요.

편집: "보안 트리밍"을 추가하려면 Html 도우미를 사용할 수 있습니다.이건 좀 더...하지만 당신은 이해합니다.

public static bool SecurityTrim<TModel>(this HtmlHelper<TModel> source, Roles requiredRoles)
{
    var authorizationService = new FormsAuthorizationService();
    var user = (User)HttpContext.Current.Session["CurrentUser"];
    return authorizationService.Authorize(user, requiredRoles);
}

그리고 보기 내부에서(여기서는 레이저 구문 사용):

@if(Html.SecurityTrim(Roles.Accounting))
{
    <span>Only for accounting</span>
}

: 파일UserSession다음과 같이 보입니다.

public class UserSession
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public bool IsAdministrator { get; set; }
    public Roles GetRoles()
    {
         // make the call to the database or whatever here.
         // or just turn this into a property.
    }
}

이렇게 하면 암호 해시 및 기타 모든 세부 정보가 사용자의 세션 수명에 필요하지 않으므로 현재 사용자의 세션 내에 노출되지 않습니다.

저는 여기에 @The Cloudless Sky 게시물을 기반으로 한 역할 제공자를 구현했습니다.제가 한 일을 추가하고 공유할 수 있다고 생각했던 것들이 몇 가지 있습니다.먼저, 사용하려면 다음과 같이 하십시오.RequirepPermission해야 합니다. 구해 특로 필에 대한 클래 스ActionFilterAttributeRequirepPermission학생들

클래스IAuthenticationService그리고.IAuthorizationService

public interface IAuthenticationService
{
    void SignIn(string userName, bool createPersistentCookie);
    void SignOut();
}

public interface IAuthorizationService
{
    bool Authorize(UserSession user, string[] requiredRoles);
}

FormsAuthenticationService 시간

/// <summary>
/// This class is for Form Authentication
/// </summary>
public class FormsAuthenticationService : IAuthenticationService
{

    public void SignIn(string userName, bool createPersistentCookie)
    {
        if (String.IsNullOrEmpty(userName)) throw new ArgumentException(@"Value cannot be null or empty.", "userName");

        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }

    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}

UserSession

public class UserSession
{
    public string UserName { get; set; }
    public IEnumerable<string> UserRoles { get; set; }
}

는 또다요점은입니다.FormsAuthorizationService 및 에 대해 설명합니다.httpContext.Session["CurrentUser"] 방법은 를 새로 userSession에서 입니다.httpContext.User.Identity.Name에 대한 은 userSession에서 확인할 수 .FormsAuthorizationService학생들

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter
{
    #region Fields

    private readonly IAuthorizationService _authorizationService;
    private readonly string[] _permissions;

    #endregion

    #region Constructors

    public RequirePermissionAttribute(string requiredRoles)
    {
        _permissions = requiredRoles.Trim().Split(',').ToArray();
        _authorizationService = null;
    }

    #endregion

    #region Methods

    private IAuthorizationService CreateAuthorizationService(HttpContextBase httpContext)
    {
        return _authorizationService ?? new FormsAuthorizationService(httpContext);
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var authSvc = CreateAuthorizationService(filterContext.HttpContext);
        // Get the current user... you could store in session or the HttpContext if you want too. It would be set inside the FormsAuthenticationService.
        if (filterContext.HttpContext.Session == null) return;
        if (filterContext.HttpContext.Request == null) return;
        var success = false;
        if (filterContext.HttpContext.Session["__Roles"] != null)
        {
            var rolesSession = filterContext.HttpContext.Session["__Roles"];
            var roles = rolesSession.ToString().Trim().Split(',').ToList();
            var userSession = new UserSession
            {
                UserName = filterContext.HttpContext.User.Identity.Name,
                UserRoles = roles
            };
            success = authSvc.Authorize(userSession, _permissions);
        }
        if (success)
            {
                // Since authorization is performed at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether or not a page should be served from the cache.
                var cache = filterContext.HttpContext.Response.Cache;
                cache.SetProxyMaxAge(new TimeSpan(0));
                cache.AddValidationCallback((HttpContext context, object data, ref HttpValidationStatus validationStatus) =>
                                                {
                                                    validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
                                                }, null);
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
    }

    private static void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Ajax requests will return status code 500 because we don't want to return the result of the
        // redirect to the login page.
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new HttpStatusCodeResult(500);
        }
        else
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }

    private HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
    {
        var authSvc = CreateAuthorizationService(httpContext);
        if (httpContext.Session != null)
        {
            var success = false;
            if (httpContext.Session["__Roles"] != null)
            {
                var rolesSession = httpContext.Session["__Roles"];
                var roles = rolesSession.ToString().Trim().Split(',').ToList();
                var userSession = new UserSession
                {
                    UserName = httpContext.User.Identity.Name,
                    UserRoles = roles
                };
                success = authSvc.Authorize(userSession, _permissions);
            }
            return success ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }
        return 0;
    }

    #endregion
}

internal class FormsAuthorizationService : IAuthorizationService
{
    private readonly HttpContextBase _httpContext;

    public FormsAuthorizationService(HttpContextBase httpContext)
    {
        _httpContext = httpContext;
    }

    public bool Authorize(UserSession userSession, string[] requiredRoles)
    {
        return userSession.UserRoles.Any(role => requiredRoles.Any(item => item == role));
    }
}

그런 다음 사용자가 인증된 후 컨트롤러에서 데이터베이스에서 역할을 가져와 역할 세션에 할당할 수 있습니다.

var roles = Repository.GetRolesByUserId(Id);
if (ControllerContext.HttpContext.Session != null)
   ControllerContext.HttpContext.Session.Add("__Roles",roles);
FormsService.SignIn(collection.Name, true);

사용자가 시스템에서 로그아웃한 후 세션을 삭제할 수 있습니다.

FormsService.SignOut();
Session.Abandon();
return RedirectToAction("Index", "Account");

이 모델에서 주의할 점은 사용자가 시스템에 로그인할 때 사용자에게 역할이 할당된 경우 사용자가 로그아웃한 후 시스템에 다시 로그인하지 않으면 권한 부여가 작동하지 않는다는 것입니다.

또한 데이터베이스에서 직접 역할을 가져와 컨트롤러의 역할 세션으로 설정할 수 있으므로 역할에 대한 클래스를 별도로 둘 필요가 없습니다.

이러한 코드를 모두 구현한 후 마지막 단계는 컨트롤러의 메서드에 이 속성을 바인딩하는 것입니다.

[RequirePermission("Admin,DM")]
public ActionResult Create()
{
return View();
}

Castle Windsor Dependency Injection(캐슬 윈저 종속성 주입)을 사용하는 경우 구현하기로 선택한 소스에서 사용자 권한을 확인하는 데 사용할 수 있는 역할 공급자 목록을 주입할 수 있습니다.

http://ivida.co.uk/2011/05/18/mvc-getting-user-roles-from-multiple-sources-register-and-resolve-arrays-of-dependencis-using-the-fluent-api/

역할에 정적 클래스를 사용할 필요는 없습니다.예를 들어 SqlRoleProvider를 사용하면 데이터베이스에서 역할을 정의할 수 있습니다.

물론 자신의 서비스 계층에서 역할을 검색하려는 경우에는 자신의 역할 공급자를 만드는 것이 그리 어렵지 않습니다. 구현할 수 있는 방법이 많지 않습니다.

적절한 인터페이스를 재정의하여 자신의 구성원 자격 및 역할 제공자를 구현할 수 있습니다.

처음부터 시작하려는 경우 일반적으로 이러한 유형의 작업은 사용자 자격 증명을 http 컨텍스트 또는 세션에 저장하는 사용자 지정 http 모듈로 구현됩니다.어느 쪽이든 인증 토큰을 사용하여 쿠키를 설정할 수 있습니다.

언급URL : https://stackoverflow.com/questions/4837103/asp-net-mvc-alternative-to-role-provider

반응형