SignalR 허브에 대한 인증 및 권한 부여(SignalR 1.x)

작성자: Patrick Fletcher, Tom FitzMacken

경고

이 설명서는 최신 버전의 SignalR용이 아닙니다. ASP.NET Core SignalR을 살펴보세요.

이 항목에서는 허브 메서드에 액세스할 수 있는 사용자 또는 역할을 제한하는 방법을 설명합니다.

개요

이 항목에는 다음과 같은 섹션이 포함되어 있습니다.

권한 부여 특성

SignalR은 허브 또는 메서드에 액세스할 수 있는 사용자 또는 역할을 지정하는 Authorize 특성을 제공합니다. 이 특성은 네임스페이스에 Microsoft.AspNet.SignalR 있습니다. 허브 또는 허브의 Authorize 특정 메서드에 특성을 적용합니다. 특성을 허브 클래스에 Authorize 적용하면 지정된 권한 부여 요구 사항이 허브의 모든 메서드에 적용됩니다. 적용할 수 있는 다양한 유형의 권한 부여 요구 사항은 다음과 같습니다. 특성이 Authorize 없으면 허브에 연결된 클라이언트에서 허브의 모든 공용 메서드를 사용할 수 있습니다.

웹 애플리케이션에서 "관리"이라는 역할을 정의한 경우 해당 역할의 사용자만 다음 코드를 사용하여 허브에 액세스할 수 있도록 지정할 수 있습니다.

[Authorize(Roles = "Admin")] 
public class AdminAuthHub : Hub 
{ 
}

또는 허브에 모든 사용자가 사용할 수 있는 메서드 하나와 아래와 같이 인증된 사용자만 사용할 수 있는 두 번째 메서드가 포함되도록 지정할 수 있습니다.

public class SampleHub : Hub 
{ 
    public void UnrestrictedSend(string message){ . . . } 

    [Authorize] 
    public void AuthenticatedSend(string message){ . . . } 
}

다음 예제에서는 다양한 권한 부여 시나리오를 다룹니다.

  • [Authorize] – 인증된 사용자만
  • [Authorize(Roles = "Admin,Manager")] – 지정된 역할의 인증된 사용자만
  • [Authorize(Users = "user1,user2")] – 지정된 사용자 이름을 가진 인증된 사용자만
  • [Authorize(RequireOutgoing=false)] – 인증된 사용자만 허브를 호출할 수 있지만 특정 사용자만 메시지를 보낼 수 있지만 다른 모든 사용자가 메시지를 받을 수 있는 경우와 같이 서버에서 클라이언트로의 호출은 권한 부여에 의해 제한되지 않습니다. RequireOutgoing 속성은 허브 내의 개별 메서드가 아닌 전체 허브에만 적용할 수 있습니다. RequireOutgoing이 false로 설정되지 않은 경우 권한 부여 요구 사항을 충족하는 사용자만 서버에서 호출됩니다.

모든 허브에 대한 인증 필요

애플리케이션이 시작될 때 RequireAuthentication 메서드를 호출하여 애플리케이션의 모든 허브 및 허브 메서드에 대한 인증을 요구할 수 있습니다. 여러 허브가 있고 모든 허브에 인증 요구 사항을 적용하려는 경우 이 메서드를 사용할 수 있습니다. 이 메서드를 사용하면 역할, 사용자 또는 나가는 권한 부여를 지정할 수 없습니다. 허브 메서드에 대한 액세스가 인증된 사용자로만 제한되도록 지정할 수 있습니다. 그러나 허브 또는 메서드에 Authorize 특성을 적용하여 추가 요구 사항을 지정할 수 있습니다. 특성에 지정한 모든 요구 사항은 인증의 기본 요구 사항 외에도 적용됩니다.

다음 예제에서는 모든 허브 메서드를 인증된 사용자로 제한하는 Global.asax 파일을 보여 줍니다.

public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
        GlobalHost.HubPipeline.RequireAuthentication();
    }
}

SignalR 요청이 RequireAuthentication() 처리된 후 메서드를 호출하면 SignalR에서 예외가 InvalidOperationException throw됩니다. 파이프라인이 호출된 후에는 HubPipeline에 모듈을 추가할 수 없으므로 이 예외가 throw됩니다. 이전 예제에서는 첫 번째 요청을 처리하기 전에 한 번 실행되는 메서드에서 Application_Start 메서드를 호출 RequireAuthentication 하는 방법을 보여 있습니다.

사용자 지정된 권한 부여

권한 부여가 결정되는 방법을 사용자 지정해야 하는 경우 에서 AuthorizeAttribute 파생되는 클래스를 만들고 UserAuthorized 메서드를 재정의할 수 있습니다. 각 요청에 대해 이 메서드가 호출되어 사용자가 요청을 완료할 수 있는 권한이 있는지 여부를 확인합니다. 재정의된 메서드에서는 권한 부여 시나리오에 필요한 논리를 제공합니다. 다음 예제에서는 클레임 기반 ID를 통해 권한 부여를 적용하는 방법을 보여줍니다.

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class AuthorizeClaimsAttribute : AuthorizeAttribute
{
    protected override bool UserAuthorized(System.Security.Principal.IPrincipal user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        var principal = (ClaimsPrincipal)user;

        if (principal != null)
        {
            Claim authenticated = principal.FindFirst(ClaimTypes.Authentication);
            return authenticated.Value == "true" ? true : false;
        }
        else
        {
            return false;
        }
    }
}

클라이언트에 인증 정보 전달

클라이언트에서 실행되는 코드에서 인증 정보를 사용해야 할 수 있습니다. 클라이언트에서 메서드를 호출할 때 필요한 정보를 전달합니다. 예를 들어 채팅 애플리케이션 메서드는 아래와 같이 메시지를 게시하는 사람의 사용자 이름을 매개 변수로 전달할 수 있습니다.

public Task SendChatMessage(string message)
{
    string name;
    var user = Context.User;

    if (user.Identity.IsAuthenticated)
    {
        name = user.Identity.Name;
    }
    else
    {
        name = "anonymous";
    }
    return Clients.All.addMessageToPage(name, message);
}

또는 아래와 같이 인증 정보를 나타내는 개체를 만들고 해당 개체를 매개 변수로 전달할 수 있습니다.

public class SampleHub : Hub
{
    public override Task OnConnected()
    {
        return Clients.All.joined(GetAuthInfo());
    }

    protected object GetAuthInfo()
    {
        var user = Context.User;
        return new
        {
            IsAuthenticated = user.Identity.IsAuthenticated,
            IsAdmin = user.IsInRole("Admin"),
            UserName = user.Identity.Name
        };
    }
}

악의적인 사용자가 클라이언트의 요청을 모방하는 데 사용할 수 있으므로 한 클라이언트의 연결 ID를 다른 클라이언트에 전달해서는 안 됩니다.

.NET 클라이언트에 대한 인증 옵션

인증된 사용자로 제한된 허브와 상호 작용하는 콘솔 앱과 같은 .NET 클라이언트가 있는 경우 쿠키, 연결 헤더 또는 인증서에 인증 자격 증명을 전달할 수 있습니다. 이 섹션의 예제에서는 사용자를 인증하기 위해 이러한 다양한 메서드를 사용하는 방법을 보여 줍니다. 완전한 기능을 갖춘 SignalR 앱이 아닙니다. SignalR을 사용하는 .NET 클라이언트에 대한 자세한 내용은 허브 API 가이드 - .NET 클라이언트를 참조하세요.

.NET 클라이언트가 ASP.NET Forms 인증을 사용하는 허브와 상호 작용하는 경우 연결에서 인증 쿠키를 수동으로 설정해야 합니다. HubConnection 개체의 CookieContainer 속성에 쿠키를 추가합니다. 다음 예제에서는 웹 페이지에서 인증 쿠키를 검색하고 해당 쿠키를 연결에 추가하는 콘솔 앱을 보여 줍니다. 예제의 URL https://www.contoso.com/RemoteLogin 은 만들어야 하는 웹 페이지를 가리킵니다. 페이지에서 게시된 사용자 이름 및 암호를 검색하고 자격 증명을 사용하여 사용자에 로그인하려고 시도합니다.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        Cookie returnedCookie;

        Console.Write("Enter user name: ");
        string username = Console.ReadLine();

        Console.Write("Enter password: ");
        string password = Console.ReadLine();

        var authResult = AuthenticateUser(username, password, out returnedCookie);

        if (authResult)
        {
            connection.CookieContainer = new CookieContainer();
            connection.CookieContainer.Add(returnedCookie);
            Console.WriteLine("Welcome " + username);
        }
        else
        {
            Console.WriteLine("Login failed");
        }    
    }

    private static bool AuthenticateUser(string user, string password, out Cookie authCookie)
    {
        var request = WebRequest.Create("https://www.contoso.com/RemoteLogin") as HttpWebRequest;
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.CookieContainer = new CookieContainer();

        var authCredentials = "UserName=" + user + "&Password=" + password;
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(authCredentials);
        request.ContentLength = bytes.Length;
        using (var requestStream = request.GetRequestStream())
        {
            requestStream.Write(bytes, 0, bytes.Length);
        }

        using (var response = request.GetResponse() as HttpWebResponse)
        {
            authCookie = response.Cookies[FormsAuthentication.FormsCookieName];
        }

        if (authCookie != null)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

콘솔 앱은 다음 코드 숨김 파일이 포함된 빈 페이지를 참조할 수 있는 www.contoso.com/RemoteLogin 자격 증명을 게시합니다.

using System;
using System.Web.Security;

namespace SignalRWithConsoleChat
{
    public partial class RemoteLogin : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string username = Request["UserName"];
            string password = Request["Password"];
            bool result = Membership.ValidateUser(username, password);
            if (result)
            {
                FormsAuthentication.SetAuthCookie(username, false);
            }
        }
    }
}

Windows 인증

Windows 인증 사용하는 경우 DefaultCredentials 속성을 사용하여 현재 사용자의 자격 증명을 전달할 수 있습니다. 연결에 대한 자격 증명을 DefaultCredentials 값으로 설정합니다.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.Credentials = CredentialCache.DefaultCredentials;
        connection.Start().Wait();
    }
}

연결 헤더

애플리케이션에서 쿠키를 사용하지 않는 경우 연결 헤더에 사용자 정보를 전달할 수 있습니다. 예를 들어 연결 헤더에 토큰을 전달할 수 있습니다.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.Headers.Add("myauthtoken", /* token data */);
        connection.Start().Wait();
    }
}

그런 다음 허브에서 사용자의 토큰을 확인합니다.

인증서

클라이언트 인증서를 전달하여 사용자를 확인할 수 있습니다. 연결을 만들 때 인증서를 추가합니다. 다음 예제에서는 연결에 클라이언트 인증서를 추가하는 방법만 보여 냅니다. 전체 콘솔 앱이 표시되지 않습니다. 인증서를 만드는 여러 가지 방법을 제공하는 X509Certificate 클래스를 사용합니다.

class Program
{
    static void Main(string[] args)
    {
        var connection = new HubConnection("http://www.contoso.com/");
        connection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
        connection.Start().Wait();
    }
}