[feat] Safer client authentication
This commit is contained in:
66
.idea/.idea.Abyss/.idea/workspace.xml
generated
66
.idea/.idea.Abyss/.idea/workspace.xml
generated
@@ -10,13 +10,13 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="bf317275-3039-49bb-a475-725a800a0cce" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/Abyss/Components/Static/ControllerExtensions.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Abyss.csproj" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Abyss.csproj" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/ImageController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/ImageController.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/VideoController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Media/VideoController.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Abyss/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Program.cs" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -40,15 +40,12 @@
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Media/LiveController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Security/UserController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Controllers/Task/TaskController.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/ConfigureService.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/ConfigureService.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/ConfigureService.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/ResourceService.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="mock:///home/acite/embd/WebProjects/Abyss/Abyss/Components/Services/ResourceService.cs" root0="SKIP_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/TaskService.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Services/UserService.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Static/Helpers.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Components/Static/HttpContextExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Model/Bookmark.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Model/ChallengeResponse.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
<setting file="file://$PROJECT_DIR$/Abyss/Model/Chip.cs" root0="FORCE_HIGHLIGHTING" />
|
||||
@@ -75,30 +72,30 @@
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
".NET Launch Settings Profile.Abyss: http.executor": "Run",
|
||||
".NET Launch Settings Profile.Abyss: https.executor": "Debug",
|
||||
".NET Project.AbyssCli.executor": "Run",
|
||||
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"Publish to folder.Publish Abyss to folder x86.executor": "Run",
|
||||
"Publish to folder.Publish Abyss to folder.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"XThreadsFramesViewSplitterKey": "0.30266345",
|
||||
"git-widget-placeholder": "dev-live",
|
||||
"last_opened_file_path": "/storage/Images/31/summary.json",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
".NET Launch Settings Profile.Abyss: http.executor": "Run",
|
||||
".NET Launch Settings Profile.Abyss: https.executor": "Debug",
|
||||
".NET Project.AbyssCli.executor": "Run",
|
||||
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||
"Publish to folder.Publish Abyss to folder x86.executor": "Run",
|
||||
"Publish to folder.Publish Abyss to folder.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"XThreadsFramesViewSplitterKey": "0.30266345",
|
||||
"git-widget-placeholder": "main",
|
||||
"last_opened_file_path": "/storage/Images/31/summary.json",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="RunManager" selected="Publish to folder.Publish Abyss to folder">
|
||||
<configuration name="Publish Abyss to folder x86" type="DotNetFolderPublish" factoryName="Publish to folder">
|
||||
<riderPublish configuration="Release" platform="Any CPU" produce_single_file="true" ready_to_run="true" self_contained="true" target_folder="/opt/security/https/server" target_framework="net9.0" uuid_high="3690631506471504162" uuid_low="-4858628519588143325">
|
||||
@@ -207,6 +204,11 @@
|
||||
<workItem from="1757076719875" duration="601000" />
|
||||
<workItem from="1757219779961" duration="112000" />
|
||||
<workItem from="1757386288260" duration="3634000" />
|
||||
<workItem from="1757428682321" duration="171000" />
|
||||
<workItem from="1757429030386" duration="20000" />
|
||||
<workItem from="1757508119360" duration="1704000" />
|
||||
<workItem from="1757519520290" duration="14000" />
|
||||
<workItem from="1757567561745" duration="2019000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -11,7 +11,7 @@ using System.IO;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ImageController(ILogger<ImageController> logger, ResourceService rs, ConfigureService config) : Controller
|
||||
public class ImageController(ILogger<ImageController> logger, ResourceService rs, ConfigureService config) : BaseController
|
||||
{
|
||||
public readonly string ImageFolder = Path.Combine(config.MediaRoot, "Images");
|
||||
|
||||
@@ -78,6 +78,4 @@ public class ImageController(ILogger<ImageController> logger, ResourceService rs
|
||||
|
||||
return PhysicalFile(d, "image/jpeg", enableRangeProcessing: true);
|
||||
}
|
||||
|
||||
private string Ip => HttpContext.Connection.RemoteIpAddress?.ToString() ?? "127.0.0.1";
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Abyss.Components.Controllers.Media;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class LiveController(ILogger<LiveController> logger, ResourceService rs, ConfigureService config): Controller
|
||||
public class LiveController(ILogger<LiveController> logger, ResourceService rs, ConfigureService config): BaseController
|
||||
{
|
||||
public readonly string LiveFolder = Path.Combine(config.MediaRoot, "Live");
|
||||
|
||||
@@ -33,25 +33,17 @@ public class LiveController(ILogger<LiveController> logger, ResourceService rs,
|
||||
return r ? Ok("Success") : BadRequest();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/{item}")]
|
||||
public async Task<IActionResult> GetLive(string id, string? token, string item)
|
||||
[HttpGet("{id}/{token}/{item}")]
|
||||
public async Task<IActionResult> GetLive(string id, string token, string item)
|
||||
{
|
||||
var d = Helpers.SafePathCombine(LiveFolder, [id, item]);
|
||||
var f = Helpers.SafePathCombine(LiveFolder, [id]);
|
||||
if (d == null || f == null) return BadRequest();
|
||||
|
||||
// TODO: ffplay does not add the m3u8 query parameter in ts requests, so special treatment is given to ts here
|
||||
// TODO: It should be pointed out that this implementation is not secure and should be modified in subsequent updates
|
||||
if (d.EndsWith(".ts"))
|
||||
{
|
||||
if(System.IO.File.Exists(d))
|
||||
return PhysicalFile(d, Helpers.GetContentType(d));
|
||||
else
|
||||
return NotFound();
|
||||
}
|
||||
// TODO: (History)ffplay does not add the m3u8 query parameter in ts requests, so special treatment is given to ts here
|
||||
// TODO: (History)It should be pointed out that this implementation is not secure and should be modified in subsequent updates
|
||||
|
||||
if(token == null)
|
||||
return StatusCode(403, new { message = "403 Denied" });
|
||||
// TODO: It's still not very elegant, but it's a bit better to some extent
|
||||
|
||||
bool r = await rs.Valid(f, token, OperationType.Read, Ip);
|
||||
if(!r) return StatusCode(403, new { message = "403 Denied" });
|
||||
@@ -61,6 +53,4 @@ public class LiveController(ILogger<LiveController> logger, ResourceService rs,
|
||||
else
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
private string Ip => HttpContext.Connection.RemoteIpAddress?.ToString() ?? "127.0.0.1";
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Abyss.Components.Controllers.Media;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class VideoController(ILogger<VideoController> logger, ResourceService rs, ConfigureService config) : Controller
|
||||
public class VideoController(ILogger<VideoController> logger, ResourceService rs, ConfigureService config) : BaseController
|
||||
{
|
||||
private ILogger<VideoController> _logger = logger;
|
||||
|
||||
@@ -109,6 +109,4 @@ public class VideoController(ILogger<VideoController> logger, ResourceService rs
|
||||
if (!r) return StatusCode(403, new { message = "403 Denied" });
|
||||
return PhysicalFile(d, "video/mp4", enableRangeProcessing: true);
|
||||
}
|
||||
|
||||
private string Ip => HttpContext.Connection.RemoteIpAddress?.ToString() ?? "127.0.0.1";
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Abyss.Components.Services;
|
||||
using Abyss.Components.Static;
|
||||
using Abyss.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.RateLimiting;
|
||||
@@ -12,7 +13,7 @@ namespace Abyss.Components.Controllers.Security;
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[EnableRateLimiting("Fixed")]
|
||||
public class UserController(UserService user, ILogger<UserController> logger) : Controller
|
||||
public class UserController(UserService user, ILogger<UserController> logger) : BaseController
|
||||
{
|
||||
private readonly ILogger<UserController> _logger = logger;
|
||||
private readonly UserService _user = user;
|
||||
@@ -125,6 +126,4 @@ public class UserController(UserService user, ILogger<UserController> logger) :
|
||||
return false;
|
||||
return Regex.IsMatch(input, @"^[a-zA-Z0-9]+$");
|
||||
}
|
||||
|
||||
private string Ip => HttpContext.Connection.RemoteIpAddress?.ToString() ?? "127.0.0.1";
|
||||
}
|
||||
57
Abyss/Components/Static/ControllerExtensions.cs
Normal file
57
Abyss/Components/Static/ControllerExtensions.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Abyss.Components.Static;
|
||||
|
||||
public abstract class BaseController : Controller
|
||||
{
|
||||
private string? _ip;
|
||||
|
||||
protected string Ip
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ip != null)
|
||||
return _ip;
|
||||
|
||||
_ip = GetClientIpAddress();
|
||||
|
||||
if (string.IsNullOrEmpty(_ip))
|
||||
throw new InvalidOperationException("invalid IP");
|
||||
|
||||
return _ip;
|
||||
}
|
||||
}
|
||||
|
||||
private string? GetClientIpAddress()
|
||||
{
|
||||
var remoteIp = HttpContext.Connection.RemoteIpAddress;
|
||||
|
||||
if (remoteIp != null && (IPAddress.IsLoopback(remoteIp) || remoteIp.ToString() == "::1"))
|
||||
{
|
||||
return remoteIp.ToString();
|
||||
}
|
||||
|
||||
string? ip = remoteIp?.ToString();
|
||||
|
||||
if (HttpContext.Request.Headers.TryGetValue("X-Forwarded-For", out var forwardedFor))
|
||||
{
|
||||
var forwardedIps = forwardedFor.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.ToArray();
|
||||
|
||||
if (forwardedIps.Length > 0)
|
||||
{
|
||||
ip = forwardedIps[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ip) && HttpContext.Request.Headers.TryGetValue("X-Real-IP", out var realIp))
|
||||
{
|
||||
ip = realIp.ToString();
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ public class Program
|
||||
{
|
||||
options.AddFixedWindowLimiter("Fixed", policyOptions =>
|
||||
{
|
||||
// 时间窗口长度
|
||||
policyOptions.Window = TimeSpan.FromSeconds(30);
|
||||
policyOptions.PermitLimit = 10;
|
||||
policyOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
||||
@@ -48,8 +47,7 @@ public class Program
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// app.UseHttpsRedirection();
|
||||
app.UseAuthorization();
|
||||
app.MapStaticAssets();
|
||||
app.MapControllers();
|
||||
|
||||
Reference in New Issue
Block a user