วิธีรับผู้ใช้ปัจจุบันในแกน asp.net


130

ฉันต้องการรับผู้ใช้ปัจจุบันเพื่อรับข้อมูลของผู้ใช้เช่นอีเมล แต่ฉันไม่สามารถทำได้ในแกน asp.net ฉันสับสนมากนี่คือรหัสของฉัน

HttpContextเกือบจะเป็นโมฆะในตัวสร้างคอนโทรลเลอร์ การรับผู้ใช้ในแต่ละการกระทำนั้นไม่ดี ฉันต้องการรับข้อมูลของผู้ใช้ครั้งเดียวและตั้งค่าเป็นViewData;

public DashboardController()
{
    var user = HttpContext.User.GetUserId();
}

5
ใช้กับ MVC หรือ Web APi?
Tushar

คำตอบ:


173
User.FindFirst(ClaimTypes.NameIdentifier).Value

แก้ไขสำหรับตัวสร้าง

โค้ดด้านล่างใช้งานได้:

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

แก้ไขสำหรับ RTM

คุณควรลงทะเบียนIHttpContextAccessor:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
    }

2
มันใช้งานได้ในการดำเนินการ แต่ฉันต้องการใช้ในตัวสร้างคอนโทรลเลอร์
Mehran Hafizi

3
เป็นไปได้ไหมที่จะใช้สิ่งนี้ในชั้นเรียน
Mehran Hafizi

5
ClaimTypes.NameIdentifierให้รหัสผู้ใช้ปัจจุบันและClaimTypes.Nameให้ชื่อผู้ใช้
Nikolay Kostov

3
ใครช่วยบอกทีว่า UserPrincipal.Current.Name ผิดพลาดอะไร
tipura

2
@ademcaglin ด้วยเหตุผลบางประการผู้ใช้จะกลับมาnullในกรณีของฉัน? ฉันใช้.Net core 2.1 Web apiแม้ว่า
Sruthi Varghese

55

วิธีง่ายๆที่ได้ผลและฉันตรวจสอบแล้ว

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

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

user.Emailแล้วคุณจะได้ทุกคุณสมบัติของตัวแปรนี้ชอบ ฉันหวังว่านี่จะช่วยใครสักคนได้

แก้ไข :

เห็นได้ชัดว่าเป็นสิ่งที่ง่าย แต่มีสาเหตุที่ซับซ้อนเล็กน้อยของระบบการตรวจสอบสิทธิ์ประเภทต่างๆใน ASP.NET Core nullปรับปรุงฉันสาเหตุที่บางคนจะได้รับ

สำหรับ JWT Authentication (ทดสอบบน ASP.NET Core v3.0.0-preview7):

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

var user = await _userManager.FindByEmailAsync(email);

1
ใช้งานได้ดีสำหรับฉันภายในคอนโทรลเลอร์ใน asp.net Core 2.0
jmdon

2
_userManager คืออะไร
NullVoxPopuli

6
ใน ASP.NET Core Identity ตัวจัดการผู้ใช้เป็นบริการที่จัดทำโดย Dependency Inject เพื่อสร้างผู้ใช้ ดูเอกสารสำหรับข้อมูลเพิ่มเติม:
Ahmad

2
วิธีนี้สามารถทำได้โดยวิธีที่ไม่ใช่ async?
T3.0

สำหรับฉันคือคืนค่าว่าง ทำไม?
Alberto Cláudio Mandlate

22

มีวิธีอื่นในการรับผู้ใช้ปัจจุบันใน Asp.NET Core - และฉันคิดว่าฉันเห็นมันที่ไหนสักแห่งที่นี่บน 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;
}  

รหัสนั้นไปที่คอนโทรลเลอร์ชื่อ DemoController จะไม่ทำงานโดยไม่ต้องรอทั้งคู่ (จะไม่รวบรวม);)


สิ่งนี้จำเป็นต้องใช้ Identity
Fraze

1
ApplicationUser คืออะไร?
ไมค์

ApplicationUser มักจะสืบทอดมาจาก IdentityUser ดังนั้นจึงสามารถขยายด้วยคุณสมบัติเพิ่มเติม ฯลฯ
Corgalore

20

ฉันต้องบอกว่าฉันค่อนข้างแปลกใจที่ HttpContext เป็นโมฆะภายในตัวสร้าง ฉันแน่ใจว่าเป็นเพราะเหตุผลด้านประสิทธิภาพ ได้รับการยืนยันว่าการใช้IPrincipalตามที่อธิบายไว้ด้านล่างจะทำให้มันถูกฉีดเข้าไปในตัวสร้าง โดยพื้นฐานแล้วมันทำเช่นเดียวกับคำตอบที่ยอมรับ แต่ในทางอินเตอร์เฟสมากกว่า


สำหรับใครก็ตามที่พบคำถามนี้กำลังมองหาคำตอบทั่วไป"How to get current user?" คุณก็สามารถเข้าถึงได้โดยตรงจากUser Controller.Userแต่คุณสามารถทำได้เฉพาะวิธีการดำเนินการภายในเท่านั้น (ฉันถือว่าเป็นเพราะตัวควบคุมไม่เพียง แต่ทำงานด้วย HttpContexts และด้วยเหตุผลด้านประสิทธิภาพ)

อย่างไรก็ตาม - หากคุณต้องการมันในคอนสตรัคเตอร์ (เหมือนที่ OP ทำ) หรือต้องการสร้างออบเจ็กต์แบบฉีดอื่น ๆ ที่ต้องการผู้ใช้ปัจจุบันวิธีการด้านล่างนี้เป็นแนวทางที่ดีกว่า:

ฉีด IPrincipal เพื่อรับผู้ใช้

พบกันครั้งแรกIPrincipalและIIdentity

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

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

IPrincipalและIIdentityแสดงถึงผู้ใช้และชื่อผู้ใช้ วิกิพีเดียจะปลอบคุณถ้า 'หลัก' เสียงแปลก ๆ

สำคัญที่ต้องตระหนักว่าไม่ว่าคุณจะได้รับจากIHttpContextAccessor.HttpContext.User, ControllerBase.UserหรือControllerBase.HttpContext.Userคุณกำลังได้รับวัตถุที่รับประกันได้ว่าจะหนึ่งClaimsPrincipalIPrincipalวัตถุซึ่งการดำเนินการ

ไม่มีผู้ใช้ประเภทอื่นที่ ASP.NET ใช้Userในขณะนี้ (แต่ไม่ได้หมายความว่าอย่างอื่นไม่สามารถใช้งานได้IPrincipal)

ดังนั้นถ้าคุณมีบางสิ่งบางอย่างที่มีการพึ่งพาของ 'ชื่อผู้ใช้ปัจจุบันที่คุณต้องการฉีดที่คุณควรจะฉีดและไม่แน่นอนIPrincipalIHttpContextAccessor

สำคัญ:อย่าเสียเวลาฉีดยาIPrincipalไปที่คอนโทรลเลอร์หรือวิธีการดำเนินการโดยตรงเพราะไม่มีประโยชน์Userสำหรับคุณแล้ว

ในstartup.cs:

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

จากนั้นในวัตถุ DI ของคุณที่ต้องการผู้ใช้ที่คุณเพิ่งฉีดIPrincipalเพื่อรับผู้ใช้ปัจจุบัน

สิ่งที่สำคัญที่สุดที่นี่คือถ้าคุณกำลังทำทดสอบหน่วยคุณไม่จำเป็นต้องส่งในHttpContextแต่ต้องมีอะไรบางอย่างที่แสดงถึงการเยาะเย้ยเท่านั้นซึ่งก็สามารถIPrincipal ClaimsPrincipal

สิ่งสำคัญอย่างหนึ่งที่ฉันไม่แน่ใจ 100% หากคุณจำเป็นต้องเข้าถึงการเรียกร้องที่เกิดขึ้นจริงจากClaimsPrincipalคุณจำเป็นต้องโยนไปIPrincipal ClaimsPrincipalนี่เป็นเรื่องปกติเนื่องจากเรารู้ 100% ว่าในรันไทม์เป็นประเภทนั้น (เพราะนั่นคือสิ่งที่HttpContext.Userเป็น) ที่จริงฉันชอบทำสิ่งนี้ในตัวสร้างเพราะฉันรู้แล้วว่าIPrincipal จะเป็นไฟล์ClaimsPrincipal.

หากคุณกำลังล้อเลียนเพียงแค่สร้างClaimsPrincipalโดยตรงและส่งต่อไปยังทุกIPrincipalอย่าง

ว่าทำไมไม่มีอินเทอร์เฟซสำหรับIClaimsPrincipalฉันไม่แน่ใจ ฉันถือว่า MS ตัดสินใจว่าClaimsPrincipalเป็นเพียง 'คอลเลกชัน' พิเศษที่ไม่รับประกันอินเทอร์เฟซ


2
สิ่งนี้ช่วยให้คุณฉีดผู้ใช้ปัจจุบันได้ทุกที่ในแอปพลิเคชันของคุณคำตอบที่ยอดเยี่ยม!
Machado

1
วิธีนี้ใช้ไม่ได้ ผมเคยได้รับสำหรับฉีดnull IPrincipalฉันต้องเพิ่มบริการชั่วคราวเป็น…GetService<IHttpContextAccessor>()?.HttpContext.User…(ด้วย?) เพราะมันจะผิดพลาดเป็นอย่างอื่น (GetService ส่งคืนค่า null)
ygoe

คุณสามารถทำได้services.AddTransient(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);ในฐานะ HttpContext ผู้ใช้คือ ClaimsPrincipal
Jay Zelos

18

ดูเหมือนว่า ณ ตอนนี้ (เมษายน 2017) ผลงานดังต่อไปนี้:

public string LoggedInUser => User.Identity.Name;

อย่างน้อยในขณะที่อยู่ใน Controller


4
คุณไม่สามารถแปลงประเภท 'System.Security.Principal.IIdentity' เป็น 'string' โดยปริยายได้
Anthony Huang

3
สตริง LoggedInUser = User.Identity.Name;
Alic W

5
เนื่องจากคนที่ไม่เคยเห็น=>โอเปอเรเตอร์ใช้แบบนี้มาก่อนจึงเรียกว่า "Expression Body Definition" และมีอธิบายไว้ในเอกสารนี้ เผื่อว่าคนอย่างฉันจะสงสัยในอนาคต
Nathan Clement

รหัสของคุณไม่อาจรวบรวมก่อนที่จะมีการแก้ไขที่ได้รับว่ามีการแปลงจากไม่มีIIdentityที่จะstringเป็นยังระบุในความคิดเห็นด้านบน การแก้ไขแก้ไขได้ง่ายๆ ฉันไม่แน่ใจว่าคุณบรรลุข้อสรุปได้อย่างไร (โดยเฉพาะอย่างยิ่งเนื่องจากคะแนน "บรรณาธิการ" มอบให้กับผู้ใช้ที่มีชื่อเสียงต่ำกว่า 2k เท่านั้น)
fuglede

9

บางทีฉันอาจไม่เห็นคำตอบ แต่นี่คือวิธีที่ฉันทำ

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

คุณต้องมีการเปลี่ยนแปลงค่าเหล่านี้

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

Startup.cs -> ConfigureServices (... )

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

MVC หรือ Web Api Controller

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

วิธีการควบคุม:

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

ผลลัพธ์คือ userName เช่น = Domain \ username


4

ปัญหาของฉันคือการเข้าถึง User ที่ล็อกอินเป็นอ็อบเจ็กต์ในไฟล์ cshtml เมื่อพิจารณาว่าคุณต้องการผู้ใช้ใน ViewData แนวทางนี้อาจเป็นประโยชน์:

ในไฟล์ 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>

ความคิดใด ๆ ที่คุณจะได้รับคุณสมบัติการนำทางเพื่อโหลด (ชื่อ บริษัท ของคุณสมบัติการนำทางของ บริษัท ในคลาส ApplicationUser ของฉัน) ไม่เห็นวิธีรวมคุณสมบัติการนำทาง
Hunter Nelson

1

นอกเหนือจากคำตอบที่มีอยู่แล้วฉันต้องการเพิ่มว่าคุณยังสามารถมีอินสแตนซ์คลาสได้ทั่วทั้งแอปซึ่งเก็บข้อมูลที่เกี่ยวข้องกับผู้ใช้เช่นUserIDอื่น ๆ

อาจเป็นประโยชน์สำหรับการปรับโครงสร้างใหม่ เช่น คุณไม่ต้องการดึงข้อมูลUserIDในทุกการกระทำของคอนโทรลเลอร์และประกาศUserIDพารามิเตอร์เพิ่มเติมในทุกวิธีที่เกี่ยวข้องกับ Service Layer

ฉันได้ทำวิจัยและนี่คือการโพสต์ของฉัน

คุณเพียงแค่ขยายคลาสของคุณที่ได้มาDbContextโดยการเพิ่มUserIdคุณสมบัติ (หรือใช้Sessionคลาสแบบกำหนดเองที่มีคุณสมบัตินี้)

ในระดับตัวกรองคุณสามารถดึงอินสแตนซ์ของชั้นเรียนและตั้งUserIdค่าได้

หลังจากนั้นทุกที่ที่คุณฉีดอินสแตนซ์ - จะมีข้อมูลที่จำเป็น (อายุการใช้งานต้องเป็นไปตามคำขอดังนั้นคุณจึงลงทะเบียนโดยใช้AddScopedวิธีการ)

ตัวอย่างการทำงาน:

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();
    }
}

สำหรับข้อมูลเพิ่มเติมโปรดดูคำตอบของฉัน


0

การถ่ายIdentityUserก็ใช้ได้เช่นกัน นี่คืออ็อบเจ็กต์ผู้ใช้ปัจจุบันและสามารถดึงค่าทั้งหมดของผู้ใช้ได้

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

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

0

หากคุณใช้ scafolded Identityและใช้ Asp.net Core 2.2+ คุณสามารถเข้าถึงผู้ใช้ปัจจุบันจากมุมมองเช่นนี้:

@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


0

นี่เป็นคำถามเก่า แต่กรณีของฉันแสดงให้เห็นว่ากรณีของฉันไม่ได้กล่าวถึงที่นี่

ฉันชอบคำตอบของ Simon_Weaver มากที่สุด ( https://stackoverflow.com/a/54411397/2903893 ) เขาอธิบายรายละเอียดวิธีรับชื่อผู้ใช้โดยใช้ IPrincipal และ IIdentity คำตอบนี้ถูกต้องอย่างยิ่งและขอแนะนำให้ใช้แนวทางนี้ อย่างไรก็ตามในระหว่างการแก้จุดบกพร่องที่ผมพบกับปัญหาที่เกิดขึ้นเมื่อไม่สามารถหลักการบริการเติม ASP.NET ถูกต้อง (หรือกล่าวอีกนัยหนึ่งคือ IPrincipal.Identity.Name เป็นโมฆะ)

เห็นได้ชัดว่าการรับชื่อผู้ใช้ MVC framework ควรนำมาจากที่ไหนสักแห่ง ในโลก. NET ASP.NET หรือ ASP.NET Core ใช้มิดเดิลแวร์ Open ID Connect ในสถานการณ์ง่ายๆเว็บแอปตรวจสอบสิทธิ์ผู้ใช้ในเว็บเบราว์เซอร์ ในสถานการณ์นี้เว็บแอปพลิเคชันจะสั่งให้เบราว์เซอร์ของผู้ใช้ลงชื่อเข้าใช้ Azure AD Azure AD ส่งคืนการตอบกลับการลงชื่อเข้าใช้ผ่านเบราว์เซอร์ของผู้ใช้ซึ่งมีการอ้างสิทธิ์เกี่ยวกับผู้ใช้ในโทเค็นความปลอดภัย เพื่อให้ใช้งานได้ในโค้ดสำหรับแอปพลิเคชันของคุณคุณจะต้องให้สิทธิ์ในการลงชื่อเข้าใช้เว็บแอปของคุณ เมื่อคุณปรับใช้เว็บแอปของคุณกับ Azure Service สถานการณ์ทั่วไปที่ตรงตามข้อกำหนดนี้คือการกำหนดค่าเว็บแอป: "App Services" -> YourApp -> ใบมีด "Authentication / Authorization" -> "App Service Authenticatio" = "On"https://github.com/Huachao/azure-content/blob/master/articles/app-service-api/app-service-api-authentication.md ) ฉันเชื่อว่า (นี่คือการคาดเดาที่มีการศึกษาของฉัน) ว่าภายใต้กระบวนการนี้ตัวช่วยสร้างจะปรับการกำหนดค่าเว็บ "หลัก" ของเว็บแอปนี้โดยเพิ่มการตั้งค่าเดียวกับที่ฉันแสดงในย่อหน้าต่อไปนี้ โดยทั่วไปแล้วปัญหาที่วิธีนี้ใช้ไม่ได้ใน ASP.NET Core เป็นเพราะการกำหนดค่าเครื่อง "parent" ถูกละเว้นโดย webconfig (ไม่แน่ใจ 100% ฉันแค่ให้คำอธิบายที่ดีที่สุดที่ฉันมี) ดังนั้นเพื่อช่วยในการทำงานคุณต้องตั้งค่าด้วยตนเองในแอปของคุณ

นี่คือบทความที่อธิบายวิธีตั้งค่าแอปของคุณให้ใช้ Azure AD เป็นจำนวนมาก https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2

ขั้นตอนที่ 1: ลงทะเบียนตัวอย่างกับผู้เช่า Azure AD ของคุณ (เห็นได้ชัดว่าไม่ต้องการใช้เวลาอธิบาย)

ขั้นตอนที่ 2: ในไฟล์ appsettings.json: แทนที่ค่า ClientID ด้วย Application ID จากแอปพลิเคชันที่คุณลงทะเบียนในพอร์ทัล Application Registration ในขั้นตอนที่ 1 แทนที่ค่า TenantId ด้วย common

ขั้นตอนที่ 3: เปิดไฟล์ Startup.cs และในวิธี ConfigureServices หลังจากบรรทัดที่มี .AddAzureAD ใส่รหัสต่อไปนี้ซึ่งช่วยให้แอปพลิเคชันของคุณสามารถลงชื่อเข้าใช้ผู้ใช้ด้วยปลายทาง Azure AD v2.0 ซึ่งเป็นทั้ง Work and School และ บัญชี Microsoft Personal

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

สรุป : ฉันได้แสดงปัญหาที่เป็นไปได้อีกอย่างหนึ่งซึ่งอาจนำไปสู่ข้อผิดพลาดที่มีการอธิบายหัวข้อเริ่มต้น สาเหตุของปัญหานี้ไม่มีการกำหนดค่าสำหรับ Azure AD (มิดเดิลแวร์ Open ID) ในการแก้ปัญหานี้ฉันขอเสนอการตั้งค่า "การตรวจสอบสิทธิ์ / การอนุญาต" ด้วยตนเอง มีการเพิ่มภาพรวมสั้น ๆ ของวิธีการตั้งค่านี้


0

คำตอบส่วนใหญ่แสดงวิธีจัดการที่ดีที่สุดHttpContextจากเอกสารซึ่งเป็นสิ่งที่ฉันไปด้วย

Enable Anonymous Authentication = trueฉันไม่ต้องการที่จะพูดถึงว่าคุณจะต้องการตรวจสอบว่าคุณตั้งค่าโครงการเมื่อการแก้จุดบกพร่องเริ่มต้นคือ


-1

ฉันมีทางออกแล้ว

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;
    }
}

1
แม้ว่ารหัสนี้อาจตอบคำถาม แต่การให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและเหตุผลในการแก้ปัญหาจะช่วยเพิ่มมูลค่าในระยะยาวของคำตอบ
Alexander
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.