【问题标题】:User-authorization on API-side with .net core使用 .net 核心在 API 端进行用户授权
【发布时间】:2018-04-17 07:11:36
【问题描述】:

我正在 .net 核心中编写一个使用 API 和网站的网络应用程序。

网络服务构建一个 JWT 令牌。 这是服务配置(删除了不必要的部分)

public void ConfigureServices(IServiceCollection services)
{
    //...

    var tokenValidationParameters = new TokenValidationParameters
    {
        // The signing key must match!
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = signingKey,

        // Validate the JWT Issuer (iss) claim
        ValidateIssuer = true,
        ValidIssuer = "ExampleIssuer",

        // Validate the JWT Audience (aud) claim
        ValidateAudience = true,
        ValidAudience = "ExampleAudience",

        // Validate the token expiry
        ValidateLifetime = true,

        // If you want to allow a certain amount of clock drift, set that here:
        ClockSkew = TimeSpan.Zero,
    };

    var serialiser = services.BuildServiceProvider().GetService<IDataSerializer<AuthenticationTicket>>();

    var dataProtector = services.BuildServiceProvider().GetDataProtector(new string[] {$"IronSphere.Web.Site-Auth"});

    services
        .AddAuthentication(o =>
        {
            o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(cfg =>
        {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
            cfg.TokenValidationParameters = tokenValidationParameters;
        })
        .AddCookie(cookie =>
        {
            cookie.Cookie.Name = "access_token";
            cookie.TicketDataFormat = new JwtTokenValidator(
                SecurityAlgorithms.HmacSha256,
                tokenValidationParameters, serialiser, dataProtector);
        });

    //...
}

到目前为止,一切都很好。登录有效,我的网站端授权有效,我可以使用[Authorize] 属性。

现在的问题是,我在网站上登录了,但不是在 API 上。

我不能将[Authorize]-attribute 用于我的 API 方法(当然,这是有道理的)。

所以当我登录时,每次调用 API 时,我也会在标头中发送令牌(这可行,我可以在 API 控制器中读取它)。我想我需要反序列化它。

我尝试使用依赖注入将 IDataSerializer&lt;AuthenticationTicket&gt; 输入到我的控制器中:

services.AddSingleton<IDataSerializer<AuthenticationTicket>>(services.BuildServiceProvider().GetService<IDataSerializer<AuthenticationTicket>>());

然后从标题中获取密钥,反序列化,然后我将拥有带有声明的用户对象。但是当我尝试注入任何控制器时,它会使我的应用程序崩溃(只是一条消息,dotnet 停止工作)

知道如何在调用 api 时验证用户吗? (如果您需要我可以发布更多代码,只是不想在这里填写太多代码)

【问题讨论】:

  • 明确一点:client = your webapi, server = your auth/id server issue the token?
  • @Tseng 不,不完全是。客户端只是网站,服务器是 webapi(也处理 auth-things 和令牌)
  • @Tseng 刚刚更新了它。有点误导,抱歉。
  • 好的,所以客户端是一个具有视图和 cookie 身份验证的 MVC 应用程序,使用 webapi/auth 服务器作为 openid 提供程序?由于客户端不是 webapi,因此您无法使用 jwt 注册它(通常作为授权请求标头传递)。成功登录客户端后,您可以通过 string accessToken = await HttpContext.GetTokenAsync("access_token"); 检索令牌并将其传递给您调用的 api,即使用 HttpClientclient.SetBearerToken(accessToken); 后跟 string content = await client.GetStringAsync("http://localhost:5001/api/example/2");
  • 还要小心多次调用services.BuildServiceProvider(),它每次都会创建一个新的 IoC 容器,并且不应在 ConfigureService 中调用它。它将在Configure 方法执行之前被隐式调用。通过 new 实例化类型,这没关系,因为您在 ConfigureServices 这是组合根。此外,当手动设置不记名令牌时(在 HttpClient 上使用 SetBearerToken 方法,您必须将其内容添加为“不记名 ”,因此完整的标头如下所示:Authorization: Bearer xyzabc

标签: c# asp.net-core .net-core authorization asp.net-core-webapi


【解决方案1】:

所以在@Tseng 的意见帮助了我很多之后,这是我的结果(更多关于如何做得更好的意见会很好):

  1. 在 Startup.cs 中添加了令牌的取消保护器作为服务

    services.AddTransient<IJwtTokenService, JwtTokenService>();
    
  2. IJwtTokenService

    public interface IJwtTokenService
    {
        string UnprotectToken(string protectedText);
    }
    
  3. 已实现的JwtTokenService

    public class JwtTokenService:IJwtTokenService
    {
        private readonly IDataSerializer<AuthenticationTicket> _ticketSerializer;
        private readonly IDataProtector _dataProtector;
    
        public JwtTokenService(IDataSerializer<AuthenticationTicket> serializer, IDataProtector protector)
        {
            _ticketSerializer = serializer;
            _dataProtector = protector;
        }
    
        public string UnprotectToken(string protectedText)
        {
            SecurityKey signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(" ......... "));
    
            TokenValidationParameters tokenValidationParameters =
                _getTokenValidationParameters();
    
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            AuthenticationTicket authTicket;
            string embeddedJwt;
    
            try
            {
                // logic to deserialize token
                // logic to validate token
                // more logic... (algorithm,..)
            }
            catch (Exception)
            {
                return null;
            }
    
            return embeddedJwt;
        }
    }
    
  4. 所以在服务中我还必须添加IDataProtector。之前抛出异常。

    services.AddTransient(x => x.GetDataProtector(new[] {$"auth"}));
    
  5. 然后我可以将IJwtTokenService添加到依赖注入的构造函数中,取消保护并与标头一起发送:

    protected ServiceBase(
        IHttpContextAccessor contextAccessor, 
        IJwtTokenService jwtTokenService, 
        IMemoryCache memoryCache = null)
    {
        MemoryCache = memoryCache ?? new MemoryCache(new MemoryCacheOptions());
        CachingFunctionalty = new CachingFunctionality();
        HttpContextAccessor = contextAccessor;
        JwtTokenService = jwtTokenService;
    }
    
    protected RestClient CreateClient()
    {
        RestClient restClient = new RestClient(ServiceAdress);
    
        var token = HttpContextAccessor.HttpContext.Request.Cookies["access_token"];
    
        if (string.IsNullOrWhiteSpace(token)) return restClient;
    
        var unprotected = JwtTokenService.UnprotectToken(token);
        restClient.AuthenticationHeaderValue = new AuthenticationHeaderValue("Bearer", unprotected);
    
        return restClient;
    }
    

现在我的 API 可以与 AuthorizeAttribute 一起使用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-18
    • 2020-02-20
    • 2018-02-28
    • 2017-05-16
    • 2020-02-16
    • 2018-03-13
    • 1970-01-01
    • 2022-10-13
    相关资源
    最近更新 更多