一、OAuth2.0的前世今生

说到身份认证,大家可能都用过账号密码登录。这种方式简单直接,但存在安全隐患。比如密码泄露、重复使用等问题。OAuth2.0就是为了解决这些问题而生的,它允许用户授权第三方应用访问自己的资源,而无需分享密码。

在.NET Core中实现OAuth2.0认证,我们需要先了解几个核心概念:

  1. 资源所有者(Resource Owner):就是用户本人
  2. 客户端(Client):要访问资源的应用
  3. 授权服务器(Authorization Server):负责颁发令牌
  4. 资源服务器(Resource Server):存放用户资源的服务器

二、搭建基础环境

我们先从创建一个.NET Core Web API项目开始。使用Visual Studio或者dotnet CLI都可以:

dotnet new webapi -n OAuthDemo
cd OAuthDemo
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

接下来配置基本的认证服务。在Startup.cs中添加以下代码:

public void ConfigureServices(IServiceCollection services)
{
    // 添加认证服务
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });
    
    services.AddControllers();
}

三、实现授权服务器

授权服务器是OAuth2.0的核心,负责颁发访问令牌。我们来实现一个简单的版本:

[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;
    
    public AuthController(IConfiguration config)
    {
        _config = config;
    }
    
    [HttpPost("token")]
    public IActionResult GetToken([FromBody] LoginModel login)
    {
        // 这里应该是验证用户凭证的逻辑
        if (login.Username != "admin" || login.Password != "123456")
            return Unauthorized();
        
        // 生成JWT令牌
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_config["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.Name, login.Username),
                new Claim(ClaimTypes.Role, "Admin")
            }),
            Expires = DateTime.UtcNow.AddHours(1),
            Issuer = _config["Jwt:Issuer"],
            Audience = _config["Jwt:Audience"],
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key), 
                SecurityAlgorithms.HmacSha256Signature)
        };
        
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var tokenString = tokenHandler.WriteToken(token);
        
        return Ok(new { Token = tokenString });
    }
}

public class LoginModel
{
    public string Username { get; set; }
    public string Password { get; set; }
}

四、保护API资源

现在我们来创建一个需要认证的API端点:

[Route("api/[controller]")]
[ApiController]
[Authorize]  // 添加认证属性
public class ValuesController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        // 可以从令牌中获取用户信息
        var userName = User.Identity.Name;
        var role = User.FindFirst(ClaimTypes.Role)?.Value;
        
        return Ok(new { 
            Message = $"Hello {userName}", 
            YourRole = role 
        });
    }
}

五、实现OAuth2.0的四种授权模式

OAuth2.0定义了四种授权模式,我们来分别实现:

1. 授权码模式(最安全)

[HttpGet("authorize")]
public IActionResult Authorize(
    [FromQuery] string response_type,  // 必须为"code"
    [FromQuery] string client_id,
    [FromQuery] string redirect_uri,
    [FromQuery] string scope,
    [FromQuery] string state)
{
    // 验证client_id等参数
    if (client_id != "valid_client")
        return BadRequest("Invalid client");
    
    // 这里应该显示授权页面,简化起见直接返回授权码
    var code = Guid.NewGuid().ToString();
    
    // 存储授权码,实际项目中应该使用数据库或缓存
    MemoryCache.Default.Set(code, new AuthCode
    {
        ClientId = client_id,
        RedirectUri = redirect_uri,
        Expiry = DateTime.Now.AddMinutes(5)
    });
    
    // 重定向到客户端指定的URI
    return Redirect($"{redirect_uri}?code={code}&state={state}");
}

2. 简化模式(适用于纯前端应用)

[HttpGet("implicit")]
public IActionResult ImplicitGrant(
    [FromQuery] string response_type,  // 必须为"token"
    [FromQuery] string client_id,
    [FromQuery] string redirect_uri,
    [FromQuery] string scope,
    [FromQuery] string state)
{
    // 验证参数
    if (response_type != "token")
        return BadRequest("Invalid response_type");
    
    // 生成访问令牌
    var token = GenerateAccessToken(client_id);
    
    // 重定向到客户端,携带访问令牌
    return Redirect($"{redirect_uri}#access_token={token}&token_type=Bearer&expires_in=3600&state={state}");
}

六、实际应用中的注意事项

  1. 安全性考虑:

    • 始终使用HTTPS
    • 令牌设置合理的过期时间
    • 实现令牌刷新机制
    • 对敏感操作要求重新认证
  2. 性能优化:

    • 使用内存缓存存储令牌
    • 考虑使用Reference Token代替JWT
    • 实现令牌的撤销机制
  3. 扩展性:

    • 支持多租户
    • 实现细粒度的权限控制
    • 考虑与现有用户系统的集成

七、总结与展望

通过上面的实现,我们在.NET Core中完成了一个完整的OAuth2.0认证授权流程。虽然示例做了简化,但核心流程已经完整呈现。在实际项目中,你可能需要考虑更多细节,比如:

  1. 使用IdentityServer4等成熟框架
  2. 实现更完善的用户管理系统
  3. 增加审计日志
  4. 支持多种登录方式(Social Login)

OAuth2.0是现代应用开发中不可或缺的一环,掌握它的实现原理对于构建安全的API服务至关重要。希望本文能为你提供有价值的参考。