【问题标题】:Allow "semi anonymous" authentication允许“半匿名”身份验证
【发布时间】:2018-04-30 01:48:36
【问题描述】:

我有一种情况,我需要一些控制器方法才能被经过身份验证的用户访问,或者如果请求在 url 中包含某种“访问令牌”。

例如:

经过身份验证的用户可以调用: https://example.com/some/resource

或者未经身份验证的用户可以进行相同的调用,但将某种令牌添加到 url(或作为标头): https://example.com/some/resource?token=123abc

令牌不必是超级机密,只有难以猜测的东西。

[AllowSpecialToken]
[HttpGet]
[Route("some/resource")]
public async Task<string> GetSomeResource()
{
    return "some resource";
}

我正在苦苦挣扎的是如何编写AllowSpecialTokenAttribute。以及如何在我们现在进行的身份验证(使用OpenIddict)之前运行它。

这是一个愚蠢的用例吗?我应该找到其他解决方案吗?

提供一些上下文:我们有一个调用我们的 API 的 SPA。 SPA 的某些页面可以通过发送链接与其他人(非用户)共享。该链接将包含令牌。这些页面的内容在安全方面并不重要,但它们不应该完全打开。

【问题讨论】:

    标签: c# authentication asp.net-core asp.net-core-mvc openiddict


    【解决方案1】:

    您需要创建自己的身份验证属性。我过去做过类似的事情,这是我的存根:

    public class TokenAuthenticationAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            // this will read `token` parameter from your URL
            ValueProviderResult valueProvided = filterContext.Controller.ValueProvider.GetValue("token");
            if (valueProvided == null)
            {
                filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
                return;
            }
    
            var providedToken = valueProvided.AttemptedValue;
    
            var storedToken = "12345"; // <-- get your token value from DB or something
    
            if (storedToken != providedToken)
            {
                filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
                return;
            }
        }
    }
    

    然后用属性装饰你的动作:

    [TokenAuthentication]
    [HttpGet]
    [Route("some/resource")]
    public async Task<string> GetSomeResource()
    {
        return "some resource";
    }    
    

    让你的 URI 看起来像 https:\\www.example.com\api\some\resource?token=12345

    【讨论】:

    • 这是用于 Asp.Net Core 还是 Asp.Net?
    • 那是旧的asp。抱歉,我错过了您将问题标记为核心
    • 是的,对不起。猜猜我用 asp.net-identity 标签愚弄了你。删除了:) 对如何使用 asp.net core 有任何想法吗?
    • 抱歉,没有机会在 Core 中做同样的事情。我只能想象该技术将在this doc 中描述
    【解决方案2】:

    您可以尝试以下方法,看看它是否适合您。 警告:我完全不知道这是否是“正确”的做法。我只知道这是一种似乎有效的方式。如果您发现问题,请测试并投票。在我编写的另一个身份验证处理程序上,我仍然有一个打开的question,但没有回复,因此请谨慎使用。如果您打算研究此用例,可能值得联系 MS 的吹镖(搜索用户)。

    中间件类

    public class TokenCodeAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        public const string DefaultSchemeName = "TokenAuthScheme";
    
        public TokenCodeAuthHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options, 
            ILoggerFactory logger, 
            UrlEncoder encoder, 
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }
    
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            AuthenticateResult result = await this.Context.AuthenticateAsync();
            if (result.Succeeded)
            {
                //User has supplied details
                return AuthenticateResult.Success(result.Ticket);
            }
            else if (Context.Request.Query["token"] == "123abc")   //TODO: Change hard-coded token
            {
                //User has supplied token
                string username = "Test";    //Get/set username here
                var claims = new[]
                    {
                        new Claim(ClaimTypes.NameIdentifier, username, ClaimValueTypes.String, Options.ClaimsIssuer),
                        new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Options.ClaimsIssuer)
                    };
    
                ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
                AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
                return AuthenticateResult.Success(ticket);
            }
            return AuthenticateResult.Fail("Unauthorized");
        }
    }
    

    在启动中配置服务

    services.AddAuthentication()
        .AddScheme<AuthenticationSchemeOptions, TokenCodeAuthHandler>(
            TokenCodeAuthHandler.DefaultSchemeName, 
            (o) => { });
    

    属性使用

    在控制器动作上使用如下: 注意 - 我似乎无法覆盖控制器级别的授权属性)。

    [Authorize(AuthenticationSchemes = TokenCodeAuthHandler.DefaultSchemeName)]
    [HttpGet]
    [Route("some/resource")]
    public async Task<string> GetSomeResource()
    {
        return "some resource";
    }  
    

    【讨论】:

    • 谢谢!但我无法让它工作:/。我的“常规”身份验证在 TokenCodeAuthHandler 甚至被调用之前就开始了......
    • 我似乎无法让它覆盖控制器级别 [Authorize] 标签,所以在我测试它的控制器上,我必须删除控制器级别标签,并且每个标签上都有适当的 Auth 标签行动。也许有人会想出更好的方法。
    • 嗯,好的。我没有在每个控制器上都有它,而是对所有东西都有一个全局 [Authorize]。
    • 嗯,也许您可​​以使用policy 来选择方案。否则,这可能不是您要寻找的答案:)
    • 好的,谢谢!但是,当我希望它覆盖配置为 services .AddMvc(options =&gt; { var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); options.Filters.Add(new AuthorizeFilter(policy)); }) 的全局身份验证过滤器时,这仍然不起作用
    猜你喜欢
    • 2023-04-09
    • 2021-03-23
    • 2011-04-16
    • 2020-07-01
    • 1970-01-01
    • 2015-02-09
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    相关资源
    最近更新 更多