Как хранить пользовательскую информацию в одном месте после входа в систему и доступа к нескольким контроллерам WEB API

Я работаю над веб-API с AngularJS. Я внедрил механизм токенов Web API несколько дней назад и смог войти в приложение, используя токен доступа. Я использовал внешнюю таблицу БД вместо таблицы идентификации ASP.NET для авторизации пользователя.

Я хочу сохранить информацию о пользователе в классе, чтобы к ней можно было легко получить доступ с разных контроллеров после входа пользователя. В настоящее время я использую ClaimsIdentity в классе контроллера для получения информации о пользователе.

UserIdentityViewModel.cs

public class UserIdentityViewModel
    {
        public string UserName { get; set; }
        public Guid UserId { get; set; }
    }

Startup.cs

public class Startup
    {
        public void Configuration(IAppBuilder app)
        {                
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            var myProvider = new AuthorizationServerProvider();
            OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/Token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = myProvider
            };
            app.UseOAuthAuthorizationServer(options);

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    }

AuthorizationServerProvider.cs

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated(); // 
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {           
            string userId = context.UserName;
            string password = context.Password;

            EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL();
            EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password);

            if(vmEmployeeAccess != null)
            {
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName));
                identity.AddClaim(new Claim("userid", Convert.ToString(vmEmployeeAccess.EmployeeId)));

                UserIdentityViewModel vmUser = new UserIdentityViewModel();
                vmUser.UserId = vmEmployeeAccess.EmployeeId;
                vmUser.UserName = vmEmployeeAccess.EmpName;

                context.Validated(identity);
            }
            else
            {
                context.SetError("invalid_grant", "Provided username and password is incorrect");
                return;
            }
        }       
    }

EventController.cs

 public class StreamEventController : ApiController
    {
        [Authorize]
        [Route("api/addevent")]
        [HttpPost]
        public List<string> AddEvent(StreamEventViewModel vmEvent)
        {
            //Able to get User Information from Identity.Claims
            var identity = (ClaimsIdentity)User.Identity;
            string userId = identity.Claims
                            .Where(c => c.Type == "userid")
                            .Select(c => c.Value).FirstOrDefault();

            //Not able to get User Information from following as new object instance gets created
            UserIdentityViewModel vmUser = new UserIdentityViewModel();

            vmEvent.CreatedBy = vmUser.UserId;
            vmEvent.ModifiedBy = vmUser.UserId;
       }
}

Вместо того, чтобы писать "Identity.Claims" в каждом методе каждого контроллера, я хочу использовать простой подход get/set или любую другую методологию для получения информации о пользователе. По моему мнению, использование статического класса также плохо, поскольку в нем будет храниться одна информация о пользователе, а информация о входе нескольких пользователей будет пропущена.

Пожалуйста, помогите мне и поделитесь со мной лучшим подходом, который использовался в других проектах Web API для входа в систему.

1 ответ

Решение

Вы можете добавить приватную переменную, которая будет установлена ​​в конструкторе контроллера, например так:

// Should only be used in protected methods.
private ClaimsIdentity ThisUser = null;

public MyController()
{
    if (User.Identity.IsAuthenticated)
        ThisUser = (ClaimsIdentity)User.Identity;
}

[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
    string userId = ThisUser.FindFirstValue("userid");

}

Или создайте класс User, в который вы загружаете все свойства:

private UserClass ThisUser = null;

public MyController()
{
    if (User.Identity.IsAuthenticated)
        ThisUser = new UserClass(User);
}

[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
    string userId = ThisUser.UserId;

}

Где UserClass это что-то вроде:

public class UserClass
{
    public string UserId { get; private set; }

    public UserClass(IPrincipal user)
    {
        UserId = user.FindFirstValue("userid");
    }
}

Но это просто накладные расходы на одно и то же. Вы можете рассмотреть возможность перенести вещи в расширение. В этом случае вы получите что-то вроде:

public static class RequestExtensions
{
    public static UserClass GetUser(this HttpRequestMessage request)
    {
        return new UserClass(request.GetOwinContext().Authentication.User);
    }

    public static ClaimsIdentiy GetUser2(this HttpRequestMessage request)
    {
        return new (ClaimsIdentity)request.GetOwinContext().Authentication.User;
    }
}

Который вы можете позвонить:

[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
    string userId = Request.GetUser.UserId;

    string userId2 = Request.GetUser2.FindFirstValue("userid");

}

Я думаю, я бы пошел на Request.GetUser2.FindFirstValue("userid");

Код призван дать вам представление. Я не тестировал код, но думаю, что он должен работать.

Другие вопросы по тегам