Web API使用记录系列OAuth授权与身份校验

Posted xihao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web API使用记录系列OAuth授权与身份校验相关的知识,希望对你有一定的参考价值。

  呼,开干第四篇,基于OWIN搭建OAuth认证授权服务器与接口身份校验。

   OAuth包含授权码模式、密码模式、客户端模式和简化模式,这里我们文章记录的是密码模式和客户端模式

  目录

  引用安装

  授权处理-发放Token

    用户名密码授权

    客户端授权

  身份校验-校验失败自定义返回信息

  TestClient增加token获取

 

  一、引用安装

  除了Owin使用时安装的引用外,还需要安装以下引用  

  Microsoft.Owin.Security.OAuth

  Microsoft.Owin.Security.Cookies

  Microsoft.AspNet.Identity.Owin

 

  二、搭建授权认证

  修改StartUp,添加关键词partial

  在App_Start下新建StartUp.Auth,同样使用关键词partial,添加ConfiguerAuth,代码如下:

  

 1 public partial class StartUp
 2     {
 3         /// <summary>
 4         /// OAuth配置
 5         /// </summary>
 6         /// <param name="app"></param>
 7         public void ConfigureAuth(IAppBuilder app)
 8         {
 9             app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
10             {
11                 AllowInsecureHttp = true,
12                 TokenEndpointPath = new PathString("/api/token"),//授权地址
13                 AccessTokenExpireTimeSpan = TimeSpan.FromHours(24), //过期时间
14                 AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
15                 Provider = new SampleAuthorizationServerProvider() //授权服务
16             });
17         }
18     }

 

  SampleAuthorizationServerProvider是提供授权服务的方法,继承自OAuthAuthorizationServerProvider,包含客户端认证和用户名密码认证,代码如下

 1 /// <summary>
 2     /// 授权服务
 3     /// </summary>
 4     public class SampleAuthorizationServerProvider : OAuthAuthorizationServerProvider
 5     {
 6         #region 客户端授权
 7         /// <summary>
 8         /// 客户端验证
 9         /// </summary>
10         /// <param name="context"></param>
11         /// <returns></returns>
12         public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
13         {
14             string clientId = "";
15             string clientSecret = "";
16             //获取传入的客户端id和客户端校验码
17             context.TryGetFormCredentials(out clientId, out clientSecret);
18             if (clientId == "发放的客户端id" && clientSecret == "发放的客户端校验码")
19             {
20                 context.Validated(clientId);
21             }
22             else
23             {
24                 context.SetError("invalid_client", "client is not valid");
25             }
26             return base.ValidateClientAuthentication(context);
27         }
28 
29         /// <summary>
30         /// 客户端授权-生成access token
31         /// </summary>
32         /// <param name="context"></param>
33         /// <returns></returns>
34         public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
35         {
36             var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
37             oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "app"));//可以继续加一些其它信息
38             var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties() { AllowRefresh = false });
39             context.Validated(ticket);
40             return base.GrantClientCredentials(context);
41         }
42         #endregion
43         
44         #region 用户名密码授权
45         public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
46         {
47             if (context.UserName == "用户名" && context.Password == "用户密码")
48             {
49                 var props = new AuthenticationProperties(new Dictionary<string, string>
50                 {
51                     {
52                         "userName", context.UserName
53                     }
54                 });
55                 var identity = new ClaimsIdentity("userinfo");
56                 identity.AddClaim(new Claim("DisplayName", "张三"));
57                 identity.AddClaim(new Claim("DutyName", "总监"));
58 
59                 var ticket = new AuthenticationTicket(identity, props);
60 
61                 context.Validated(ticket);
62             }
63             else
64             {
65                 context.SetError("invalid_user", "username or password error");
66             }
67             return base.GrantResourceOwnerCredentials(context);
68         }
69         #endregion
70 
71     }

 

  在根目录App_Start中增加  ConfigureAuth(app);

  请求Token(以客户端验证为示例)

 1           $.ajax({
 2                 url: /api/token,
 3                 type: post,
 4                 data: {
 5                     "grant_type": "client_credentials",
 6                     "client_id": $("#clientid").val(),
 7                     "client_secret": $("#clientscuret").val()
 8                 },
 9                 dataType: "json",
10                 success: function (data) {
11                     var accessToken = data.access_token;
12                     console.log("获取到的token:"+accessToken);
13                 }
14             });    

 

 

  三、身份认证与身份认证失败自定义返回

  增加身份认证,只需要在需要使用的controller上增加 特性 [Authorize]

  不需要启用验证的使用特性 [AllowAnonymous]

  认证失败返回401,但为了和我们项目的返回数据结构一致,我们需要在认证失败的时候修改返回,新建过滤器继承AuthorizeAttribute,完事将使用Authorized的地方替换为自定义的过滤器

  

 1 public class CustomAuthorizeAttribute:AuthorizeAttribute
 2     {
 3         protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
 4         {
 5             base.HandleUnauthorizedRequest(actionContext);
 6             actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
 7             {
 8                 Content = new StringContent("项目返回的数据格式数据")
 9             };
10         }
11     }

 

 

  四、TestClient

  为了方便在线测试接口添加token的方便,我们需要给弹出的调用窗口增加authorization的header信息、获取token的操作;并将获取到的token存储到cookie,打开窗口时存在自填充,省去每次的获取操作

  》文件TestClientDialogs文件,在<div class="panel"> 内添加如下代码

  

 1 <div style="margin-bottom:10px;background:#EFEFEF;border:1px solid #ccc;padding:10px">
 2             <div>
 3                 客户端id:
 4                 <input type="text" id="clientid" value="admin" />
 5                 客户端securet:
 6                 <input type="password" id="clinetsecuret" value="admin" />
 7                 <a href=javascript:; id="btngettk">获取 Token</a>
 8             </div>
 9             <div style="padding:5px;background:#ffd800;" id="getmsg"> <span> 正在获取Token,请稍后...</span></div>
10         </div>
11 
12         <hr />

  》WebApiTestClient.js中修改如下(TestClientViewModel方法末尾添加):

 1 //支持弹出窗口自动填充token
 2         var accessToken;
 3         try {
 4             accessToken = $.cookie("token");
 5         } catch (e) {
 6             console.log(e); 
 7         }
 8         addOrReplaceHeader(self.RequestHeaders, "authorization", "Bearer " + accessToken);
 9         //不存在token时点击获取按钮获取token
10         $("#getmsg").hide();
11         $("#btngettk").click(function () {
12             $("#getmsg").show();
13             $.ajax({
14                 url: /api/token,
15                 type: post,
16                 data: {
17                     "grant_type": "client_credentials",
18                     "client_id": $("#txtusername").val(),
19                     "client_secret": $("#txtpwd").val()
20                 },
21                 dataType: "json",
22                 success: function (data) {
23                     var headers = self.RequestHeaders;
24                     var accessToken = data.access_token;
25                     $.cookie("token", accessToken, { path: "/", expires: 1 });
26                     addOrReplaceHeader(headers, "authorization", "Bearer " + accessToken);
27                     $("#getmsg").hide();
28                 }
29             });
30 
31         })

 

  

 本系列使用记录到此,有用到的再补充,有不对的地方希望大家帮忙指正修改,感谢!

 

以上是关于Web API使用记录系列OAuth授权与身份校验的主要内容,如果未能解决你的问题,请参考以下文章

Google OAuth2 服务帐户 API 授权

OIDC-基于OAuth2的下一代身份认证授权协议

WEB API项目实战干货系列- API登录与身份验证

使用 suds 在 Python 中使用 OAuth2 进行 Google Adwords API 身份验证

REST API 授权和身份验证(网络 + 移动)

ASP.NET Web API:返回 401/未授权响应的正确方法