ASP.NET Web API与Owin OAuth:调用与用户相关的Web API

参考页面:

http://www.yuanjiaocheng.net/webapi/web-api-route.html

http://www.yuanjiaocheng.net/webapi/parameter-binding.html

http://www.yuanjiaocheng.net/webapi/action-method-returntype.html

http://www.yuanjiaocheng.net/webapi/web-api-reqresq-format.html

http://www.yuanjiaocheng.net/webapi/media-formatter.html

在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 Web API。

在这篇博文中,我们将以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权。

根据 OAuth 规范,客户端获取 Access Token 的请求方式如下:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

根据上面的请求方式,在 C# 中用 HttpClient 实现一个简单的客户端,代码如下:

public class OAuthClientTest
{
    private HttpClient _httpClient;

    public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    } 

    [Fact]
    public async Task Get_Accesss_Token_By_Resource_Owner_Password_Credentials_Grant()
    {
        Console.WriteLine(await GetAccessToken());
    }

    private async Task<string> GetAccessToken()
    {
        var clientId = "1234";
        var clientSecret = "5678";

        var parameters = new Dictionary<string, string>();
        parameters.Add("grant_type", "password");
        parameters.Add("username", "博客园团队");
        parameters.Add("password", "cnblogs.com");

        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
            "Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret))
            );

        var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
        var responseValue = await response.Content.ReadAsStringAsync();
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return JObject.Parse(responseValue)["access_token"].Value<string>();
        }
        else
        {
            Console.WriteLine(responseValue);
            return string.Empty;
        }
    }
}

(注:与之前相比,这里的 client_id/client_secret 改为了 Basic Authorization,以更好的遵循 OAuth 规范)

在服务端,基于 Owin OAuth, 针对 Resource Owner Password Credentials Grant 的授权方式,只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可。代码如下:

public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    //...

    public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    {
        //调用后台的登录服务验证用户名与密码

        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantResourceOwnerCredentials(context);
    }
}

完整的CNBlogsAuthorizationServerProvider实现代码如下(与之前相比,context.TryGetFormCredentials 改为了 context.TryGetBasicCredentials):

public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;
        context.TryGetBasicCredentials(out clientId, out clientSecret);

        if (clientId == "1234"
            && clientSecret == "5678")
        {
            context.Validated(clientId);
        }

        await base.ValidateClientAuthentication(context);
    }

    public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantClientCredentials(context);
    }

    public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    {
        //调用后台的登录服务验证用户名与密码

        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        await base.GrantResourceOwnerCredentials(context);
    }
}

CNBlogsAuthorizationServerProvider

这样,运行客户端程序就可以拿到 Access Token 了。

接下来,我们拿着以这种方式获取的 Access Token,就可以调用与用户相关的 Web API 了。

在服务端我们通过一个简单的 Web API 测试一下,代码如下:

public class UsersController : ApiController
{
    [Authorize]
    public string GetCurrent()
    {
        return User.Identity.Name;
        //这里可以调用后台用户服务,获取用户相关数所,或者验证用户权限进行相应的操作
    }
}

然后,客户端用以 grant_type=password 方式拿到的 Access Token 调用这个Web API,客户端增加的代码如下:

[Fact]
public async Task Call_WebAPI_By_Resource_Owner_Password_Credentials_Grant()
{
    var token = await GetAccessToken();
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    Console.WriteLine(await (await _httpClient.GetAsync("/api/users/current")).Content.ReadAsStringAsync());
}

客户端运行结果如下:

"博客园团队"

调用成功!运行结果正是获取 Access Token 时所用的 username 。

结合 ASP.NET 现有的安全机制,借助 OWIN 的威力,Microsoft.Owin.Security.OAuth 的确让开发基于 OAuth 的 Web API 变得更简单。

时间: 2024-10-09 05:30:49

ASP.NET Web API与Owin OAuth:调用与用户相关的Web API的相关文章

ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API

在前一篇博文中,我们使用OAuth的Client Credential Grant授权方式,在服务端通过CNBlogsAuthorizationServerProvider(Authorization Server的一个实现)成功发放了Access Token,并在客户端成功拿到了Access Token. 那Access Token有什么用呢?在OAuth中对Resource Server(比如Web API)访问权限的验证都是基于Access Token.不管是什么样的客户端来调用,Reso

在ASP.NET Web API 2中使用Owin OAuth 刷新令牌

在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Api中启用刷新令牌的后端实现. 本文要用到上篇文章所使用的代码,代码编写环境为VS 2017..Net Framework 4.7.2,数据库为MS SQL 2008 R2. OAuth 刷新令牌 上文已经搞了一套Token授权访问,这里有多出来一个刷新令牌(Refresh Token),平白添加程序

在WebApi中基于Owin OAuth使用授权发放Token

如何基于Microsoft.Owin.Security.OAuth,使用Client CredentialsGrant授权方式给客户端发放access token? Client CredentialsGrant的授权方式就是只验证客户端(Client),不验证用户(Resource Owner),只要客户端通过验证就发accesstoken.举一个对应的应用场景例子,比如我们想提供一个“获取网站首页最新博文列表”的WebAPI给客户端App调用.由于这个数据与用户无关,所以不涉及用户登录与授权

IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API

IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习之保护API. 使用IdentityServer4 来实现使用客户端凭据保护ASP.NET Core Web API 访问. IdentityServer4 GitHub: https://github.com/IdentityServer/IdentityServer4 IdentityServer 框架支持以下功能: 身份验证服务所有应用程序(Web,本机,移动,服务)的集中登录

[ASP.NET] 结合Web API在OWIN下实现OAuth

OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAuth是安全的. 本节目录: Owin下WebAPI SelfHost 创建AccessToken 使用AccessToken Owin下WebAPI SelfHost 1.创建一个控制台项目(其实类库都可以)ApiServe

Web API在OWIN下实现OAuth

Web API在OWIN下实现OAuth OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAuth是安全的. 本节目录: Owin下WebAPI SelfHost 创建AccessToken 使用AccessToken Owin下WebAPI SelfHost 1.创建一个控

为IIS Host ASP.NET Web Api添加Owin Middleware

将OWIN App部署在IIS上 要想将Owin App部署在IIS上,只添加Package:Microsoft.OWIN.Host.SystemWeb包即可.它提供了所有Owin配置,Middleware注册等方面的Api.我们需要做的其实和SelfHost差不多. 我们依然需要实现Startup类,但是不是通过WebApp来启动了.我们需要通过将Startup类打上[assembly: OwinStartup(typeof(Startup))]来定义这是OWIN的Startup类,当应用运行

ASP.NET OWIN OAuth:refresh token的持久化

在前一篇博文中,我们初步地了解了refresh token的用途——它是用于刷新access token的一种token,并且用简单的示例代码体验了一下获取refresh token并且用它刷新access token.在这篇博文中,我们来进一步探索refresh token. 之前只知道refresh token是用于刷新access token的,却不知道refresh token凭什么可以刷新access token?知其然,却不知其所以然. 这是由于之前没有发现refresh token

ASP.NET OWIN OAuth:遇到的2个refresh token问题

之前写过2篇关于refresh token的生成与持久化的博文:1)Web API与OAuth:既生access token,何生refresh token:2)ASP.NET OWIN OAuth:refresh token的持久化. 之后我们在CNBlogsRefreshTokenProvider中这样实现了refresh token的生成与持久化: public class CNBlogsRefreshTokenProvider : AuthenticationTokenProvider