(ASP.NET Core) SignalR 클라이언트 인증 처리 및 인증 정보 기준 특정 클라이언트에게 전송 처리 (feat JWT인증)

 

지난 글에서 JWT인증 처리 방법에 대해 설명했었습니다.

이번 글은 지난 글 JWT인증을 사용해서 ASP.NET Core 에서 SignalR 호스팅시 인증된 클라이언트만 접속을 허용하고 인증된 클라이언트 특정에게 RPC메서드 통신을 하는 방법입니다.

SignalR 클라이언트 인증 처리

우선 SignalR에서 인증 클라이언트 허용은 지난번 글에서 처리 했던 것 처럼 Authorize어트리뷰트만으로 처리 할 수 있습니다.

인증이 필요한 Hub 클레스에서 Authorize어트리뷰트를 추가 합니다.

[Authorize(Roles = "일반사용자")]
public class MyHub : Hub

위 추가만으로 인증된 클라이언트만 접속이 가능합니다.

클라이언트 측에서는 SignalR Client 모듈을 통해 다음과 같이 헤더에 JWT토큰 인증정보를 포함해서 SignalR 호스트에 접속할 수 있습니다.

다음은 클라이언트가 WPF인 경우 예제 코드 입니다.

먼저 누겟에서 SignalR Client관련 패키지를 설치하고


Microsoft.AspNetCore.SignalR.Client.Core (기타 의존 모듈도 자동으로 설치)


[JWT인증 정보를 통한 SignalR 호스트 접속]

HubConnection _realStatusHub;
_realStatusHub = new HubConnectionBuilder()
    .WithUrl(realStatusHub_URL, options =>
    {
        // jwt 인증 정보 추가
        options.Headers.Add("Authorization", $"Bearer {JWT 엑세스 토큰  넘김}");
    })
    .WithAutomaticReconnect(서버  접속 규칙 설정)
    .Build();
_realStatusHub.StartAsync();

참고로 javascript 클라이언트 측에서 JWT인증 정보를 포함해서 SignalR에 접속 요청은 다음과 같습니다.

var connection =
new signalR.HubConnectionBuilder()
    .withUrl("signalR 호스트 url", options =>
                                  {
                                    options.accessTokenFactory = "JWT 엑세스토큰"
                                  }).build();
  • javascript가 웹 브라우저 환경인 경우 SignalR 호스트 url이 현재 사이트와 다르다면 cros문제로 ASP.NET Core 서버측에서 cros설정이 추가적으로 필요합니다.

SignalR 인증된 특정 클라이언트 RPC통신

SignalR의 Microsoft.AspNetCore.SignalR.IHubClients 클래스에 User(string userId) 메서드를 통해서 특정 클라이언트 객체를 가져올 수 있습니다.

User(string userId) 메서드는 파라메터로 userID를 입력받고 있는데

내부에서 Microsoft.AspNetCore.SignalR.IUserIdProvider 서비스를 주입받아서 GetUserId(HubConnectionContext connection) 메서드를 통해 파라메터로 받은 userID를 찾고 있습니다.

따라서 파라메터로 전달받은 userID를 어떤 기준으로 찾을 것인지 Microsoft.AspNetCore.SignalR.IUserIdProvider 서비스를 구현해서 서비스에 등록 시켜주어야 합니다.

[UserFindProvider.cs]

using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;
using System.Linq;
 
public class UserFindProvider : IUserIdProvider
{
    public string GetUserId(HubConnectionContext connection)
    {
        if(connection.User.Identity is ClaimsIdentity claimsIdentity)
        {
            var claim = claimsIdentity.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Authentication);  // claim인증 정보중 Authentication키 기준으로 userID를 찾음
            if(claim != null)
            {
                return claim.Value;
            }
            else
            {
                return null;
            }
        }
 
        return null;
    }
}

이렇게 구현한 Microsoft.AspNetCore.SignalR.IUserIdProviderUser(string userId) 메서드 내부에서 DI할 수 있도록 서비스로 등록합니다.

[Startup.cs]

public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton<IUserIdProvider, UserFindProvider>();
}

이제 Microsoft.AspNetCore.SignalR.IHubClientsUser(string userId) 메서드를 통해 특정 클라이언트 객체를 찾을 수 있습니다.

[특정 클라이언트 전송]

var client = Clients.User(macAddr);
if(client != null)
    await client.SendAsync("메서드명", param1, param12 .....);