Cara mendapatkan pengguna saat ini di inti asp.net

129

Saya ingin mendapatkan pengguna saat ini untuk mendapatkan informasi pengguna seperti email. Tapi saya tidak bisa melakukan itu di inti asp.net. Saya sangat bingung Ini adalah kode saya.

HttpContexthampir nol dalam konstruktor pengontrol. Tidak baik mendapatkan pengguna di setiap tindakan. Saya Ingin mendapatkan informasi pengguna sekali dan mengaturnya menjadi ViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}
Mehran Hafizi
sumber
5
Menggunakan dengan MVC atau Web APi?
Tushar

Jawaban:

172
User.FindFirst(ClaimTypes.NameIdentifier).Value

EDIT untuk konstruktor

Kode di bawah ini berfungsi:

public Controller(IHttpContextAccessor httpContextAccessor)
{
    var userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value 
}

Edit untuk RTM

Anda harus mendaftar IHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }
adem caglin
sumber
2
itu bekerja dalam tindakan. tetapi saya ingin menggunakan dalam konstruktor pengontrol.
Mehran Hafizi
3
apakah mungkin menggunakan ini di kelas?
Mehran Hafizi
5
ClaimTypes.NameIdentifiermemberikan id pengguna saat ini, dan ClaimTypes.Namememberikan nama pengguna.
Nikolay Kostov
3
Adakah yang bisa memberi tahu saya apa yang salah dengan UserPrincipal.Current.Name?
tipura
2
@ademcaglin Untuk beberapa alasan Pengguna kembali nulldalam kasus saya? Saya menggunakan .Net core 2.1 Web apisekalipun.
Sruthi Varghese
55

Cara sederhana yang berhasil dan saya periksa.

private readonly UserManager<IdentityUser> _userManager;
public CompetitionsController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);

maka Anda dapat semua properti variabel ini seperti user.Email. Saya harap ini akan membantu seseorang.

Edit :

Ini adalah hal yang tampaknya sederhana tetapi agak rumit karena berbagai jenis sistem otentikasi di ASP.NET Core. Saya memperbarui karena beberapa orang mendapatkan null.

Untuk JWT Authentication (Diuji pada ASP.NET Core v3.0.0-preview7):

var email = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

var user = await _userManager.FindByEmailAsync(email);
Ahmad
sumber
1
berfungsi dengan baik untuk saya di dalam pengontrol di asp.net Core 2.0
jmdon
2
apa itu _userManager?
NullVoxPopuli
6
Dalam ASP.NET Core Identity, User Manager adalah layanan yang disediakan oleh Dependency Inject to Create Users. Lihat dokumen untuk info lebih lanjut:
Ahmad
2
Bagaimana ini bisa dicapai dengan metode non-async?
T3.0
Bagi saya mengembalikan nol. Mengapa?
Alberto Cláudio Mandlate
22

Memiliki cara lain untuk mendapatkan pengguna saat ini di Asp.NET Core - dan saya rasa saya melihatnya di suatu tempat di sini, di SO ^^

// Stores UserManager
private readonly UserManager<ApplicationUser> _manager; 

// Inject UserManager using dependency injection.
// Works only if you choose "Individual user accounts" during project creation.
public DemoController(UserManager<ApplicationUser> manager)  
{  
    _manager = manager;  
}

// You can also just take part after return and use it in async methods.
private async Task<ApplicationUser> GetCurrentUser()  
{  
    return await _manager.GetUserAsync(HttpContext.User);  
}  

// Generic demo method.
public async Task DemoMethod()  
{  
    var user = await GetCurrentUser(); 
    string userEmail = user.Email; // Here you gets user email 
    string userId = user.Id;
}  

Kode itu masuk ke pengontrol bernama DemoController. Tidak akan berfungsi tanpa kedua await (tidak akan dikompilasi);)

Hekkaryk
sumber
Ini membutuhkan penggunaan Identity
Fraze
1
Apa itu ApplicationUser?
Mike
ApplicationUser biasanya diwarisi dari IdentityUser sehingga dapat diperpanjang dengan properti tambahan, dll.
Corgalore
20

Saya harus mengatakan saya cukup terkejut bahwa HttpContext adalah null di dalam konstruktor. Saya yakin itu untuk alasan kinerja. Telah mengkonfirmasikan bahwa menggunakan IPrincipalseperti yang dijelaskan di bawah ini berhasil memasukkannya ke dalam konstruktor. Ini pada dasarnya melakukan hal yang sama seperti jawaban yang diterima, tetapi dengan cara yang lebih antarmuka.


Bagi siapa pun yang menemukan pertanyaan ini mencari jawaban untuk pertanyaan umum "Bagaimana cara mendapatkan pengguna saat ini?" Anda dapat Userlangsung mengaksesnya dari Controller.User. Tetapi Anda hanya dapat melakukan ini di dalam metode tindakan (saya berasumsi karena pengontrol tidak hanya berjalan dengan HttpContexts dan untuk alasan kinerja).

Namun - jika Anda membutuhkannya di konstruktor (seperti yang dilakukan OP) atau perlu membuat objek injeksi lain yang membutuhkan pengguna saat ini, maka di bawah ini adalah pendekatan yang lebih baik:

Masukkan IPrincipal untuk mendapatkan pengguna

Pertama bertemu IPrincipaldanIIdentity

public interface IPrincipal
{
    IIdentity Identity { get; }
    bool IsInRole(string role);
}

public interface IIdentity
{
    string AuthenticationType { get; }
    bool IsAuthenticated { get; }
    string Name { get; }
}

IPrincipaldan IIdentitymewakili pengguna dan nama pengguna. Wikipedia akan menghibur Anda jika 'Kepala Sekolah' terdengar aneh .

Penting untuk disadari bahwa baik Anda mendapatkannya dari IHttpContextAccessor.HttpContext.User, ControllerBase.Useratau ControllerBase.HttpContext.UserAnda mendapatkan suatu objek yang dijamin menjadi ClaimsPrincipalobjek yang diimplementasikanIPrincipal .

Tidak ada jenis Pengguna lain yang digunakan ASP.NET untuk Usersaat ini, (tetapi itu tidak berarti hal lain tidak dapat diterapkan IPrincipal).

Jadi jika Anda memiliki sesuatu yang memiliki ketergantungan 'nama pengguna saat ini' yang ingin Anda suntikkan, Anda harus menyuntikkan IPrincipaldan tentu saja tidak IHttpContextAccessor.

Penting: Jangan buang waktu untuk menyuntikkan IPrincipallangsung ke pengontrol Anda, atau metode tindakan - tidak ada gunanya karena Usersudah tersedia untuk Anda di sana.

Masuk startup.cs:

   // Inject IPrincipal
   services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);

Kemudian di objek DI Anda yang membutuhkan pengguna, Anda baru saja menyuntikkan IPrincipaluntuk mendapatkan pengguna saat ini.

Hal yang paling penting di sini adalah jika Anda melakukan pengujian unit, Anda tidak perlu mengirimkannya HttpContext, tetapi hanya perlu mengejek sesuatu yang mewakili IPrincipal yang sebenarnya bisa jadi ClaimsPrincipal .

Satu hal ekstra penting yang saya tidak 100% yakin. Jika Anda perlu untuk mengakses klaim yang sebenarnya dari ClaimsPrincipalyang Anda butuhkan untuk cor IPrincipaluntuk ClaimsPrincipal. Ini baik-baik saja karena kita tahu 100% bahwa pada waktu proses itu dari jenis itu (karena itulah HttpContext.User). Saya sebenarnya suka melakukan ini di konstruktor karena saya sudah tahu pasti ada yang IPrincipal akan menjadi ClaimsPrincipal.

Jika Anda sedang mengejek, buatClaimsPrincipal saja langsung dan berikan ke apa pun IPrincipal.

Persisnya mengapa tidak ada antarmuka untuk IClaimsPrincipalSaya tidak yakin. Saya berasumsi MS memutuskan ClaimsPrincipalitu hanya 'koleksi' khusus yang tidak memerlukan antarmuka.

Simon_Weaver
sumber
2
Ini memungkinkan Anda untuk menyuntikkan pengguna saat ini di mana saja di aplikasi Anda, jawaban yang bagus!
Machado
1
Ini tidak berhasil. Saya selalu mendapatkan nullsuntikan IPrincipal. Saya juga perlu menambahkan layanan sementara sebagai …GetService<IHttpContextAccessor>()?.HttpContext.User…(dengan ?) karena akan macet jika tidak (GetService mengembalikan null).
ygoe
Anda bisa melakukannya services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);karena HttpContext.User adalah ClaimsPrincipal.
Jay Zelos
18

Tampaknya mulai sekarang (April 2017) yang berfungsi sebagai berikut:

public string LoggedInUser => User.Identity.Name;

Setidaknya selama dalam a Controller

Grandizer
sumber
4
Anda tidak dapat secara implisit mengonversi jenis 'System.Security.Principal.IIdentity' menjadi 'string'.
Anthony Huang
3
string LoggedInUser = User.Identity.Name;
Alic W
5
Sebagai orang yang belum pernah melihat =>operator menggunakan seperti ini sebelumnya, ini disebut "Definisi Tubuh Ekspresi" dan dijelaskan dalam dokumentasi ini . Kalau-kalau orang masa depan seperti saya bertanya-tanya.
Nathan Clement
Kode Anda tidak mungkin dapat dikompilasi sebelum diedit, karena tidak ada konversi dari IIdentityke string, seperti yang juga dinyatakan di komentar teratas. Hasil edit hanya memperbaikinya. Saya juga tidak yakin bagaimana Anda mencapai kesimpulan Anda (khususnya karena poin "editor" hanya diberikan kepada pengguna di bawah reputasi 2k).
fuglede
9

Mungkin saya tidak melihat jawabannya, tetapi beginilah cara saya melakukannya.

  1. .Net Core -> Properties -> launchSettings.json

Anda harus mengubah nilai-nilai ini

"windowsAuthentication": true, // needs to be true
"anonymousAuthentication": false,  // needs to be false 

Startup.cs -> ConfigureServices (...)

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC atau Pengontrol Api Web

private readonly IHttpContextAccessor _httpContextAccessor;
//constructor then
_httpContextAccessor = httpContextAccessor;

Metode pengontrol:

string userName = _httpContextAccessor.HttpContext.User.Identity.Name;

Hasilnya adalah nama pengguna misalnya = Domain \ nama pengguna

Tom Stickel
sumber
4

Masalah saya adalah mengakses Pengguna yang masuk sebagai objek di file cshtml. Mengingat Anda menginginkan pengguna di ViewData, pendekatan ini mungkin bisa membantu:

Di file cshtml

@using Microsoft.AspNetCore.Identity
@inject UserManager<ApplicationUser> UserManager

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
    @UserManager.FindByNameAsync(UserManager.GetUserName(User)).Result.Email
    </title>
  </head>
  <body>

  </body>
</html>
MikeMajara
sumber
Tahu bagaimana Anda bisa mendapatkan properti navigasi untuk dimuat (Nama perusahaan dari properti navigasi perusahaan di kelas ApplicationUser saya). Tidak melihat cara untuk menyertakan properti navigasi.
Hunter Nelson
1

Selain jawaban yang ada, saya ingin menambahkan bahwa Anda juga dapat memiliki instance kelas yang tersedia di seluruh aplikasi yang menyimpan data terkait pengguna seperti UserIDdll.

Mungkin berguna untuk refactoring mis. Anda tidak ingin mengambil UserIDsetiap tindakan pengontrol dan mendeklarasikan UserIDparameter tambahan di setiap metode yang terkait dengan Lapisan Layanan.

Saya telah melakukan penelitian dan inilah postingan saya .

Anda hanya perlu memperluas kelas yang Anda peroleh DbContextdengan menambahkan UserIdproperti (atau menerapkan Sessionkelas kustom yang memiliki properti ini).

Pada tingkat filter, Anda dapat mengambil instance kelas Anda dan menetapkan UserIdnilai.

Setelah itu di mana pun Anda memasukkan instance - itu akan memiliki data yang diperlukan (masa hidup harus sesuai permintaan , jadi Anda mendaftarkannya menggunakan AddScopedmetode).

Contoh kerja:

public class AppInitializationFilter : IAsyncActionFilter
{
    private DBContextWithUserAuditing _dbContext;

    public AppInitializationFilter(
        DBContextWithUserAuditing dbContext
        )
    {
        _dbContext = dbContext;
    }

    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next
        )
    {
        string userId = null;
        int? tenantId = null;

        var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;

        var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
        if (userIdClaim != null)
        {
            userId = userIdClaim.Value;
        }

        var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
        if (tenantIdClaim != null)
        {
            tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
        }

        _dbContext.UserId = userId;
        _dbContext.TenantId = tenantId;

        var resultContext = await next();
    }
}

Untuk informasi lebih lanjut lihat jawaban saya .

Alex Herman
sumber
0

Mengambil IdentityUserjuga akan berhasil. Ini adalah objek pengguna saat ini dan semua nilai pengguna dapat diambil.

private readonly UserManager<IdentityUser> _userManager;
public yourController(UserManager<IdentityUser> userManager)
{
    _userManager = userManager;
}

var user = await _userManager.GetUserAsync(HttpContext.User);
zzdhxu
sumber
0

Jika Anda menggunakan Identitas scafold dan menggunakan Asp.net Core 2.2+, Anda dapat mengakses pengguna saat ini dari tampilan seperti ini:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

 @if (SignInManager.IsSignedIn(User))
    {
        <p>Hello @User.Identity.Name!</p>
    }
    else
    {
        <p>You're not signed in!</p>
    }

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio

Andrew
sumber
0

Ini pertanyaan lama tetapi kasus saya menunjukkan bahwa kasus saya tidak dibahas di sini.

Saya paling suka jawaban Simon_Weaver ( https://stackoverflow.com/a/54411397/2903893 ). Ia menjelaskan secara detail bagaimana cara mendapatkan nama pengguna menggunakan IPrincipal dan IIdentity. Jawaban ini sepenuhnya benar dan saya merekomendasikan untuk menggunakan pendekatan ini. Namun, selama debugging saya mengalami masalah ketika ASP.NET TIDAK dapat mengisi prinsip layanan dengan benar . (atau dengan kata lain, IPrincipal.Identity.Name adalah null)

Jelas bahwa untuk mendapatkan kerangka kerja MVC nama pengguna harus mengambilnya dari suatu tempat. Di dunia .NET, ASP.NET atau ASP.NET Core menggunakan middleware Open ID Connect. Dalam skenario sederhana, aplikasi web mengautentikasi pengguna di browser web. Dalam skenario ini, aplikasi web mengarahkan browser pengguna untuk masuk ke Azure AD. Azure AD mengembalikan respons masuk melalui browser pengguna, yang berisi klaim tentang pengguna dalam token keamanan. Untuk membuatnya berfungsi dalam kode untuk aplikasi Anda, Anda harus memberikan otoritas kepada mana delegasi aplikasi web Anda masuk. Saat Anda menerapkan aplikasi web Anda ke Azure Service, skenario umum untuk memenuhi persyaratan ini adalah dengan mengonfigurasi aplikasi web: "Layanan Aplikasi" -> Aplikasi Anda -> "Otentikasi / Otorisasi" blade -> "Autentikasi Layanan Aplikasi" = "Aktif"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ). Saya percaya (ini adalah tebakan saya) bahwa di balik proses ini, wizard menyesuaikan konfigurasi web "induk" dari aplikasi web ini dengan menambahkan pengaturan yang sama dengan yang saya tunjukkan di paragraf berikut. Pada dasarnya, masalah mengapa pendekatan ini TIDAK berfungsi di ASP.NET Core adalah karena konfigurasi mesin "induk" diabaikan oleh webconfig. (ini belum 100% yakin, saya hanya memberikan penjelasan terbaik yang saya miliki). Jadi, untuk membuatnya bekerja, Anda perlu mengatur ini secara manual di aplikasi Anda.

Berikut adalah artikel yang menjelaskan cara banyak mengatur aplikasi Anda untuk menggunakan Azure AD. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

Langkah 1: Daftarkan sampel dengan penyewa Azure AD Anda. (sudah jelas, saya tidak ingin menghabiskan waktu penjelasan saya).

Langkah 2: Di file appsettings.json: ganti nilai ClientID dengan ID Aplikasi dari aplikasi yang Anda daftarkan di portal Pendaftaran Aplikasi pada Langkah 1. ganti nilai TenantId dengan common

Langkah 3: Buka file Startup.cs dan dalam metode ConfigureServices, setelah baris yang berisi .AddAzureAD masukkan kode berikut, yang memungkinkan aplikasi Anda memasukkan pengguna dengan titik akhir Azure AD v2.0, yaitu Work and School dan Akun Microsoft Personal.

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
});

Ringkasan : Saya telah menunjukkan satu lagi kemungkinan masalah yang dapat menyebabkan kesalahan sehingga topik pembuka dijelaskan. Alasan masalah ini adalah tidak adanya konfigurasi untuk Azure AD (Open ID middleware). Untuk mengatasi masalah ini saya mengusulkan pengaturan secara manual "Otentikasi / Otorisasi". Tinjauan singkat tentang cara menyiapkan ini ditambahkan.

Roman Oira-Oira
sumber
0

Sebagian besar jawaban menunjukkan cara terbaik menangani HttpContextdari dokumentasi, yang juga saya gunakan.

Saya ingin menyebutkan bahwa Anda ingin memeriksa pengaturan proyek Anda saat debugging, defaultnya adalah Enable Anonymous Authentication = true.

Juan Emmanuel Afable
sumber
-1

Saya mendapatkan solusi saya

var claim = HttpContext.User.CurrentUserID();

public static class XYZ
{
    public static int CurrentUserID(this ClaimsPrincipal claim)
    {
        var userID = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
         "UserID").Value;
        return Convert.ToInt32(userID);
    }
    public static string CurrentUserRole(this ClaimsPrincipal claim)
    {
        var role = claimsPrincipal.Claims.ToList().Find(r => r.Type == 
        "Role").Value;
        return role;
    }
}
neeraj rai
sumber
1
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan mengapa kode ini memecahkan masalah akan meningkatkan nilai jangka panjang jawaban.
Alexander