PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin
In the previous articles, we have gradually built a complete PicoServer local Web Admin system, including:
- Routing mechanism
- REST API architecture
- Static file hosting
- Web Admin management backend
Up to this point, we already have the following architecture:
Web UI
↓
REST API
↓
Business Service
However, for any real-world system, there is still one most critical component missing:
Authentication and Authorization System (Auth System)
Without a permission system:
- Any user can access the management backend
- All APIs can be called directly
- The entire system has no security boundaries
Therefore, in this article, we will design a:
complete authentication and authorization architecture suitable for PicoServer
And implement the following features:
- Login authentication
- Token mechanism
- API permission protection
- Role-based permission model
- API filter
- Permission attribute mechanism
Ultimately, we will build a scalable permission system framework.
I. Permission System Architecture Design
In web systems, a permission system usually consists of three layers:
- Authentication Identity verification
- Authorization Permission control
- Permission Model Permission modeling
The relationship between these layers is as follows:
User Login
↓
Get Token
↓
API Requests Carry Token
↓
Server Validates Token
↓
Check User Permissions
↓
Allow / Deny Access
II. Overall Architecture Design
In PicoServer, we design the following architecture:
Web Admin UI
│
│ fetch API
▼
┌───────────────────┐
│ PicoServer │
│ │
│ Auth Middleware │
│ │ │
│ ▼ │
│ Permission Filter │
│ │ │
│ ▼ │
│ Controller │
│ │ │
│ ▼ │
│ Service Layer │
└───────────────────┘
Core Idea:
All API requests pass through the authentication middleware first.
III. Token Authentication Mechanism
In a local Web Admin system, Token-based authentication is recommended for the following reasons:
- Does not rely on browser sessions
- Suitable for REST APIs
- Simple to call from the frontend
- Low implementation cost
The authentication process is as follows:
User Login
↓
Server Generates Token
↓
Frontend Stores Token
↓
Token is Carried in Each API Request
↓
Server Validates Token
IV. Implement the Login Interface
First, implement the login API:
POST /api/login
Request body:
{
"username":"admin",
"password":"123456"
}
Login Interface Implementation
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 Tool Correction (from Article 3)
The previous version had a bug where the contentType was not set correctly. The corrected version is as follows, with the method name changed from WriteJson to 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");
}
}
V. Token Service Design
To manage tokens, we create a 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 Data Structure:
public class UserSession
{
public string? Username { get; set; }
public string? Role { get; set; }
public DateTime ExpireTime { get; set; }
}
VI. Authentication Middleware (Auth Middleware)
To avoid manually validating tokens in every API, we design a unified authentication middleware.
Request flow:
All APIs
↓
Auth Middleware
↓
Controller
Implementation:
public class AuthMiddleware
{
public static UserSession? Authenticate(HttpListenerRequest request)
{
var token = request.Headers["Authorization"];
if (string.IsNullOrEmpty(token))
return null;
return TokenService.Validate(token);
}
}
VII. API Filter Mechanism
Furthermore, we can add an API Filter with the following functions:
- Automatically intercept unauthorized access
- Return unified error responses
Example implementation:
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;
}
VIII. Protect API Interfaces
Now APIs can be written like this:
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);
}
This way, all APIs are automatically protected.
IX. Permission Model Design
As the system becomes more complex, a role-based permission model needs to be introduced.
Classic permission model hierarchy:
User
↓
Role
↓
Permission
Example:
User
├─ admin
└─ operator
Role
├─ admin
└─ viewer
Permission
├─ product.view
├─ product.edit
└─ order.manage
APIs can be associated with specific permissions:
- /api/product/list → product.view
- /api/product/create → product.edit
- /api/order/delete → order.manage
The server determines access rights based on the user's role.
X. Permission Attribute Design (Advanced Architecture)
A more advanced design is to use permission attributes (Attribute):
Example:
[RequirePermission("product.view")]
public async Task ProductList()
{
}
Attribute Definition:
public class RequirePermissionAttribute : Attribute
{
public string Permission { get; }
public RequirePermissionAttribute(string permission)
{
Permission = permission;
}
}
Before API execution:
- Read the attribute
- Check permissions
- Automatically intercept unauthorized requests
This is the mechanism used in many web frameworks, such as:
- ASP.NET Core
- Spring Boot
- NestJS
XI. Complete Permission System Architecture
The final system architecture is as follows:
MAUI App
│
├── WebView
│
└── PicoServer
│
├── Static File Server
│
├── REST API
│
├── Auth Middleware
│
├── Permission Filter
│
└── Service Layer
Advantages:
- Serverless deployment
- Local operation
- High performance
- Cross-platform compatibility
XII. Additional Code
Due to space limitations, some code is not fully displayed. Please refer to the synchronized source code in the project: https://github.com/densen2014/MauiPicoAdmin
Final Route Registration:
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);
}
Add Test File: api_test.http
Relevant documentation: 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
### Automatically extract response content from the previous request through variables and scripts, and reuse it in the next request
# @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
XIII. Summary of This Article
In this article, we have built a complete PicoServer permission system architecture, implementing the following features:
- Login authentication API
- Token mechanism
- API permission protection
- Auth Middleware
- API Filter
- Role-based permission model
- Permission attribute architecture
At this point, our system has been equipped with:
enterprise-level Web Admin infrastructure
Next Article Preview
Next, we will continue to upgrade the system capabilities with:
Practical Combat of MAUI Embedded Web Architecture (7)
Local Database and Data Persistence
We will implement:
- SQLite database
- ORM data access
- Product CRUD APIs
- Admin data management
- Local data synchronization
Ultimately, we will build:
a fully usable local management system
Top comments (0)