Maui Blazor 中文社区 QQ群:645660665

MAUI 嵌入式 Web 架构实战(六) Web Admin 权限系统与登录认证

MAUI 嵌入式 Web 架构实战(六)

PicoServer Web Admin 权限系统设计与认证架构

源码地址:
https://github.com/densen2014/MauiPicoAdmin

在前几篇文章中,我们已经逐步构建了一个完整的 PicoServer 本地 Web Admin 系统

  • 路由机制
  • REST API 架构
  • 静态文件托管
  • Web Admin 管理后台

至此,我们已经拥有:

Web UI
 ↓
REST API
 ↓
业务服务

但对于任何一个真实系统来说,还缺少一个最关键的部分:

身份认证与权限控制(Auth System)

如果没有权限系统:

  • 任意用户都可以访问管理后台
  • 所有 API 都可以被直接调用
  • 整个系统没有安全边界

因此,本篇我们将设计一套:

适用于 PicoServer 的完整认证与权限架构

并实现:

  • 登录认证
  • Token 机制
  • API 权限保护
  • 角色权限模型
  • API 过滤器
  • 权限注解机制

最终构建出一个 可扩展的权限系统框架


一、权限系统架构设计

在 Web 系统中,权限系统通常包含三个层级:

Authentication
身份认证

Authorization
权限控制

Permission Model
权限模型

其关系如下:

用户登录
   ↓
获取 Token
   ↓
API 请求携带 Token
   ↓
服务器验证 Token
   ↓
检查用户权限
   ↓
允许 / 拒绝访问

二、整体架构设计

在 PicoServer 中,我们设计如下架构:

             Web Admin UI
                │
                │ fetch API
                ▼
          ┌───────────────────┐
          │  PicoServer       │
          │                   │
          │ Auth Middleware   │
          │       │           │
          │       ▼           │
          │ Permission Filter │
          │       │           │
          │       ▼           │
          │ Controller        │
          │       │           │
          │       ▼           │
          │ Service Layer     │
          └───────────────────┘

核心思想:

所有 API 请求先经过认证中间件。


三、Token 认证机制

在本地 Web Admin 系统中,推荐使用 Token 认证

原因:

  • 不依赖浏览器 Session
  • 适合 REST API
  • 前端调用简单
  • 实现成本低

认证流程:

用户登录
   ↓
服务器生成 Token
   ↓
前端保存 Token
   ↓
每次 API 请求携带 Token
   ↓
服务器验证 Token

四、实现登录接口

首先实现登录 API:

POST /api/login

请求:

{
  "username":"admin",
  "password":"123456"
}

登录接口实现


public class LoginController
{
    public async Task Login(HttpListenerRequest request, HttpListenerResponse response)
    {
        var body = await new StreamReader(request.InputStream).ReadToEndAsync();

         var login = JsonSerializer.Deserialize<LoginRequest>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        if (login != null && login.Username == "admin" && login.Password == "123456")
        {
            var token = TokenService.GenerateToken(login.Username);

            await response.WriteJsonAsync(new
            {
                success = true,
                token = token
            });
        }
        else
        {
            response.StatusCode = 401;

            await response.WriteJsonAsync(new
            {
                success = false,
                message = "Invalid username or password"
            });
        }
    }

    public class LoginRequest
    {
        public string? Username { get; set; }
        public string? Password { get; set; }
    }
}

  • 文章第三篇 HttpHelper 工具修正

之前写的有bug, 未能正确设置 contentType , 修正如下, 命名由 WriteJson 改为 WriteJsonAsync

public static class HttpHelper
{
    public static async Task WriteJsonAsync(HttpListenerResponse response, object obj)
    {
        string json = JsonSerializer.Serialize(obj);
        await response.WriteAsync(json, contentType: "application/json");
    }
}

五、Token 服务设计

为了管理 Token,我们创建一个 TokenService:

public class TokenService
{
    static Dictionary<string,UserSession> sessions = new();

    public static string GenerateToken(string username)
    {
        var token = Guid.NewGuid().ToString();

        sessions[token] = new UserSession
        {
            Username = username,
            Role = "admin",
            ExpireTime = DateTime.Now.AddHours(12)
        };

        return token;
    }

    public static UserSession Validate(string token)
    {
        if(!sessions.ContainsKey(token))
            return null;

        var session = sessions[token];

        if(session.ExpireTime < DateTime.Now)
            return null;

        return session;
    }
}

Session 数据结构:

public class UserSession
{
    public string? Username { get; set; }

    public string? Role { get; set; }

    public DateTime ExpireTime { get; set; }
}

六、认证中间件(Auth Middleware)

为了避免在每个 API 手动验证 Token,我们设计一个:

统一认证中间件

所有 API
   ↓
Auth Middleware
   ↓
Controller

实现:

public class AuthMiddleware
{
    public static UserSession? Authenticate(HttpListenerRequest request)
    {
        var token = request.Headers["Authorization"];

        if (string.IsNullOrEmpty(token))
            return null;

        return TokenService.Validate(token);
    }
}

七、API Filter 机制

进一步,我们可以增加一个 API Filter

作用:

  • 自动拦截未授权访问
  • 统一返回错误

示例:

public static async Task<bool> RequireAuth(
    HttpListenerRequest request,
    HttpListenerResponse response)
{
    var session = AuthMiddleware.Authenticate(request);

    if(session == null)
    {
        response.StatusCode = 401;

        await response.WriteJsonAsync(new
        {
            message = "Unauthorized"
        });

        return false;
    }

    return true;
}

八、保护 API 接口

现在 API 可以这样写:

public async Task GetUserList(HttpListenerRequest request, HttpListenerResponse response)
{
    if(!await RequireAuth(request,response))
        return;

    var users = new[]
    {
        new { id = 1, name = "admin" },
        new { id = 2, name = "operator" }
    };

    await response.WriteJsonAsync(users);
}

这样所有 API 都会自动保护。


九、权限模型设计

当系统复杂后,需要引入 角色权限模型

经典模型:

User
 ↓
Role
 ↓
Permission

例如:

User
 ├─ admin
 └─ operator

Role
 ├─ admin
 └─ viewer

Permission
 ├─ product.view
 ├─ product.edit
 └─ order.manage

API 可以定义权限:

/api/product/list
/api/product/create
/api/order/delete

服务器通过角色判断是否允许访问。


十、权限注解设计(高级架构)

更高级的设计是:

权限注解(Attribute)

例如:

[RequirePermission("product.view")]
public async Task ProductList()
{
}

Attribute 定义:

public class RequirePermissionAttribute : Attribute
{
    public string Permission { get; }

    public RequirePermissionAttribute(string permission)
    {
        Permission = permission;
    }
}

在 API 执行前:

  • 读取 Attribute
  • 判断权限
  • 自动拦截

这就是很多 Web 框架使用的机制:

  • ASP.NET Core
  • Spring Boot
  • NestJS

十一、完整权限系统架构

最终系统结构:

MAUI App
   │
   ├── WebView
   │
   └── PicoServer
          │
          ├── Static File Server
          │
          ├── REST API
          │
          ├── Auth Middleware
          │
          ├── Permission Filter
          │
          └── Service Layer

优势:

  • 无服务器部署
  • 本地运行
  • 高性能
  • 跨平台

十二、其他代码

因篇幅有限, 部分代码并未完整贴出, 请参考工程同步源码: https://github.com/densen2014/MauiPicoAdmin

最终 RegisterRoutes

    private void RegisterRoutes()
    {
        var demo = new DemoController();
        var product = new ProductController();
        var login = new LoginController();
        var user = new UserController();
        api.AddStaticFiles("/", wwwrootPath);
        api.AddRoute("/api/hello", demo.Hello);
        api.AddRoute("/api/time", demo.GetTime);
        api.AddRoute("/api/status", demo.GetStatus);
        api.AddRoute("/api/product/list", product.List);
        api.AddRoute("/api/product/detail", product.Detail); 
        api.AddRoute("/api/login", login.Login); 
        api.AddRoute("/api/user/list", user.GetUserList); 
    }

添加测试文件 api_test.http

相关说明 https://aka.ms/vs/httpfile

# For more info on HTTP files go to https://aka.ms/vs/httpfile

@hostname=127.0.0.1

### Hello
GET http://{{hostname}}:8090/api/hello

### Time
GET http://{{hostname}}:8090/api/time

### Status
GET http://{{hostname}}:8090/api/status

### Product List
GET http://{{hostname}}:8090/api/product/list

### Product Detail
GET http://{{hostname}}:8090/api/product/detail?id=1

### Login
### 通过变量和脚本自动提取上一个请求的响应内容,并在下一个请求中复用
# @name login
POST http://{{hostname}}:8090/api/login
Content-Type: application/json

{
  "username": "admin",
  "password": "123456"
} 

### User List (with token)
GET http://{{hostname}}:8090/api/user/list
Authorization: {{login.response.body.$.token}}

# Response



十三、本篇总结

在本篇文章中,我们构建了一套完整的 PicoServer 权限系统架构

实现了:

  • 登录认证 API
  • Token 机制
  • API 权限保护
  • Auth Middleware
  • API Filter
  • 角色权限模型
  • 权限注解架构

至此,我们的系统已经具备:

企业级 Web Admin 基础架构


下一篇预告

接下来我们将继续升级系统能力:

MAUI 嵌入式 Web 架构实战(七)

本地数据库与数据持久化

我们将实现:

  • SQLite 数据库
  • ORM 数据访问
  • 商品 CRUD API
  • Admin 数据管理
  • 本地数据同步

最终实现:

一个完整可用的本地管理系统。

posted @ 2026-03-10 19:06  AlexChow  阅读(572)  评论(0)    收藏  举报