如何实现对他人拥有资源的安全匿名访问?附实现思路与代码示例
如何实现对他人拥有资源的安全匿名访问?附实现思路与代码示例
针对你提出的「用户创建资源后,能通过邮件分享给好友,好友无需注册就能匿名安全访问该特定资源」这个场景,确实没有特别通用的“标准Web模式”,不过你的初始思路——用ASP.NET Core的数据保护功能加密资源相关信息——是非常合理且安全的方案。下面我来梳理下完整的实现逻辑和优化后的代码示例:
核心实现思路
- 加密访问令牌:使用
IDataProtectionProvider加密包含「资源ID + 资源所有者ID」的信息,生成无法被篡改的共享令牌,将其放入共享链接(比如/shared-resource/[加密后的令牌]) - 分离权限逻辑:用两个控制器分别处理「登录用户的资源管理」和「匿名用户的共享访问」,避免权限逻辑混杂
- 统一业务逻辑:把资源的核心业务逻辑抽离到独立服务类中,让两个控制器复用同一套业务规则,减少代码冗余
完整代码实现
1. 共享的资源业务服务类
先把资源的核心操作抽离到服务类,让登录用户控制器和匿名访问控制器复用同一套业务规则:
public class PersonIdentity { public int PersonId { get; } // 从Claims初始化(登录用户场景) public PersonIdentity(IEnumerable<Claim> claims) { // 从JWT Claims中解析用户ID,需和登录时的Claim配置一致 PersonId = int.Parse(claims.First(c => c.Type == "PersonId").Value); } // 从共享令牌信息初始化(匿名访问场景) public PersonIdentity(int personId) { PersonId = personId; } } public class ResourceService { private readonly IDataAccess _dataAccess; public ResourceService(IDataAccess dataAccess) { _dataAccess = dataAccess; } // 获取资源:同时支持登录用户和匿名共享场景的权限校验 public ResourceDto GetResource(int resourceId, PersonIdentity identity) { var resource = _dataAccess.GetResource(resourceId); // 核心校验:确保访问者是资源所有者(或该资源已被所有者共享) if (resource.OwnerId != identity.PersonId) { throw new UnauthorizedAccessException("无权访问该资源"); } return ResourceDto.ToDto(resource); } // 登录用户专属的资源操作(比如修改、删除) public void UpdateResource(int resourceId, PersonIdentity identity, ResourceUpdateDto updateDto) { var resource = _dataAccess.GetResource(resourceId); if (resource.OwnerId != identity.PersonId) { throw new UnauthorizedAccessException("无权修改该资源"); } _dataAccess.UpdateResource(resource, updateDto); } }
2. 登录用户的资源管理控制器
处理登录用户的资源查询、修改、生成共享链接等操作,需通过[Authorize]验证身份:
[Authorize] public class ResourceController : ControllerBase { private readonly ResourceService _resourceService; private readonly IDataAccess _dataAccess; public ResourceController(ResourceService resourceService, IDataAccess dataAccess) { _resourceService = resourceService; _dataAccess = dataAccess; } // 登录用户查询自己的资源 [HttpGet("resources/{id:int}")] public IActionResult GetResource(int id) { var identity = new PersonIdentity(User.Claims); try { var resourceDto = _resourceService.GetResource(id, identity); return Ok(resourceDto); } catch (UnauthorizedAccessException) { return Forbid(); } } // 登录用户修改自己的资源 [HttpPut("resources/{id:int}")] public IActionResult UpdateResource(int id, [FromBody] ResourceUpdateDto updateDto) { var identity = new PersonIdentity(User.Claims); try { _resourceService.UpdateResource(id, identity, updateDto); return NoContent(); } catch (UnauthorizedAccessException) { return Forbid(); } } // 生成资源共享链接(仅资源所有者可操作) [HttpGet("resources/{id:int}/generate-share-link")] public IActionResult GenerateShareLink(int id, [FromServices] IDataProtectionProvider dataProtectionProvider) { var identity = new PersonIdentity(User.Claims); // 先校验当前用户是资源所有者 var resource = _dataAccess.GetResource(id); if (resource.OwnerId != identity.PersonId) { return Forbid(); } // 加密「资源ID + 所有者ID」生成共享令牌 var protector = dataProtectionProvider.CreateProtector("ResourceShareToken"); var shareToken = protector.Protect(JsonSerializer.Serialize(new ShareToken { ResourceId = id, OwnerId = identity.PersonId })); // 生成完整的共享链接(可根据前端路由调整格式) var shareUrl = Url.Action("GetSharedResource", "SharedResource", new { token = shareToken }, Request.Scheme); return Ok(new { ShareUrl = shareUrl }); } } // 用于加密的共享令牌模型 public class ShareToken { public int ResourceId { get; set; } public int OwnerId { get; set; } }
3. 匿名访问的共享资源控制器
处理匿名用户的共享资源访问,无需身份验证,但需解密令牌并验证权限:
[AllowAnonymous] public class SharedResourceController : ControllerBase { private readonly ResourceService _resourceService; private readonly IDataProtectionProvider _dataProtectionProvider; public SharedResourceController(ResourceService resourceService, IDataProtectionProvider dataProtectionProvider) { _resourceService = resourceService; _dataProtectionProvider = dataProtectionProvider; } [HttpGet("shared-resource")] public IActionResult GetSharedResource(string token) { try { // 解密共享令牌 var protector = _dataProtectionProvider.CreateProtector("ResourceShareToken"); var shareToken = JsonSerializer.Deserialize<ShareToken>(protector.Unprotect(token)); // 用令牌中的所有者ID创建身份,调用共享资源服务 var identity = new PersonIdentity(shareToken.OwnerId); var resourceDto = _resourceService.GetResource(shareToken.ResourceId, identity); return Ok(resourceDto); } catch (CryptographicException) { // 令牌无效或被篡改 return BadRequest("无效的共享链接"); } catch (UnauthorizedAccessException) { // 令牌信息与资源实际权限不匹配 return Forbid("无权访问该资源"); } } }
可选优化点
- 令牌过期机制:在
ShareToken中添加ExpiryTime字段,加密时设置过期时间,解密时校验,避免链接永久有效 - 共享状态记录:在资源表中添加
IsShared字段或单独建共享记录表,生成令牌时标记资源为可共享,访问时校验状态 - 访问次数限制:给每个共享令牌添加唯一标识,记录访问次数,防止链接被滥用
内容来源于stack exchange
相关产品推荐
相关产品推荐

