webapi基于Microsoft.Owin.Security.OAuth的OAuth实现

版权声明:本文为马立弘原创文章,欢迎引用,谢绝转载。 https://blog.csdn.net/manimanihome/article/details/53457801

webapi基于Microsoft.Owin.Security.OAuth的OAuth实现

一、在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token和调用webapi

建一个Web API项目
打开Startup.Auth.cs ,精简一下代码,我们只需要实现以Client Credentials Grant授权方式拿到token,其它无关代码全部清除,最终剩下如下代码:

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        var OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new CNBlogsAuthorizationServerProvider(),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

        app.UseOAuthBearerTokens(OAuthOptions);
    }
}


创建一个新的类 CNBlogsAuthorizationServerProvider,并继承自 OAuthAuthorizationServerProvider,重载 OAuthAuthorizationServerProvider() 与 GrantClientCredentials() 这两个方法。代码如下:


public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{

    //获取客户端的 client_id 与 client_secret 进行验证
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        string clientId;
        string clientSecret;

    //建议使用 TryGetBasicCredentials
        context.TryGetBasicCredentials(out clientId, out clientSecret);

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

        return base.ValidateClientAuthentication(context);
    }

    //对客户端进行授权
    public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    {
        var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "iOS App"));
        var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket);

        return base.GrantClientCredentials(context);
    }
}

[Authorize]
public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

注:
WebApiConfig.cs中的如下代码修改了[Authorize]的作用:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));



客户端调用获得access_token测试(不用账号密码):
public class OAuthClientTest
{
    private HttpClient _httpClient;

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

    [Fact]
    public void Get_Accesss_Token_By_Client_Credentials_Grant()
    {
        var clientId =  "1234";
        var clientSecret = "5678";        
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Basic",
                Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));

        var parameters = new Dictionary<string, string>();        
        parameters.Add("grant_type", "client_credentials");       

        Console.WriteLine(_httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters))
            .Result.Content.ReadAsStringAsync().Result);
    }
}

//拿到结果
{"access_token":"8PqaWilv_SJT7vRXambP7Mebyaf3KO1GXYHsqA-oPMOQF6xk1YpluczOZGo-WwATU5YmGb0wSR0cUQMC8RSZfwO8nwom7yG11FIANhy2PNiqTg2CYdJF0sf0ggFs6it_i3mc_m1iEFCK2dLBPDJXPI24wngCPR0wP_zugZvyKv314BM0PQmnnwg3kLXR1DISKRbs5-i59VCtFSZgkM7A0w","token_type":"bearer","expires_in":1209599}


客户端使用Access Toke调用受保护的webapi(不用账号密码)


客户端使用Access Token调用webapi:只要在http请求头中加上Bearer:Token即可
如果不使用Access Token,调用API时就会出现如下的错误:
{"Message":"Authorization has been denied for this request."}

public class OAuthClientTest
    {
        private HttpClient _httpClient;

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

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


        private async Task<string> GetAccessToken()
        {
            var parameters = new Dictionary<string, string>();
            parameters.Add("client_id", "1234");
            parameters.Add("client_secret", "5678");
            parameters.Add("grant_type", "client_credentials");

            var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
            var responseValue = await response.Content.ReadAsStringAsync();                

            return JObject.Parse(responseValue)["access_token"].Value<string>();
        }


    }

//得到结果
["value1","value2"]



二、调用与用户相关的Web API
以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。
只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可

1.服务端代码
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);
    }
}


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


2.客户端代码:
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());
    }

//客户端调用webapi:
[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());
}


    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", "admin");
        parameters.Add("password", "mypassword");

        _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;
        }
    }
}


参考:
http://www.cnblogs.com/dudu/p/4578511.html


猜你喜欢

转载自blog.csdn.net/manimanihome/article/details/53457801