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

Posted

技术标签:

【中文标题】使用本地帐户使用安全的 ASP Net 5 web api【英文标题】:Consuming secure ASP Net 5 web api using local account 【发布时间】:2016-03-23 05:49:47 【问题描述】:

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

【问题讨论】:

【参考方案1】:

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

public class TokenController : Controller

    private readonly IBearerTokenGenerator generator;
    private readonly IClientsManager clientsManager;
    private readonly IOptions<TokenAuthOptions> options;

    public TokenController(IBearerTokenGenerator generator,
        IClientsManager clientsManager,
        IOptions<TokenAuthOptions> options)
    
        this.generator = generator;
        this.clientsManager = clientsManager;
        this.options = options;
    

    [HttpPost, AllowAnonymous]
    public IActionResult Post(AuthenticationViewModel req)
    
        return clientsManager
            .Find(req.client_id, req.client_secret)
            .Map(c => c.Client)
            .Map(c => (IActionResult)new ObjectResult(new 
                access_token = generator.Generate(c),
                expires_in = options.Value.ExpirationDelay.TotalSeconds,
                token_type = "Bearer"
            ))
            .ValueOrDefault(HttpUnauthorized);
    


public class BearerTokenGenerator : IBearerTokenGenerator

    private readonly IOptions<TokenAuthOptions> tokenOptions;

    public BearerTokenGenerator(IOptions<TokenAuthOptions> tokenOptions)
    
        this.tokenOptions = tokenOptions;
    

    public string Generate(Client client)
    
        var expires = Clock.UtcNow.Add(tokenOptions.Value.ExpirationDelay);
        var handler = new JwtSecurityTokenHandler();

        var identity = new ClaimsIdentity(new GenericIdentity(client.Identifier, "TokenAuth"), new Claim[] 
            new Claim("client_id", client.Identifier)
        );

        var securityToken = handler.CreateToken(
            issuer: tokenOptions.Value.Issuer,
            audience: tokenOptions.Value.Audience,
            signingCredentials: tokenOptions.Value.SigningCredentials,
            subject: identity,
            expires: expires);

        return handler.WriteToken(securityToken);
    


public class ClientsManager : IClientsManager

    private readonly MembershipDataContext db;
    private readonly ISecretHasher hasher;

    public ClientsManager(MembershipDataContext db,
        ISecretHasher hasher)
    
        this.db = db;
        this.hasher = hasher;
    

    public void Create(string name, string identifier, string secret, Company company)
    
        var client = new Client(name, identifier, company);
        db.Clients.Add(client);

        var hash = hasher.HashSecret(secret);
        var apiClient = new ApiClient(client, hash);

        db.ApiClients.Add(apiClient);
    

    public Option<ApiClient> Find(string identifier, string secret)
    
        return FindByIdentifier(identifier)
            .Where(c => hasher.Verify(c.SecretHash, secret));
    

    public void ChangeSecret(string identifier, string secret)
    
        var client = FindByIdentifier(identifier).ValueOrDefault(() => 
            throw new ArgumentException($"could not find any client with identifier  identifier ");
        );

        var hash = hasher.HashSecret(secret);
        client.ChangeSecret(hash);
    

    public string GenerateRandomSecret()
    
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        var random = new Random();
        var generated = new string(Enumerable.Repeat(chars, 12).Select(s => s[random.Next(s.Length)]).ToArray());
        return Convert.ToBase64String(Encoding.UTF8.GetBytes(generated));
    

    private Option<ApiClient> FindByIdentifier(string identifier)
    
        return db.ApiClients
            .Include(c => c.Client)
            .SingleOrDefault(c => c.Client.Identifier == identifier)
            .ToOptionByRef();
    


public class SecretHasher : ISecretHasher

    private static Company fakeCompany = new Company("fake", "fake");
    private static Client fakeClient = new Client("fake", "fake", fakeCompany);

    private readonly IPasswordHasher<Client> hasher;

    public SecretHasher(IPasswordHasher<Client> hasher)
    
        this.hasher = hasher;
    

    public string HashSecret(string secret)
    
        return hasher.HashPassword(fakeClient, secret);
    

    public bool Verify(string hashed, string secret)
    
        return hasher.VerifyHashedPassword(fakeClient, hashed, secret) == PasswordVerificationResult.Success;
    

现在在 Startup.cs 中

            services.Configure<TokenAuthOptions>(options =>
        
            options.Audience = "API";
            options.Issuer = "Web-App";
            options.SigningCredentials = new SigningCredentials(GetSigninKey(), SecurityAlgorithms.RsaSha256Signature);
            options.ExpirationDelay = TimeSpan.FromDays(365);
        );

            services.AddAuthorization(auth =>
        
            auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
                .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
                .RequireAuthenticatedUser()
                .Build());
        

        app.UseJwtBearerAuthentication(options =>
        
            options.TokenValidationParameters.IssuerSigningKey = GetSigninKey();
            options.TokenValidationParameters.ValidAudience = "API";
            options.TokenValidationParameters.ValidIssuer = "Web-App";
            options.TokenValidationParameters.ValidateSignature = true;
            options.TokenValidationParameters.ValidateLifetime = true;
            options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(0);
        );

【讨论】:

以上是关于使用本地帐户使用安全的 ASP Net 5 web api的主要内容,如果未能解决你的问题,请参考以下文章

当用户导航到 ASP.NET 网站时使用哪个帐户?

ASP.Net Core Web API 中的安全用户操作

具有个人用户帐户身份验证的 ASP.NET MVC 5 WEB API

asp.net从谷歌应用程序帐户发送邮件不起作用

用户 ASP.NET 运行在

使用受信任连接时,哪个帐户用于向 SQL 验证 ASP.net?