当前位置:   article > 正文

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

详解.net实现oauth2

一、密码模式的认证流程

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

二、受限资源设计

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

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

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

  1. public IHttpActionResult Get()
  2. {
  3. return Ok(new string[] { "value1", "value2" });
  4. }

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

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

  1. [Authorize]
  2. public IHttpActionResult Get()
  3. {
  4. return Ok(new string[] { "value1", "value2" });
  5. }

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

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

三、密码模式实现

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

  1. [assembly: OwinStartup(typeof(PasswordMode.Startup))]//让整个网站的入口为Startup这个类
  2. namespace PasswordMode
  3. {
  4. public class Startup
  5. {
  6. public void Configuration(IAppBuilder app)
  7. {
  8. //配置OAuth
  9. ConfigureOAuth(app);
  10. //配置网站路由等信息
  11. HttpConfiguration config = new HttpConfiguration();
  12. Register(config);
  13. //允许跨域访问
  14. app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
  15. app.UseWebApi(config);
  16. }
  17. private void ConfigureOAuth(IAppBuilder app)
  18. {
  19. OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
  20. {
  21. AllowInsecureHttp = true,//允许http而非https访问
  22. TokenEndpointPath = new PathString("/token"),//访问host/token获取AccessToken
  23. AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),//AccessToken在30分钟后过期
  24. Provider = new AuthorizationServerProvider()//AccessToken的提供类
  25. };
  26. app.UseOAuthAuthorizationServer(OAuthServerOptions);
  27. app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
  28. }
  29. private static void Register(HttpConfiguration config)
  30. {
  31. config.MapHttpAttributeRoutes();
  32. config.Routes.MapHttpRoute(
  33. name: "DefaultApi",
  34. routeTemplate: "api/{controller}/{id}",
  35. defaults: new { id = RouteParameter.Optional }
  36. );
  37. var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
  38. jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
  39. }
  40. }
  41. }

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

下面是AuthorizationServerProvider类的实现代码:

  1. public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
  2. {
  3. public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  4. {
  5. context.Validated();
  6. }
  7. public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  8. {
  9. //允许跨域访问
  10. context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
  11. //获取用户传入的用户名和密码
  12. string UserName = context.UserName;
  13. string Password = context.Password;
  14. //通过查数据库,判断用户名和密码是否正确
  15. //以下只是一个示例,用户名必须以test开头
  16. if (!UserName.StartsWith("test"))
  17. {
  18. context.SetError("invalid_grant", "用户名或密码不正确");
  19. return;
  20. }
  21. //以下即为认证成功
  22. //通过查数据库,得到一些用户的信息
  23. int userid = 13;
  24. string role = "管理员";
  25. string scope = "权限1,权限2";
  26. var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  27. identity.AddClaim(new Claim("userid", userid.ToString()));
  28. identity.AddClaim(new Claim("role", role));
  29. identity.AddClaim(new Claim("scope", scope));
  30. context.Validated(identity);
  31. }
  32. }

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

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

我们用Postman测试:

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

四、部分访问限制

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

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

  1. //通过查数据库,得到一些用户的信息
  2. int userid = 13;
  3. string role = "管理员";
  4. string scope = "权限1,权限2";
  5. var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  6. identity.AddClaim(new Claim("userid", userid.ToString()));
  7. identity.AddClaim(new Claim("role", role));
  8. identity.AddClaim(new Claim("scope", scope));

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

  1. public IHttpActionResult Get()
  2. {
  3. string userid = "";
  4. string role = "";
  5. string scope = "";
  6. foreach (Claim c in (this.User.Identity as ClaimsIdentity).Claims)
  7. {
  8. switch (c.Type)
  9. {
  10. case "userid":
  11. userid = c.Value;
  12. break;
  13. case "role":
  14. role = c.Value;
  15. break;
  16. case "scope":
  17. scope = c.Value;
  18. break;
  19. }
  20. }
  21. if (userid != "13" || role != "管理员" || !scope.Contains("权限1"))
  22. {
  23. return Unauthorized();
  24. }
  25. return Ok(new string[] { "value1", "value2" });
  26. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/83508
推荐阅读
相关标签
  

闽ICP备14008679号