使用本地帐户使用安全的ASP Net 5 web api

Gui*_*lez 7 c# oauth-2.0 asp.net-web-api asp.net-core

我需要使用本地帐户从Web客户端使用ASP.NET 5安全Web API.在过去,有一个处理程序发出持票令牌以支持OAuth,现在持票人令牌发行责任已从身份中删除.有些人建议使用identityServer3,它需要客户端注册,这与identity2方法不同.在ASP.NET 5 Web API中实现授权的最简单方法是什么?在使用资源所有者密码流时,如何避免传递客户端ID和客户端密钥以获取访问令牌?如何调用api避免通过范围?

Gui*_*lez 0

我由此构建了一个简单的不记名令牌发行者,但使用身份密码哈希器。您可以在下面看到完整的代码:

\n\n
public class TokenController : Controller\n{\n    private readonly IBearerTokenGenerator generator;\n    private readonly IClientsManager clientsManager;\n    private readonly IOptions<TokenAuthOptions> options;\n\n    public TokenController(IBearerTokenGenerator generator,\n        IClientsManager clientsManager,\n        IOptions<TokenAuthOptions> options)\n    {\n        this.generator = generator;\n        this.clientsManager = clientsManager;\n        this.options = options;\n    }\n\n    [HttpPost, AllowAnonymous]\n    public IActionResult Post(AuthenticationViewModel req)\n    {\n        return clientsManager\n            .Find(req.client_id, req.client_secret)\n            .Map(c => c.Client)\n            .Map(c => (IActionResult)new ObjectResult(new {\n                access_token = generator.Generate(c),\n                expires_in = options.Value.ExpirationDelay.TotalSeconds,\n                token_type = "Bearer"\n            }))\n            .ValueOrDefault(HttpUnauthorized);\n    }\n}\n\npublic class BearerTokenGenerator : IBearerTokenGenerator\n{\n    private readonly IOptions<TokenAuthOptions> tokenOptions;\n\n    public BearerTokenGenerator(IOptions<TokenAuthOptions> tokenOptions)\n    {\n        this.tokenOptions = tokenOptions;\n    }\n\n    public string Generate(Client client)\n    {\n        var expires = Clock.UtcNow.Add(tokenOptions.Value.ExpirationDelay);\n        var handler = new JwtSecurityTokenHandler();\n\n        var identity = new ClaimsIdentity(new GenericIdentity(client.Identifier, "TokenAuth"), new Claim[] {\n            new Claim("client_id", client.Identifier)\n        });\n\n        var securityToken = handler.CreateToken(\n            issuer: tokenOptions.Value.Issuer,\n            audience: tokenOptions.Value.Audience,\n            signingCredentials: tokenOptions.Value.SigningCredentials,\n            subject: identity,\n            expires: expires);\n\n        return handler.WriteToken(securityToken);\n    }\n}\n\npublic class ClientsManager : IClientsManager\n{\n    private readonly MembershipDataContext db;\n    private readonly ISecretHasher hasher;\n\n    public ClientsManager(MembershipDataContext db,\n        ISecretHasher hasher)\n    {\n        this.db = db;\n        this.hasher = hasher;\n    }\n\n    public void Create(string name, string identifier, string secret, Company company)\n    {\n        var client = new Client(name, identifier, company);\n        db.Clients.Add(client);\n\n        var hash = hasher.HashSecret(secret);\n        var apiClient = new ApiClient(client, hash);\n\n        db.ApiClients.Add(apiClient);\n    }\n\n    public Option<ApiClient> Find(string identifier, string secret)\n    {\n        return FindByIdentifier(identifier)\n            .Where(c => hasher.Verify(c.SecretHash, secret));\n    }\n\n    public void ChangeSecret(string identifier, string secret)\n    {\n        var client = FindByIdentifier(identifier).ValueOrDefault(() => {\n            throw new ArgumentException($"could not find any client with identifier { identifier }");\n        });\n\n        var hash = hasher.HashSecret(secret);\n        client.ChangeSecret(hash);\n    }\n\n    public string GenerateRandomSecret()\n    {\n        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";\n        var random = new Random();\n        var generated = new string(Enumerable.Repeat(chars, 12).Select(s => s[random.Next(s.Length)]).ToArray());\n        return Convert.ToBase64String(Encoding.UTF8.GetBytes(generated));\n    }\n\n    private Option<ApiClient> FindByIdentifier(string identifier)\n    {\n        return db.ApiClients\n            .Include(c => c.Client)\n            .SingleOrDefault(c => c.Client.Identifier == identifier)\n            .ToOptionByRef();\n    }\n}\n\npublic class SecretHasher : ISecretHasher\n{\n    private static Company fakeCompany = new Company("fake", "fake");\n    private static Client fakeClient = new Client("fake", "fake", fakeCompany);\n\n    private readonly IPasswordHasher<Client> hasher;\n\n    public SecretHasher(IPasswordHasher<Client> hasher)\n    {\n        this.hasher = hasher;\n    }\n\n    public string HashSecret(string secret)\n    {\n        return hasher.HashPassword(fakeClient, secret);\n    }\n\n    public bool Verify(string hashed, string secret)\n    {\n        return hasher.VerifyHashedPassword(fakeClient, hashed, secret) == PasswordVerificationResult.Success;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在在 Startup.cs 中

\n\n
            services.Configure<TokenAuthOptions>(options =>\n        {\n            options.Audience = "API";\n            options.Issuer = "Web-App";\n            options.SigningCredentials = new SigningCredentials(GetSigninKey(), SecurityAlgorithms.RsaSha256Signature);\n            options.ExpirationDelay = TimeSpan.FromDays(365);\n        });\n\n            services.AddAuthorization(auth =>\n        {\n            auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()\n                .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme\xe2\x80\x8c\xe2\x80\x8b)\n                .RequireAuthenticatedUser()\n                .Build());\n        }\n\n        app.UseJwtBearerAuthentication(options =>\n        {\n            options.TokenValidationParameters.IssuerSigningKey = GetSigninKey();\n            options.TokenValidationParameters.ValidAudience = "API";\n            options.TokenValidationParameters.ValidIssuer = "Web-App";\n            options.TokenValidationParameters.ValidateSignature = true;\n            options.TokenValidationParameters.ValidateLifetime = true;\n            options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(0);\n        });\n
Run Code Online (Sandbox Code Playgroud)\n