using Microsoft.AspNetCore.Authentication;
namespace Dpz.Core.Auth.Controllers;
public class ShellController(
IUserPasskeySessionService passkeySessionService,
IAccountSessionService accountSessionService
) : Controller
{
[HttpGet("~/sign-in")]
public IActionResult SignIn(string? returnUrl = null, string? msg = null)
{
if (User.Identity?.IsAuthenticated == true)
{
if (!string.IsNullOrWhiteSpace(returnUrl) && Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
return this.ShellView(
new AuthShellViewModel<EmptyAuthShellPayload>
{
EntryName = "public",
PageName = "sign-in",
ReturnUrl = returnUrl,
Message = msg,
}
);
}
[Authorize, Route("~/sign-out")]
public async Task<IActionResult> Logout(string? returnUrl)
{
var account = User.NameIdentifier;
var sessionId = User.SessionId;
if (!string.IsNullOrWhiteSpace(account) && !string.IsNullOrWhiteSpace(sessionId))
{
await accountSessionService.RevokeAsync(account, sessionId);
}
passkeySessionService.ClearCurrentCredentialId();
await HttpContext.SignOutAsync();
return string.IsNullOrWhiteSpace(returnUrl) || !Url.IsLocalUrl(returnUrl)
? Redirect("/")
: Redirect(returnUrl);
}
[Authorize, HttpGet, Route("change-password.html")]
public IActionResult ChangePassword()
{
return UserShell("change-password");
}
[Authorize, HttpGet, Route("passkeys.html")]
public IActionResult Passkeys()
{
return UserShell("passkeys");
}
[Authorize, HttpGet, Route("security-onboarding.html")]
public IActionResult SecurityOnboarding(string? returnUrl = null)
{
returnUrl =
string.IsNullOrWhiteSpace(returnUrl) || !Url.IsLocalUrl(returnUrl)
? Url.Action("Index", "Home") ?? "/"
: returnUrl;
return UserShell("security-onboarding", returnUrl);
}
[HttpGet("auth-status/{reason}")]
public IActionResult AuthStatus(string reason)
{
return this.ShellView(
new AuthShellViewModel<EmptyAuthShellPayload>
{
EntryName = "public",
PageName = "auth-status",
Message = reason,
}
);
}
private IActionResult UserShell(string pageName, string? returnUrl = null)
{
return this.ShellView(
new AuthShellViewModel<EmptyAuthShellPayload>
{
EntryName = "identity",
PageName = pageName,
ReturnUrl = returnUrl,
User = new VmUserInfo
{
Id = User.NameIdentifier ?? "",
Name = User.Identity?.Name ?? "",
Avatar = "",
Sex = Sex.Other,
Permissions = null,
Enable = true,
Key = "",
},
}
);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码说明
这是一个 ASP.NET Core MVC 控制器,名为 ShellController,主要负责处理用户身份认证相关的页面路由和逻辑。
主要功能模块
1. 构造函数依赖注入
public class ShellController(
IUserPasskeySessionService passkeySessionService,
IAccountSessionService accountSessionService
) : Controller
使用了 C# 12 的主构造函数语法,注入两个服务:
IUserPasskeySessionService: 管理 Passkey(无密码认证)会话IAccountSessionService: 管理用户账户会话
2. 登录页面 (SignIn)
[HttpGet("~/sign-in")]
public IActionResult SignIn(string? returnUrl = null, string? msg = null)
- 检查用户是否已认证
- 已认证用户:重定向到
returnUrl或首页 - 未认证用户:返回登录页面视图,传递参数包括入口名称、页面名称、返回URL和消息
3. 登出功能 (Logout)
[Authorize, Route("~/sign-out")]
public async Task<IActionResult> Logout(string? returnUrl)
- 需要授权访问(
[Authorize]) - 执行操作:
- 撤销当前账户的会话(通过
accountSessionService) - 清除 Passkey 凭证ID
- 执行 ASP.NET Core 的登出流程
- 重定向到指定URL或根路径
- 撤销当前账户的会话(通过
4. 用户设置页面
修改密码
[Authorize, HttpGet, Route("change-password.html")]
public IActionResult ChangePassword()
Passkeys 管理
[Authorize, HttpGet, Route("passkeys.html")]
public IActionResult Passkeys()
安全向导
[Authorize, HttpGet, Route("security-onboarding.html")]
public IActionResult SecurityOnboarding(string? returnUrl = null)
- 提供安全设置引导页面
- 验证
returnUrl的安全性(必须是本地URL)
5. 认证状态页面 (AuthStatus)
[HttpGet("auth-status/{reason}")]
public IActionResult AuthStatus(string reason)
显示认证状态信息,接受一个 reason 参数说明状态原因。
6. 辅助方法 (UserShell)
private IActionResult UserShell(string pageName, string? returnUrl = null)
- 私有辅助方法,用于生成用户相关页面的统一视图
- 构建包含用户信息的视图模型:
- 用户ID、名称
- 头像、性别
- 权限、启用状态等
安全特性
- URL 验证: 多处使用
Url.IsLocalUrl()防止开放重定向攻击 - 授权控制: 敏感操作使用
[Authorize]特性保护 - 会话管理: 登出时主动撤销服务端会话
- Passkey 支持: 集成了现代无密码认证方案
架构模式
- 使用依赖注入实现松耦合
- 统一的视图模型 (
AuthShellViewModel) 处理不同页面 - 遵循 RESTful 路由约定
- 清晰的职责分离(控制器仅负责路由和视图渲染)
AI 正在分析代码…
评论加载中...