详解.NET实现OAuth2.0四种模式(2)密码模式

一、密码模式的认证流程

密码模式是比较简单的一种模式,其认证流程如下图所示:

二、受限资源设计

OAuth2.0设计的目的是限制某些资源的访问,用户必须认证通过之后才能访问。我们在项目中加入一个简单的Controller,作为资源示例。

右击项目,添加一个Web API控制器类(v2.1),如下图所示:

我们把这个类命名为ValuesController,在这个类中只添加一个函数:

public IHttpActionResult Get()
{
    return Ok(new string[] { "value1", "value2" });
}

完成编译之后,我们可以在Postman中访问这个资源:

显然,这个资源并没有受到保护,任何人不经过授权都可以访问。如果要让这个资源在授权之后才能访问,做法非常简单,就是在函数的头上加上[Authorize]:

[Authorize]
public IHttpActionResult Get()
{
    return Ok(new string[] { "value1", "value2" });
}

这时候,如果我们再去访问这个资源时,将会被拒绝:

如果要想访问此资源,我们就需要通过认证。

三、密码模式实现

首先,在项目中加入一个名为Startup的类,其代码如下所示:

[assembly: OwinStartup(typeof(PasswordMode.Startup))]//让整个网站的入口为Startup这个类
namespace PasswordMode
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //配置OAuth
            ConfigureOAuth(app);

            //配置网站路由等信息
            HttpConfiguration config = new HttpConfiguration();
            Register(config);

            //允许跨域访问
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

            app.UseWebApi(config);
        }

        private void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,//允许http而非https访问
                TokenEndpointPath = new PathString("/token"),//访问host/token获取AccessToken
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),//AccessToken在30分钟后过期
                Provider = new AuthorizationServerProvider()//AccessToken的提供类
            };

            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        }

        private static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
            jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        }
    }
}

代码中的注释应该已经比较清楚。由于这个项目是一个空项目,没有加入任何API或MVC的特性,所以我们在Register函数中注册了这些属性。ConfigureOAuth函数是对OAuth2.0协议的配置,如果使用密码模式,就是按照上面代码中的配置。用户只需要输入用户名、密码,然后访问host/token这个地址,即可获取AccessToken。

下面是AuthorizationServerProvider类的实现代码:

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        //允许跨域访问
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        //获取用户传入的用户名和密码
        string UserName = context.UserName;
        string Password = context.Password;

        //通过查数据库,判断用户名和密码是否正确
        //以下只是一个示例,用户名必须以test开头
        if (!UserName.StartsWith("test"))
        {
            context.SetError("invalid_grant", "用户名或密码不正确");
            return;
        }

        //以下即为认证成功

        //通过查数据库,得到一些用户的信息
        int userid = 13;
        string role = "管理员";
        string scope = "权限1,权限2";

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("userid", userid.ToString()));
        identity.AddClaim(new Claim("role", role));
        identity.AddClaim(new Claim("scope", scope));

        context.Validated(identity);

    }
}

我们先在上下文中获取用户名和密码,然后一般通过查数据库,判断此用户名和密码是否正确。如果不正确,认证就失败了。如果正确,我们可以查数据库,获取用户的角色、权限范围等信息。

完成上述代码的编写后,密码模式就完成了。(是不是很简单)

我们用Postman测试:

在返回结果中,我们看到了access_token,把它复制出来,用在资源的访问上:

四、部分访问限制

我们在上述的代码中可以发现,当我们拿到AccessToken之后,所有标注[Authorize]的资源都可以访问了。但实际上,我们的资源限制会更复制一些。例如我们只允许管理员身份的用户去访问,或者只允许某个时间之前注册的用户,或者像微信登录时询问的,获取昵称的权限、获取头像的权限等等。对于这种需求,我们可以在资源函数中进行处理。

首先,我们在认证通过时,通过查数据库,得到了一些用户的属性:

//通过查数据库,得到一些用户的信息
int userid = 13;
string role = "管理员";
string scope = "权限1,权限2";

var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("userid", userid.ToString()));
identity.AddClaim(new Claim("role", role));
identity.AddClaim(new Claim("scope", scope));

我们可以在资源函数中根据这些属性进行判断,是否允许用户继续访问。例如我们的资源函数可以改成这样:

public IHttpActionResult Get()
{
    string userid = "";
    string role = "";
    string scope = "";
    foreach (Claim c in (this.User.Identity as ClaimsIdentity).Claims)
    {
        switch (c.Type)
        {
            case "userid":
                userid = c.Value;
                break;
            case "role":
                role = c.Value;
                break;
            case "scope":
                scope = c.Value;
                break;
        }
    }

    if (userid != "13" || role != "管理员" || !scope.Contains("权限1"))
    {
        return Unauthorized();
    }

    return Ok(new string[] { "value1", "value2" });
}

这样的话,只有用户ID为13,并且角色为管理员,同时又具有权限1这个访问权限的用户,才能正常地获取资源。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现OAuth2.0服务端,需要遵循OAuth2.0协议,实现授权服务器和资源服务器之间的交互。以下是基本的步骤: 1.创建一个ASP.NET Core Web API项目。 2.安装IdentityServer4和Microsoft.AspNetCore.Authentication.JwtBearer NuGet包。 3.在Startup.cs中配置IdentityServer,并添加JwtBearer认证。 4.创建客户端和API资源,并将它们添加到IdentityServer中。 5.实现OAuth2.0授权终结点,例如授权码授权、密码授权、客户端凭证授权等。 6.实现API资源的保护,验证JWT令牌并确保访问令牌有效。 7.测试OAuth2.0授权和保护API资源。 下面是一个简单的示例,演示如何实现OAuth2.0服务端,包括JWT: 1. 在Startup.cs中配置IdentityServer和JwtBearer认证。示例代码如下: ```csharp public void ConfigureServices(IServiceCollection services) { //添加IdentityServer服务 services.AddIdentityServer() .AddInMemoryClients(Config.Clients) .AddInMemoryApiResources(Config.Apis) .AddDeveloperSigningCredential(); //添加JwtBearer认证 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "http://localhost:5000"; //IdentityServer地址 options.RequireHttpsMetadata = false; //HTTPS设置为false,方便测试 options.Audience = "api1"; //API资源名称 }); services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseIdentityServer(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } ``` 2. 创建客户端和API资源。示例代码如下: ```csharp public static class Config { public static IEnumerable<Client> Clients => new List<Client> { new Client { ClientId = "client1", ClientSecrets = { new Secret("secret1".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api1" } } }; public static IEnumerable<ApiResource> Apis => new List<ApiResource> { new ApiResource("api1", "My API") }; } ``` 3. 实现授权终结点。示例代码如下: ```csharp [HttpPost] [Route("/connect/token")] public async Task<IActionResult> Token([FromBody] TokenRequest request) { if (request.GrantType == "password") { var user = _userService.ValidateCredentials(request.UserName, request.Password); if (user != null) { var accessToken = await _tokenService.CreateAccessTokenAsync(user); return Ok(new { access_token = accessToken, token_type = "Bearer", expires_in = (int)_tokenService.Options.Expiration.TotalSeconds }); } } return BadRequest(new { error = "unsupported_grant_type" }); } ``` 4. 实现API资源的保护。示例代码如下: ```csharp [Authorize] [HttpGet] [Route("test")] public IActionResult Test() { return Ok(new { message = "Hello, World!" }); } ``` 以上是一个基本的OAuth2.0服务端实现,包括JWT。你可以根据自己的需求进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值