基于OpenIddict6.4.0搭建授权认证服务的web管理端-用户登录

什么是授权认证服务的管理端?

授权认证服务的Web管理端是一个图形化的管理后台,让管理员(而非普通用户)能够集中、可视化的管理和控制整个身份认证与授权系统。它是整个SSO、OAuth 2.0、OpenID Connect系统的“大脑”和“控制台”。

授权认证服务的管理端需要登录吗?如何登录?

绝对需要,而且其登录安全要求是最高级别的。

管理端的登录过程,本身就是其核心能力的一次完美演示。通常流程如下:

  1. 访问管理端地址:管理员在浏览器中输入一个特定的URL(例如 https://sso-admin.your-company.com/Account/Login)。

  2. 重定向到登录页:管理端会立即将管理员重定向到它自己所管理的那个统一的认证服务登录页面。这非常关键——管理端自己就是它服务的第一个“客户”

  3. 管理员身份认证

    • 管理员输入自己的高权限账户(不是普通用户账户)和密码。

    • 系统通常会强制要求进行多因素认证,比如输入手机验证码或使用生物识别,以确保是管理员本人在操作。

  4. 授权与登录

    • 认证通过后,认证服务会颁发一个令牌给管理员浏览器。

    • 浏览器带着这个令牌回到管理端,管理端验证令牌有效后,才允许管理员进入并看到管理界面。

基于OpenIddict的实践

预期部署架构

项目许可证
授权中心服务https://xxx.com/passport
授权中心服务web管理端https://xxx.com/passport/Account/Login

最佳实践

新增AccountController.cs登录控制器

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Passport.Models;
using Passport.Services;
using Microsoft.AspNetCore.Identity;
using Passport.Domain.Models;
using System.Security.Policy;
using Microsoft.IdentityModel.Tokens;
using static OpenIddict.Abstractions.OpenIddictConstants;
using System.Collections.Immutable;

namespace Passport.Controllers
{
    [Route("Account")]
    public class AccountController : Controller
    {
        private readonly ILogger<AccountController> _logger;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;

        public AccountController(ILogger<AccountController> logger, SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
        {
            _logger = logger;
            _signInManager = signInManager;
            _userManager = userManager;
        }

        [HttpGet("login")]
        public IActionResult Login([FromQuery] string returnUrl)
        {
            return View(new LoginViewModel() { ReturnUrl = returnUrl });
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("login")]
        public async Task<IActionResult> Login([FromBody] LoginViewModel model)
        {
            var badRequestResult = new ResultData();
            var error = string.Empty;
            // 1. 使用 SignInManager 进行密码验证
            var result = await _signInManager.PasswordSignInAsync(
                       model.UserName,
                       model.Password,
                       isPersistent: model.RememberMe,
                       lockoutOnFailure: false); // 不启用登录失败锁定
            if (result.Succeeded)
            {
                // 2. 登录成功,获取用户信息
                var user = await _userManager.FindByNameAsync(model.UserName);
                if (user == null)
                {
                    error = "用户不存在";
                    badRequestResult.SetCodeMsg("error", error);
                    return StatusCode(StatusCodes.Status401Unauthorized, badRequestResult);
                }
                if (user.IsEnabled == false)
                {
                    error = "用户已禁用";
                    badRequestResult.SetCodeMsg("error", error);
                    return StatusCode(StatusCodes.Status401Unauthorized, badRequestResult);
                }
                var url = string.IsNullOrEmpty(model.ReturnUrl) || model.ReturnUrl == "/" ? "" : model.ReturnUrl;
                return Ok(new { Success = true, Url = url });
            }
            else if (result.IsLockedOut)
            {
                error = "账户已被锁定,请稍后重试";
            }
            else if (result.RequiresTwoFactor)
            {
                error = "需要双因素认证";
            }
            else
            {
                error = "用户名或密码错误";
            }
            badRequestResult.SetCodeMsg("error", error);
            return StatusCode(StatusCodes.Status401Unauthorized, badRequestResult);
        }
    }
}

新增Login.cshtml

<form id="smsForm">
    <!-- 账号 -->
    <div class="form-item">
        <div class="account-box">
            <input class="input"
                   type="text"
                   placeholder="账号"
                   id="account"
                   required />
        </div>
    </div>
    <div class="form-item">
        <div class="password-wrapper">
            <input class="input"
                   type="password"
                   id="password"
                   name="password"
                   placeholder="密码"
                   required
                   autocomplete="current-password" />
            <span class="toggle-password" id="togglePassword">👁️</span>
        </div>
        <div id="errorMsg" style="margin-top:5px;color: #fa2a2d;font-size: 12px">
        </div>
    </div>
    <!-- 
    <div class="agree">
        <input type="checkbox" id="rememberMe" name="rememberMe" />
        <div for="rememberMe">
            记住我
        </div>
    </div>
    -->
    <!-- 勾选 -->
    <div class="agree">
        <input type="checkbox" id="agree" name="rememberMe" checked />
        <div for="agree">
            记住我
        </div>
    </div>

    <!-- 登录 -->
    <button id="submit-btn" class="submit-btn" type="submit">登 录</button>
</form>

新增首页控制器HomeController.cs(需要登录后才能访问,加入特性[Authorize])

using Passport.API.Controllers;
using Passport.API.Models;
using Passport.Domain;
using Passport.Models;
using Passport.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using System.Diagnostics;
using System.Security.Claims;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace Passport.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger, Session session, IUserService userService)
        {
            _logger = logger;
            _session = session;
        }

        /// <summary>
        /// 首页
        /// </summary>
        /// <returns></returns>
        [Authorize]
        public async Task<IActionResult> Index()
        {
            var userName = User.Identity.Name;
            return View(new HomeViewModel() { UserName=userName });
        }
    }
}

新增首页页面Index.cshtml

@model Passport.API.Models.HomeViewModel
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>授权中心</title>
    <link rel="stylesheet" type="text/css" href="~/css/index.css">
</head>
<body>
    @await Html.PartialAsync("Header")
    <main class="main-content">
        <div class="welcome-section">
            <h2>您好,{Model.UserName},欢迎使用授权中心</h2>
        </div>
    </main>
</body>
</html>

登录后会成功写入浏览器cookie,此时登录就成功了。

总结:

  1. 登录核心方法使用SignInManager提供的方法完成登录并写入cookie,也可以通过await HttpContext.SignInAsync手动实现
  2. 登录后默认cooike保存为会话级别,可以通过isPersistent参数控制cookie保存时长。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值