相同用户不同角色的多次登录

huangapple go评论51阅读模式
英文:

Multiple logins from same user with different roles

问题

以下是您要翻译的代码部分:

My Blazor Server app uses authentication. We have two roles: Attendee and Supervisor.

To handle the authentication, I use this:

    public class CustomAuthenticationStateProvider : AuthenticationStateProvider
    {
    
    	private readonly ProtectedSessionStorage _browserStorage;
    	private readonly ClaimsPrincipal _anonymous = new(new ClaimsIdentity());
    
    	public CustomAuthenticationStateProvider(ProtectedSessionStorage protectedBrowserStorage)
    	{
    		_browserStorage = protectedBrowserStorage;
    	}
    
    	public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    	{
    		try
    		{
    			var userSessionStorageResult = await _browserStorage.GetAsync<UserSession>("UserSession");
    			var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
    
    			if (userSession == null)
    			{
    				return await Task.FromResult(new AuthenticationState(_anonymous));
    			}
    
    			var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
    			{
    				new(ClaimTypes.GivenName, userSession.FirstName!),
    				new(ClaimTypes.Name, userSession.FullName!),
    				new(ClaimTypes.Email, userSession.Email!),
    				new(ClaimTypes.Role, userSession.UserType!),
    			}, "CustomAuth"));
    
    			return await Task.FromResult(new AuthenticationState(claimsPrincipal));
    		}
    		catch
    		{
    			return await Task.FromResult(new AuthenticationState(_anonymous));
    		}
    	}
    
    	public async Task UpdateAuthenticationState(UserSession? userSession)
    	{
    		ClaimsPrincipal claimsPrincipal;
    
    		if (userSession != null)
    		{
    			await _browserStorage.SetAsync("UserSession", userSession);
    
    			claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
    			{
    				new(ClaimTypes.GivenName, userSession.FirstName!),
    				new(ClaimTypes.Name, userSession.FullName!),
    				new(ClaimTypes.Email, userSession.Email!),
    				new(ClaimTypes.Role, userSession.UserRole!)
    			}));
    		}
    		else
    		{
    			await _browserStorage.DeleteAsync("UserSession");
    			claimsPrincipal = _anonymous;
    		}
    
    		NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
    	}
    }

Startup.cs:

    builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

That works as expected, but what if I want a user to be logged in with both roles at a time?
Often the supervisors have separate Attendee-accounts for test purposes, but when they're at the same time logged out as Supervisor (because the UserSession is overwritten).

I have tried to fix this by adding the role name to the UserSession key like this:

     	public async Task UpdateAuthenticationState(UserSession? userSession, string? userRole)
    	{
    		ClaimsPrincipal claimsPrincipal;
    
    		if (userSession != null)
    		{
    			await _browserStorage.SetAsync("UserSession" + userRole, userSession);
    
    			claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
    			{
    				new(ClaimTypes.GivenName, userSession.FirstName!),
    				new(ClaimTypes.Name, userSession.FullName!),
    				new(ClaimTypes.Email, userSession.Email!),
    				new(ClaimTypes.Role, userSession.UserRole!)
    			}));
    		}
    		else
    		{
    			await _browserStorage.DeleteAsync("UserSession" + userRole);
    			claimsPrincipal = _anonymous;
    		}
    
    		NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
    	}
    }

I believe that could work, but how should I change `GetAuthenticationStateAsync()` as it is looking for `UserSession`?

希望这些翻译对您有所帮助。如果您有任何其他问题或需要进一步的翻译,请随时告诉我。

英文:

My Blazor Server app uses authentication. We have two roles: Attendee and Supervisor.

To handle the authentication, I use this:

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly ProtectedSessionStorage _browserStorage;
private readonly ClaimsPrincipal _anonymous = new(new ClaimsIdentity());
public CustomAuthenticationStateProvider(ProtectedSessionStorage protectedBrowserStorage)
{
_browserStorage = protectedBrowserStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
try
{
var userSessionStorageResult = await _browserStorage.GetAsync<UserSession>("UserSession");
var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;
if (userSession == null)
{
return await Task.FromResult(new AuthenticationState(_anonymous));
}
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new(ClaimTypes.GivenName, userSession.FirstName!),
new(ClaimTypes.Name, userSession.FullName!),
new(ClaimTypes.Email, userSession.Email!),
new(ClaimTypes.Role, userSession.UserType!),
}, "CustomAuth"));
return await Task.FromResult(new AuthenticationState(claimsPrincipal));
}
catch
{
return await Task.FromResult(new AuthenticationState(_anonymous));
}
}
public async Task UpdateAuthenticationState(UserSession? userSession)
{
ClaimsPrincipal claimsPrincipal;
if (userSession != null)
{
await _browserStorage.SetAsync("UserSession", userSession);
claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new(ClaimTypes.GivenName, userSession.FirstName!),
new(ClaimTypes.Name, userSession.FullName!),
new(ClaimTypes.Email, userSession.Email!),
new(ClaimTypes.Role, userSession.UserRole!)
}));
}
else
{
await _browserStorage.DeleteAsync("UserSession");
claimsPrincipal = _anonymous;
}
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
}
}

Startup.cs:

builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

That works as expected, but what if I want a user to be logged in with both roles at a time?
Often the supervisors have separate Attendee-accounts for test purposes, but when they are logged in as Supervisor and then log in as Attendee, they're at the same time logged out as Supervisor (because the UserSession is overwritten).

I have tried to fix this by adding the role name to the UserSession key like this:

 	public async Task UpdateAuthenticationState(UserSession? userSession, string? userRole)
{
ClaimsPrincipal claimsPrincipal;
if (userSession != null)
{
await _browserStorage.SetAsync("UserSession" + userRole, userSession);
claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new(ClaimTypes.GivenName, userSession.FirstName!),
new(ClaimTypes.Name, userSession.FullName!),
new(ClaimTypes.Email, userSession.Email!),
new(ClaimTypes.Role, userSession.UserRole!)
}));
}
else
{
await _browserStorage.DeleteAsync("UserSession" + userRole);
claimsPrincipal = _anonymous;
}
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
}
}

I believe that could work, but how should I change GetAuthenticationStateAsync() as it is looking for UserSession?

答案1

得分: 0

你可能也需要修改GetAuthenticationStateAsync来检查会话中的两个角色,并将它们的声明合并到单个ClaimsPrincipal中。尝试这样做:

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    try
    {
        var attendeeSessionResult = await _browserStorage.GetAsync<UserSession>("UserSessionAttendee");
        var supervisorSessionResult = await _browserStorage.GetAsync<UserSession>("UserSessionSupervisor");

        var attendeeSession = attendeeSessionResult.Success ? attendeeSessionResult.Value : null;
        var supervisorSession = supervisorSessionResult.Success ? supervisorSessionResult.Value : null;

        if (attendeeSession == null && supervisorSession == null)
        {
            return await Task.FromResult(new AuthenticationState(_anonymous));
        }

        var claims = new List<Claim>();

        if (attendeeSession != null)
        {
            claims.AddRange(new[]
            {
                new Claim(ClaimTypes.GivenName, attendeeSession.FirstName!),
                new Claim(ClaimTypes.Name, attendeeSession.FullName!),
                new Claim(ClaimTypes.Email, attendeeSession.Email!),
                new Claim(ClaimTypes.Role, attendeeSession.UserRole!),
            });
        }

        if (supervisorSession != null)
        {
            claims.AddRange(new[]
            {
                new Claim(ClaimTypes.GivenName, supervisorSession.FirstName!),
                new Claim(ClaimTypes.Name, supervisorSession.FullName!),
                new Claim(ClaimTypes.Email, supervisorSession.Email!),
                new Claim(ClaimTypes.Role, supervisorSession.UserRole!),
            });
        }

        var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "CustomAuth"));
        return await Task.FromResult(new AuthenticationState(claimsPrincipal));
    }
    catch
    {
        return await Task.FromResult(new AuthenticationState(_anonymous));
    }
}

编辑部分:

也许你可以这样修改:

if (attendeeSession != null)
{
    claims.AddRange(new[]
    {
        new Claim(ClaimTypes.GivenName, attendeeSession.FirstName!),
        new Claim(ClaimTypes.Name, attendeeSession.FullName!),
        new Claim("AttendeeEmail", attendeeSession.Email!),
        new Claim(ClaimTypes.Role, attendeeSession.UserRole!),
    });
}

if (supervisorSession != null)
{
    claims.AddRange(new[]
    {
        new Claim(ClaimTypes.GivenName, supervisorSession.FirstName!),
        new Claim(ClaimTypes.Name, supervisorSession.FullName!),
        new Claim("SupervisorEmail", supervisorSession.Email!),
        new Claim(ClaimTypes.Role, supervisorSession.UserRole!),
    });
}

然后:

var userEmail = User.Claims.FirstOrDefault(c => c.Type == "AttendeeEmail")?.Value;

通过为参与者和主管的电子邮件地址使用不同的声明类型,你可以存储和检索它们的数据,而不会互相覆盖或混淆。

英文:

You probably also need to modify the GetAuthenticationStateAsync to check for both roles in session and merge claims from both of them into singe ClaimsPrincipal ? Try this:

public override async Task&lt;AuthenticationState&gt; GetAuthenticationStateAsync()
{
try
{
var attendeeSessionResult = await _browserStorage.GetAsync&lt;UserSession&gt;(&quot;UserSessionAttendee&quot;);
var supervisorSessionResult = await _browserStorage.GetAsync&lt;UserSession&gt;(&quot;UserSessionSupervisor&quot;);
var attendeeSession = attendeeSessionResult.Success ? attendeeSessionResult.Value : null;
var supervisorSession = supervisorSessionResult.Success ? supervisorSessionResult.Value : null;
if (attendeeSession == null &amp;&amp; supervisorSession == null)
{
return await Task.FromResult(new AuthenticationState(_anonymous));
}
var claims = new List&lt;Claim&gt;();
if (attendeeSession != null)
{
claims.AddRange(new[]
{
new Claim(ClaimTypes.GivenName, attendeeSession.FirstName!),
new Claim(ClaimTypes.Name, attendeeSession.FullName!),
new Claim(ClaimTypes.Email, attendeeSession.Email!),
new Claim(ClaimTypes.Role, attendeeSession.UserRole!),
});
}
if (supervisorSession != null)
{
claims.AddRange(new[]
{
new Claim(ClaimTypes.GivenName, supervisorSession.FirstName!),
new Claim(ClaimTypes.Name, supervisorSession.FullName!),
new Claim(ClaimTypes.Email, supervisorSession.Email!),
new Claim(ClaimTypes.Role, supervisorSession.UserRole!),
});
}
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, &quot;CustomAuth&quot;));
return await Task.FromResult(new AuthenticationState(claimsPrincipal));
}
catch
{
return await Task.FromResult(new AuthenticationState(_anonymous));
}
}

EDIT: ===============

You could modify it this way maybe?:

if (attendeeSession != null)
{
claims.AddRange(new[]
{
new Claim(ClaimTypes.GivenName, attendeeSession.FirstName!),
new Claim(ClaimTypes.Name, attendeeSession.FullName!),
new Claim(&quot;AttendeeEmail&quot;, attendeeSession.Email!),
new Claim(ClaimTypes.Role, attendeeSession.UserRole!),
});
}
if (supervisorSession != null)
{
claims.AddRange(new[]
{
new Claim(ClaimTypes.GivenName, supervisorSession.FirstName!),
new Claim(ClaimTypes.Name, supervisorSession.FullName!),
new Claim(&quot;SupervisorEmail&quot;, supervisorSession.Email!),
new Claim(ClaimTypes.Role, supervisorSession.UserRole!),
});
}

and then:

var userEmail = User.Claims.FirstOrDefault(c =&gt; c.Type == &quot;AttendeeEmail&quot;)?.Value;

By using separate claim types for Attendee and Supervisor email addresses, you can store and retrieve their respective data without overwriting or mixing them up.

答案2

得分: 0

代码部分不要翻译。以下是翻译好的内容:

"Your original question:

> but what if I want a user to be logged in with both roles at a time?

The User provided by the AuthenticationState is a ClaimsPrincipal object.

A ClaimsPrincipal can have multiple ClaimsIdentity objects. You should be able to use this to add identities with different roles.

Here's a very simple custom AuthenticationStateProvider that demonstrates how to add multiple identities with different roles to a ClaimsPrincipal.

This user will be able to access pages with @attribute [Authorize(Roles = &quot;User&quot;)] and @attribute [Authorize(Roles = &quot;Admin&quot;)] set."

英文:

Your original question:

> but what if I want a user to be logged in with both roles at a time?

The User provided by the AuthenticationState is a ClaimsPrincipal object.

A ClaimsPrincipal can have multiple ClaimsIdentity objects. You should be able to use this to add identities with different roles.

Here's a very sinmple custom AuthenticationStateProvider that demonstrates how to add multiple identities with different roles to a ClaimsPrincipal.

public class CustomAuthenticationStateProvider : ServerAuthenticationStateProvider
{
    public override async Task&lt;AuthenticationState&gt; GetAuthenticationStateAsync()
    {
        var authState = await base.GetAuthenticationStateAsync();
        var user = authState.User;
        user.AddIdentity(new ClaimsIdentity(new List&lt;Claim&gt;() { new Claim(ClaimTypes.Role, &quot;Admin&quot;) }));
        user.AddIdentity(new ClaimsIdentity(new List&lt;Claim&gt;() { new Claim(ClaimTypes.Role, &quot;User&quot;) }));

        return await Task.FromResult(new AuthenticationState(user));
    }
}

This user will be able to access pages with @attribute [Authorize(Roles = &quot;User&quot;)] and @attribute [Authorize(Roles = &quot;Admin&quot;)] set.

huangapple
  • 本文由 发表于 2023年3月15日 19:58:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75744398.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定