using OpenIddict.Client.AspNetCore;

namespace Dpz.Core.Web.Controllers;

public class AccountController(
    IApplicationSignInManager signInManager,
    IAccountService accountService,
    IConfiguration configuration
) : Controller
{
    [HttpPost]
    public async Task<IActionResult> LogOut()
    {
        await HttpContext.SignOutAsync(Program.AuthorizeCookieName);

        // return SignOut(
        //    new AuthenticationProperties { RedirectUri = "/" },
        //    OpenIddictClientAspNetCoreDefaults.AuthenticationScheme
        // );
        return Redirect("/");
    }

    /// <summary>
    /// 认证中心回调
    /// </summary>
    /// <returns></returns>
    [Route("signin-oidc"), HttpGet]
    public async Task<IActionResult> SignIn()
    {
        var result = await HttpContext.AuthenticateAsync(
            OpenIddictClientAspNetCoreDefaults.AuthenticationScheme
        );

        if (!result.Succeeded || !result.Principal.Authenticated)
        {
            return Forbid(Program.AuthorizeCookieName);
        }

        // var principal = result.Principal;

        var sub = result.Principal.FindFirst("sub")?.Value ?? throw new Exception("sub is null");
        var userInfo = await accountService.GetOneUserAsync(sub);
        if (userInfo == null)
        {
            return Forbid(Program.AuthorizeCookieName);
        }

        await signInManager.SignInAsync(
            userInfo,
            new AuthenticationProperties { IsPersistent = true }
        );
        // 跳转回原页面
        var returnUrl = result.Properties?.RedirectUri ?? "/";
        return Redirect(returnUrl);
    }

    [CheckAuthorize]
    public IActionResult Index()
    {
        var memberHost = configuration["Server:Issuer"];
        return Redirect($"{memberHost}/user");
    }

    [Route("re-login.html")]
    public async Task<IActionResult> ReLogin()
    {
        await HttpContext.SignOutAsync(Program.AuthorizeCookieName);
        return View();
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个基于 ASP.NET Core 的账户控制器(AccountController),集成了 OpenIddict 进行身份认证。以下是详细解释:


📌 类定义与依赖注入

public class AccountController(
    IApplicationSignInManager signInManager,
    IAccountService accountService,
    IConfiguration configuration
) : Controller
  • 使用 主构造函数(C# 12 新特性)注入三个依赖:
    • IApplicationSignInManager:管理用户登录状态
    • IAccountService:获取用户信息的业务服务
    • IConfiguration:读取应用配置(如认证服务器地址)

🔹 方法 1:LogOut(登出)

[HttpPost]
public async Task<IActionResult> LogOut()

功能:

  • 清除本地 Cookie 认证状态
  • 使用自定义的 Program.AuthorizeCookieName 退出登录
  • 重定向到首页 /

注释代码:

  • 被注释的代码是通过 OpenIddict 客户端 退出,会同时清除认证中心的会话
  • 当前实现仅清除本地 Cookie

🔹 方法 2:SignIn(OIDC 回调处理)

[Route("signin-oidc"), HttpGet]
public async Task<IActionResult> SignIn()

功能:

这是 OAuth2/OIDC 认证流程的回调端点,处理认证中心返回的认证结果。

流程:

  1. 认证结果验证
var result = await HttpContext.AuthenticateAsync(
    OpenIddictClientAspNetCoreDefaults.AuthenticationScheme
);
  • 从 OpenIddict 客户端方案中获取认证结果
  1. 失败处理
if (!result.Succeeded || !result.Principal.Authenticated)
{
    return Forbid(Program.AuthorizeCookieName);
}
  • 认证失败或未通过则返回 403 禁止访问
  1. 提取用户标识
var sub = result.Principal.FindFirst("sub")?.Value ?? throw new Exception("sub is null");
  • 从 Claims 中获取 sub(Subject,用户唯一标识符)
  • 这是 OIDC 标准中的用户 ID
  1. 获取用户信息
var userInfo = await accountService.GetOneUserAsync(sub);
if (userInfo == null)
{
    return Forbid(Program.AuthorizeCookieName);
}
  • 根据 sub 从业务系统中查询用户详细信息
  • 用户不存在则拒绝访问
  1. 建立本地会话
await signInManager.SignInAsync(
    userInfo,
    new AuthenticationProperties { IsPersistent = true }
);
  • 使用自定义的 SignInManager 创建本地 Cookie 会话
  • IsPersistent = true 表示创建持久化 Cookie("记住我"功能)
  1. 跳转回原页面
var returnUrl = result.Properties?.RedirectUri ?? "/";
return Redirect(returnUrl);
  • 从认证属性中获取原始请求的 URL
  • 默认跳转到首页

🔹 方法 3:Index(用户中心重定向)

[CheckAuthorize]
public IActionResult Index()
{
    var memberHost = configuration["Server:Issuer"];
    return Redirect($"{memberHost}/user");
}

功能:

  • 需要授权才能访问([CheckAuthorize] 自定义特性)
  • 从配置中读取认证服务器地址(Server:Issuer
  • 重定向到认证中心的用户管理页面

🔹 方法 4:ReLogin(重新登录)

[Route("re-login.html")]
public async Task<IActionResult> ReLogin()
{
    await HttpContext.SignOutAsync(Program.AuthorizeCookieName);
    return View();
}

功能:

  • 清除本地 Cookie
  • 返回一个视图(可能包含重新登录的引导页面)

🏗️ 架构特点

  1. 分离式认证

    • 使用外部 OIDC 认证中心(通过 OpenIddict Client)
    • 本地维护会话(通过自定义 Cookie)
  2. 双重身份验证

    • OIDC 认证 → 本地用户查询 → 创建本地会话
  3. 灵活的注销策略

    • 可选择仅本地注销或联动认证中心注销

🔐 安全要点

  • ✅ 验证 sub 不为空
  • ✅ 验证用户在本地系统存在
  • ✅ 使用持久化 Cookie 提升用户体验
  • ⚠️ throw new Exception 应替换为更友好的错误处理

这是一个典型的 OIDC 客户端实现,适用于微服务架构中的 Web 应用统一认证场景。

评论加载中...