赞
踩
密码模式是比较简单的一种模式,其认证流程如下图所示:
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这个访问权限的用户,才能正常地获取资源。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。