热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

OAuth2.0介绍和使用

一、OAuth2.0OAuth2.0是一个应用之间彼此访问数据的开源授权协议。比如,一个游戏应用可以访问Facebook的用户数据或者一个基于地理的应用可以访问Foursquare

一、OAuth 2.0

OAuth 2.0是一个应用之间彼此访问数据的开源授权协议。比如,一个游戏应用可以访问Facebook的用户数据或者一个基于地理的应用可以访问Foursquare的用户数据等。下面是一张阐述该概念的图:

用户访问web游戏应用,该游戏应用要求用户通过Facebook登录。用户登录到了Facebook,再重定向会游戏应用, 游戏应用就可以访问用户在Facebook的数据了,并且该应用可以代表用户向Facebook调用函数(如发送状态更新)。

下图说明了OAuth2.0整个授权过程:



  • 用户访问客户端web应用。应用中的按钮”通过Facebook登录”(或者其他的系统,如Google或Twitter)。

  • 当用户点击了按钮后,会被重定向到授权的应用(如Facebook)。用户登录并确认授权应用中的数据给客户端应用。

  • 授权应用将用户重定向到客户端应用提供的URI,提供这种重定向的URI通常是通过注册客户端应用程序与授权应用程序完成。在注册中,客户端应用的拥有者组注册该重定向URI,在注册过程中认证应用也会给客户端应用客户端标识和密码。在URI后追加一个认证码。该认证码代表了授权。

  • 用户在客户端应用访问网页被定位到重定向的URI。在背后客户端应用连接授权应用,并且发送在重定向请求参数中接收到的客户端标识,客户端密码和认证码。授权应用将返回一个访问口令。

  • 一旦客户端有了访问口令,该口令便可以被发送到Facebook、Google、Twitter等来访问登录用户的资源。

OAuth 2.0为用户和应用定义了如下角色,这些角色在下图中表示为:



  • 资源拥有者

  • 资源服务器

  • 客户端应用

  • 授权服务器

 

更详细的介绍可参考:https://www.w3cschool.cn/oauth2/5yej1ja2.html。


二、OWIN实现OAuth 2.0 之客户端模式

1、原理

客户端使用自己的名义,而不是用户的名义,向“服务提供商” 进行认证。如下图展示了整个流程:

可以得出一个大概的结论



  1. 用户(User)通过客户端(Client)访问受限资源(Resource)

  2. 因为资源受限,所以需要授权;而这个授权是Client与Authentication之间完成的,可以说跟User没有什么关系

  3. 根据2得出,Resource与User没有关联关系,即User不是这个Resource的Owner(所有者)


2、过程



  • Client网站向认证服务网站发出请求。


https://xx.com:8081/grant_type=client_credentials&client_id=ClientCredentials&client_secret=secret

  上面 URL 中,grant_type参数等于client_credentials表示采用凭证式,client_idclient_secret用来让认证服务确认Client的身份。



  • 认证服务网站验证通过以后,直接返回令牌。这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。

  • Client网站拿到令牌以后,就可以向资源服务网站(资源服务网站和认证服务网站可以是一个)的 API 请求数据。此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。


curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://xx:8008/api/Values"

  上面命令中,ACCESS_TOKEN就是拿到的令牌。


3、适应场景



  • 不太适合用作登录认证!因为登录认证后需要得到用户的一些基本信息,如昵称,头像之类,这些信息是属于User的;

  • 适用于一些对于权限要求不强的资源认证,比如:仅用于区分用户是否登录,排除匿名用户获取资源


4、示例(.net framework)


(1)新建API资源项目:ResourceService

从nuget中引入下面组件:注意以下六个组件都要安装。

新增Startup.cs

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(
typeof(ResourceService.Startup))]
namespace ResourceService
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

新增Startup.Auth.cs

using Owin;
namespace ResourceService
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// 这句是资源服务器认证token的关键,认证逻辑在里边封装好了,我们看不到
app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions());
}
}
}

新增ValuesController.cs

using System.Web.Http;
namespace ResourceService.Controllers
{
[Authorize]
public class ValuesController : ApiController
{
public string Get()
{
return "qiuxainhu";
}
}
}

web.config配置文件中添加machineKey

  这个节允许你设置用于加密数据和创建数字签名的服务器特定的密钥,ASP.NET会自动使用它。比如在分布式集群项目中,对页面的请求由一台计算机处理,而页面回发又由另一台计算机处理,第二个服务器就不能解 密来自第一台服务器的视图状态和表单COOKIE。这个问题之所以会发生,是因为两台服务器使用了不同的密钥。可以通过配置相同的 解决。具体可以看machineKey的介绍https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff649308(v=pandp.10)?redirectedfrom=MSDN


(2)新建认证服务项目:ClientCredentialService

引入以下组件:注意以下五个组件都要引用

新增Startup.cs

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(
typeof(ClientCredentialService.Startup))]
namespace ClientCredentialService
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

新增Startup.Auth.cs

using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
namespace ClientCredentialService
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
//创建OAuth授权服务器
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
TokenEndpointPath
= new PathString("/Token"),//access_token 授权服务请求地址,即http://localhost:端口号/token;
ApplicatiOnCanDisplayErrors= true,
AccessTokenExpireTimeSpan
= TimeSpan.FromDays(10),//access_token 过期时间
#if DEBUG
AllowInsecureHttp
= true,
#endif
// Authorization server provider which controls the lifecycle of Authorization Server
Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientAuthentication
= ValidateClientAuthentication,
OnGrantClientCredentials
= GrantClientCredetails
}
});
}
///


/// ValidateClientAuthentication方法用来对third party application 认证,
/// 获取客户端的 client_id 与 client_secret 进行验证
/// context.Validated(); 表示允许此third party application请求。
///

///


///
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = null;
string clientSecret = null;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
context.TryGetFormCredentials(
out clientId, out clientSecret))
{
if (clientId == "123456" && clientSecret == "abcdef")
{
context.Validated();
}
}
return Task.FromResult(0);
}
///


/// 该方法是对客户端模式进行授权的时候使用的
/// 对客户端进行授权,授了权就能发 access token 。
/// 只有这两个方法(ValidateClientAuthentication和GrantClientCredetails)同时认证通过才会颁发token。
private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
{
GenericIdentity genericIdentity
= new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType);
ClaimsIdentity claimsIdentity
= new ClaimsIdentity(genericIdentity, context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
context.Validated(claimsIdentity);
return Task.FromResult(0);
}
}
}

web.config配置文件中添加machineKey

自此,认证服务项目算是建好了,因为对于客户端模式,认证服务器只需要返回token


(3)新增Client项目

新增一个控制台项目,用于测试,如下: 

新建一个帮助类ConfigSetting,用于读取配置文件:

using System;
using System.Configuration;
namespace ConfigHelper
{
public class ConfigSetting
{
public static readonly string TOKENPATH = GetAppSettingValue("TOKENPATH");
public static readonly string OAUTH_SERVER_URL=GetAppSettingValue("OAUTH_SERVER_URL");
public static readonly string OAUTH_TOKEN_PATH = GetAppSettingValue("OAUTH_TOKEN_PATH");
public static readonly string CLIENTID = GetAppSettingValue("CLIENTID");
public static readonly string SECRET = GetAppSettingValue("SECRET");
public static readonly string RESOURCE_SERVER_URL = GetAppSettingValue("RESOURCE_SERVER_URL");
public static readonly string RESOURCE_ME_PATH = GetAppSettingValue("RESOURCE_ME_PATH");
private static string GetAppSettingValue(string key)
{
string value = null;
foreach (string item in ConfigurationManager.AppSettings)
{
if (item.Equals(key, StringComparison.CurrentCultureIgnoreCase))
{
value
= ConfigurationManager.AppSettings[item];
break;
}
}
return value;
}
}
}

编辑app.config配置文件:


"OAUTH_SERVER_URL" value="http://localhost:8061/" />
"OAUTH_TOKEN_PATH" value="/Token" />
"RESOURCE_SERVER_URL" value="http://localhost:8062/" />
"RESOURCE_ME_PATH" value="/api/Values" />
"CLIENTID" value="123456" />
"SECRET" value="abcdef" />

修改program.cs,这里使用方式一HttpClient 来做请求:

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace ClientConsole
{
class Program
{
private static string OAUTH_SERVER_URL = ConfigSetting.OAUTH_SERVER_URL;
private static string OAUTH_TOKEN_PATH = ConfigSetting.OAUTH_TOKEN_PATH;
private static string RESOURCE_SERVER_URL = ConfigSetting.RESOURCE_SERVER_URL;
private static string RESOURCE_ME_PATH = ConfigSetting.RESOURCE_ME_PATH;
private static readonly string ClientID = ConfigSetting.CLIENTID;
private static readonly string Secret = ConfigSetting.SECRET;
private static string _AccessToken;
static void Main(string[] args)
{
HttpClient _httpClient
= new HttpClient();
Dictionary
<string, string> parameters = new Dictionary<string, string>();
parameters.Add(
"grant_type", "client_credentials");
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(ClientID
+ ":" + Secret))
);
var respOnse= _httpClient.PostAsync(new Uri(new Uri(OAUTH_SERVER_URL), OAUTH_TOKEN_PATH), new FormUrlEncodedContent(parameters)).Result;
var respOnseValue= response.Content.ReadAsStringAsync().Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
_AccessToken
= JObject.Parse(responseValue)["access_token"].Value<string>();
}
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", _AccessToken);
Console.WriteLine(_httpClient.GetAsync(
new Uri(new Uri(RESOURCE_SERVER_URL), RESOURCE_ME_PATH)).Result.Content.ReadAsStringAsync().Result);
Console.ReadKey();
Console.ReadKey();
}
}
}

看下运行效果:

修改program.cs,使用方式二DotNetOpenAuth.OAuth2来请求

using DotNetOpenAuth.OAuth2;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ClientConsole
{
class Program
{
private static string OAUTH_SERVER_URL = ConfigSetting.OAUTH_SERVER_URL;
private static string OAUTH_TOKEN_PATH = ConfigSetting.OAUTH_TOKEN_PATH;
private static string RESOURCE_SERVER_URL = ConfigSetting.RESOURCE_SERVER_URL;
private static string RESOURCE_ME_PATH = ConfigSetting.RESOURCE_ME_PATH;
private static readonly string ClientID = ConfigSetting.CLIENTID;
private static readonly string Secret = ConfigSetting.SECRET;
private static WebServerClient _WebServerClient;
private static string _AccessToken;
static void Main(string[] args)
{
InitializeWebServerClient();
Console.WriteLine(
"Requesting Token...");
RequestToken();
Console.WriteLine(
"Access Token: {0}", _AccessToken);
Console.WriteLine(
"Access Protected Resource");
AccessProtectedResource();
Console.ReadKey();
}
private static void InitializeWebServerClient()
{
Uri authorizationServerUri
= new Uri(OAUTH_SERVER_URL);
AuthorizationServerDescription authorizationServer
= new AuthorizationServerDescription
{
TokenEndpoint
= new Uri(authorizationServerUri, OAUTH_TOKEN_PATH)
};
_WebServerClient
= new WebServerClient(authorizationServer, ClientID, Secret);
}
private static void RequestToken()
{
IAuthorizationState state
= _WebServerClient.GetClientAccessToken();
Console.WriteLine(state);
_AccessToken
= state.AccessToken;
}
private static void AccessProtectedResource()
{
Uri resourceServerUri
= new Uri(RESOURCE_SERVER_URL);
HttpClient client
= new HttpClient(_WebServerClient.CreateAuthorizingHandler(_AccessToken));
string body = client.GetStringAsync(new Uri(resourceServerUri, RESOURCE_ME_PATH)).Result;
Console.WriteLine(body);
}
}
}

使用DotNetOpenAuth.OAuth2需要加下配置文件:

"1.0" encoding="utf-8" ?>


"
dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth.Core">

"messaging" type="DotNetOpenAuth.Configuration.MessagingElement, DotNetOpenAuth.Core" requirePermission="false" allowLocation="true" />
"reporting" type="DotNetOpenAuth.Configuration.ReportingElement, DotNetOpenAuth.Core" requirePermission="false" allowLocation="true" />



"true"/>


"OAUTH_SERVER_URL" value="http://localhost:8061/" />
"OAUTH_TOKEN_PATH" value="/Token" />
"RESOURCE_SERVER_URL" value="http://localhost:8062/" />
"RESOURCE_ME_PATH" value="/api/Values" />
"CLIENTID" value="123456" />
"SECRET" value="abcdef" />


"v4.0" sku=".NETFramework,Version=v4.7.2" />

看下运行效果:


三、OWIN实现OAuth 2.0 之密码模式

1、原理

用户向客户端提供用户名和密码,客户端使用这些信息向认证服务进行认证,密码模式的流程图:


2、过程

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。



  • A 网站要求用户提供 B 网站的用户名和密码。拿到以后,A 就直接向 B 请求令牌。


https://xx.com/token?grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID

  上面 URL 中,grant_type参数是授权方式,这里的password表示"密码式",usernamepassword是 B 的用户名和密码。



  • B 网站验证身份通过后,直接给出令牌。注意,这时不需要跳转,而是把令牌放在 JSON 数据里面,作为 HTTP 回应,A 因此拿到令牌。这种方式需要用户给出自己的用户名/密码,显然风险很大,因此只适用于其他授权方式都无法采用的情况,而且必须是用户高度信任的应用。


3、示例


(1)认证服务

引入下面组件:

新建Startup.cs文件

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(
typeof(PasswordService.Startup))]
namespace PasswordService
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

新建Startup.Auth.cs文件

using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Security.Claims;
using System.Threading.Tasks;
namespace PasswordService
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
//创建OAuth授权服务器
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
TokenEndpointPath
= new PathString("/Token"),//获取 access_token 授权服务请求地址,即http://localhost:端口号/token;
ApplicatiOnCanDisplayErrors= true,
AccessTokenExpireTimeSpan
= TimeSpan.FromDays(10),//access_token 过期时间
#if DEBUG
AllowInsecureHttp
= true,
#endif
// Authorization server provider which controls the lifecycle of Authorization Server
Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientAuthentication
= ValidateClientAuthentication,
//这个方法就是后台处理密码模式认证关键的地方
OnGrantResourceOwnerCredentials= GrantResourceOwnerCredentials
}
});
}
///


/// ValidateClientAuthentication方法用来对third party application 认证,
/// 获取客户端的 client_id 与 client_secret 进行验证
/// context.Validated(); 表示允许此third party application请求。
///

///


///
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = null;
string clientSecret = null;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
context.TryGetFormCredentials(
out clientId, out clientSecret))
{
if (clientId == "123456" && clientSecret == "abcdef")
{
context.Validated();
}
}
return Task.FromResult(0);
}
///


/// 这个方法就是后台处理密码模式认证关键的地方
/// 认证服务判断查询数据库判断该用户的用户名和密码是否正确。如果正确就会授权,产生token。
///

///


///
private async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName != "qiuxianhu" || context.Password != "123456")
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
else
{
ClaimsIdentity claimsIdentity
= new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(
new Claim(ClaimTypes.Name, context.UserName));
var ticket = new AuthenticationTicket(claimsIdentity, new AuthenticationProperties());
context.Validated(ticket);
}
await Task.CompletedTask;
}
}
}

web.config配置文件中添加machineKey


(2)资源服务

我们沿用客户端凭证模式中的资源服务。


(3)Client

我们沿用客户端凭证模式中的客户端,这里简单修改下代码:

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace Client
{
class Program
{
static void Main(string[] args)
{
string server_url = "http://localhost:8063/";//认证服务地址
string resource_url = "http://localhost:8062/";//资源服务地址
string clientid = "123456";
string secret = "abcdef";
HttpClient _httpClient
= new HttpClient();
Dictionary
<string, string> parameters = new Dictionary<string, string>();
parameters.Add(
"grant_type", "password");
parameters.Add(
"UserName", "qiuxianhu");
parameters.Add(
"Password", "123456");
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(clientid
+ ":" + secret))
);
var respOnse= _httpClient.PostAsync(new Uri(new Uri(server_url), "/Token"), new FormUrlEncodedContent(parameters)).Result;
var respOnseValue= response.Content.ReadAsStringAsync().Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string token = JObject.Parse(responseValue)["access_token"].Value<string>();
Console.WriteLine(token);
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", token);
Console.WriteLine(_httpClient.GetAsync(
new Uri(new Uri(resource_url), "/api/values")).Result.Content.ReadAsStringAsync().Result);
}
Console.ReadKey();
}
}
}

效果如下:


四、OWIN实现OAuth 2.0 之授权码模式

1、原理

通过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。



  1. 用户访问客户端,后者将前者导向认证服务器。

  2. 用户选择是否给予客户端授权。

  3. 假设用户给予授权,认证服务器首先生成一个授权码,并返回给用户,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

  4. 客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

  5. 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

  6. Client拿着access token去访问Resource资源


2、示例


(1)认证服务

引入以下组件:

新建Startup.cs

using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(
typeof(CodeService.Startup))]
namespace CodeService
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

新增Startup.Auth.cs

using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.COOKIEs;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace CodeService
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// 使应用程序可以使用 COOKIE 来存储已登录用户的信息
// 并使用 COOKIE 来临时存储有关使用第三方登录提供程序登录的用户的信息
// 配置登录 COOKIE
app.UseCOOKIEAuthentication(new COOKIEAuthenticationOptions
{
AuthenticationType
= DefaultAuthenticationTypes.ApplicationCOOKIE,
AuthenticationMode
= AuthenticationMode.Passive,
LoginPath
= new PathString("/Account/Login"),
LogoutPath
= new PathString("/Account/LoginOut"),
});
// Setup Authorization Server
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath
= new PathString("/OAuth/Authorize"),// AuthorizeEndpointPath是授权终结点,这个需要自己去写实现逻辑
TokenEndpointPath = new PathString("/OAuth/Token"),//TokenEndpointPath是生成Token的终结点,这个不需要我们写额外的逻辑了,因为Token的生成涉及到很多方面,例如序列化反序列加密解密等逻辑,所以框架默认已经帮我们做好了。
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
ApplicationCanDisplayErrors
= true,
#if DEBUG
AllowInsecureHttp
= true,//重要!!AllowInsecureHttp设置整个通信环境是否启用ssl,不仅是OAuth服务端,也包含Client端(当设置为false时,若登记的Client端重定向url未采用https,则不重定向)。
#endif
// Provider里面是用来验证客户端跳转地址和客户端验证,正确的做法应该是先判断客户端发送过来的ClientID是否合法,如果合法则验证通过。
Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri
= ValidateClientRedirectUri,
OnValidateClientAuthentication
= ValidateClientAuthentication
},
// Authorization code provider which creates and receives authorization code
AuthorizatiOnCodeProvider= new AuthenticationTokenProvider
{
OnCreate
= CreateAuthenticationCode,
OnReceive
= ReceiveAuthenticationCode,
},
// Refresh token provider which creates and receives referesh token
RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate
= CreateRefreshToken,
OnReceive
= ReceiveRefreshToken,
}
});
}
///


/// 客户端发送到授权终结点的请求是会被OWIN截取跳转到注册的 OnValidateClientRedirectUri委托,
/// 客户端发送授权请求代码:
/// var userAuthorization = _webServerClient.PrepareRequestUserAuthorization();
/// userAuthorization.Send(HttpContext);
/// Response.End();
///

///


///
private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
context.Validated();
return Task.FromResult(0);
}
///


/// TryGetBasicCredentials:是指Client可以按照Basic身份验证的规则提交ClientId和ClientSecret
/// TryGetFormCredentials:是指Client可以把ClientId和ClientSecret放在Post请求的form表单中提交
///

///


///
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
context.TryGetFormCredentials(
out clientId, out clientSecret))
{
if (clientId == "123456" && clientSecret == "abcdef")
{
context.Validated();
}
}
return Task.FromResult(0);
}
private readonly ConcurrentDictionary<string, string> _authenticatiOnCodes=
new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
{
context.SetToken(Guid.NewGuid().ToString(
"n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token]
= context.SerializeTicket();
}
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
string value;
if (_authenticationCodes.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);
}
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}
}
}

添加OAuthController.cs代码如下:

using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2;
using System;
using System.Net.Http;
using System.Web.Mvc;
namespace ClientMvc.Controllers
{
public class HomeController : Controller
{
static string accessToken = null;
static string refreshToken = null;
public ActionResult Index()
{
Uri authorizatiOnServerUri= new Uri("http://localhost:8064/");//认证服务
AuthorizationServerDescription authorizatiOnServerDescription= new AuthorizationServerDescription
{
AuthorizatiOnEndpoint= new Uri(authorizationServerUri, "OAuth/Authorize"),
TokenEndpoint = new Uri(authorizationServerUri, "OAuth/Token")
};
WebServerClient webServerClient = new WebServerClient(authorizationServerDescription, "123456", "abcdef");
if (string.IsNullOrEmpty(accessToken))
{
IAuthorizationState authorizatiOnState= webServerClient.ProcessUserAuthorization(Request);
if (authorizationState != null)
{
accessToken = authorizationState.AccessToken;
refreshToken = authorizationState.RefreshToken;
}
}
// 授权申请
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestAuthorize")))
{
//这里 new[] { "scopes1", "scopes2" }为需要申请的scopes,或者说是Resource Server的接口标识,或者说是接口权限。然后Send(HttpContext)即重定向。
OutgoingWebResponse grantRequest = webServerClient.PrepareRequestUserAuthorization(new[] { "scopes1", "scopes2" });
grantRequest.Send(HttpContext);
Response.End();
}
// 申请资源
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestResource")))
{
var resourceServerUri = new Uri("http://localhost:8062/");//资源服务
var resourceRequest = new HttpClient(webServerClient.CreateAuthorizingHandler(accessToken));
ViewBag.ResourceRespOnse= resourceRequest.GetStringAsync(new Uri(resourceServerUri, "api/Values")).Result;
}
return View();
}
}
}

添加Authorize.cshtml,代码如下:







认证页面

登录用户:@ViewBag.IdentityName

第三方应用需要你给他开放以下权限



    @foreach (var scope in ViewBag.Scopes)
    {
  • @scope

  • }







添加AccountController.cs,代码如下:

using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
namespace CodeService.Controllers
{
public class AccountController : Controller
{
public ActionResult Login()
{
if (Request.HttpMethod == "POST")
{
IAuthenticationManager authentication = HttpContext.GetOwinContext().Authentication;
// 默认用户登录成功,生产环境需要单独整合第三方登录信息
var username = Request.Form["username"];
var password = Request.Form["password"];
if (username == "qiuxianhu" && password == "123456")
{
authentication.SignIn(
new AuthenticationProperties { IsPersistent = true },
new ClaimsIdentity(new[] { new Claim(ClaimsIdentity.DefaultNameClaimType, username) }, DefaultAuthenticationTypes.ApplicationCOOKIE)
);
var ticket = authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCOOKIE).Result;
}
}
return this.View();
}
public ActionResult Logout()
{
IAuthenticationManager authenticatiOnManager= HttpContext.GetOwinContext().Authentication;
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCOOKIE);
return View();
}
}
}

添加Login.cshtml,代码如下:

@{
ViewBag.Title = "Login";
}




添加Logout.cshtml,代码如下:

@{
ViewBag.Title = "Logout";
}

Logout

web.config配置文件中添加machineKey


(2)资源服务  

我们沿用上边的资源服务器


(3)客户端

 新建一个mvc项目:

添加HomeController,代码如下:

using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2;
using System;
using System.Net.Http;
using System.Web.Mvc;
namespace ClientMvc.Controllers
{
public class HomeController : Controller
{
static string accessToken = null;
static string refreshToken = null;
public ActionResult Index()
{
Uri authorizationServerUri
= new Uri("http://localhost:8064/");//认证服务
AuthorizationServerDescription authorizatiOnServerDescription= new AuthorizationServerDescription
{
AuthorizationEndpoint
= new Uri(authorizationServerUri, "OAuth/Authorize"),
TokenEndpoint
= new Uri(authorizationServerUri, "OAuth/Token")
};
WebServerClient webServerClient
= new WebServerClient(authorizationServerDescription, "123456", "abcdef");
if (string.IsNullOrEmpty(accessToken))
{
IAuthorizationState authorizationState
= webServerClient.ProcessUserAuthorization(Request);
if (authorizationState != null)
{
accessToken
= authorizationState.AccessToken;
refreshToken
= authorizationState.RefreshToken;
}
}
// 授权申请
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestAuthorize")))
{
//这里 new[] { "scopes1", "scopes2" }为需要申请的scopes,或者说是Resource Server的接口标识,或者说是接口权限。然后Send(HttpContext)即重定向。
OutgoingWebResponse grantRequest = webServerClient.PrepareRequestUserAuthorization(new[] { "scopes1", "scopes2" });
grantRequest.Send(HttpContext);
Response.End();
}
// 申请资源
if (!string.IsNullOrEmpty(Request.Form.Get("btnRequestResource")))
{
var resourceServerUri = new Uri("http://localhost:8062/");//资源服务
var resourceRequest = new HttpClient(webServerClient.CreateAuthorizingHandler(accessToken));
ViewBag.ResourceResponse
= resourceRequest.GetStringAsync(new Uri(resourceServerUri, "api/Values")).Result;
}
return View();
}
}
}

Index.cshtml代码如下:


"http://www.w3.org/1999/xhtml">




"form1" method="POST">


"Authorize" name="btnRequestAuthorize" value="向认证服务器索要授权" type="submit" />
"Resource" name="btnRequestResource" value="访问资源(Resource)" type="submit" />



运行ClientMvc进行测试,发生如下错误:

 

解决方法:配置文件中加入如下节点:

运行效果如下: 

 


原文链接:https://www.cnblogs.com/qtiger/p/14871887.html



推荐阅读
  • 大数据领域的职业路径与角色解析
    本文将深入探讨大数据领域的各种职业和工作角色,帮助读者全面了解大数据行业的需求、市场趋势,以及从入门到高级专业人士的职业发展路径。文章还将详细介绍不同公司对大数据人才的需求,并解析各岗位的具体职责、所需技能和经验。 ... [详细]
  • 本文介绍了实现人工智能的多种方法,并重点探讨了当前最热门的技术——通过深度学习训练神经网络。文章通过具体实例详细解释了神经网络的基本原理及其应用。 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 在2019中国国际智能产业博览会上,百度董事长兼CEO李彦宏强调,人工智能应务实推进其在各行业的应用。随后,在“ABC SUMMIT 2019百度云智峰会”上,百度展示了通过“云+AI”推动AI工业化和产业智能化的最新成果。 ... [详细]
  • Facebook开发先进AI系统,深入解析个人生活视角
    Facebook正大力投资于增强现实技术,与Ray-Ban合作开发AR眼镜。目前,这些设备主要用于图像的记录和分享,但公司展望未来,认为这些装置将具备更广泛的功能。一个由研究人员组成的团队正在开发先进的AI系统,旨在深入解析用户的个人生活视角,从而提供更加个性化的体验和服务。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • PostgreSQL 与 MySQL 的主要差异及应用场景分析
    本文详细探讨了 PostgreSQL 和 MySQL 在架构、性能、功能以及适用场景方面的关键差异。通过对比分析,帮助读者更好地理解两种数据库系统的特性和优势,为实际应用中的选择提供参考。 ... [详细]
  • 巴比特 | 每日元宇宙精选:高端VR设备年销量突破千万,行业或将迎来转折点? ... [详细]
  • 题目要求将数字字符串转换为对应的字母组合,例如“111”可以转化为“AAA”、“KA”或“AK”。本文通过深入解析暴力递归方法,详细探讨了这一问题的解法,并结合真实的 Facebook 面试题目,提供了从左至右尝试模型的具体实现和优化策略。 ... [详细]
  • 本文深入探讨了 hCalendar 微格式在事件与时间、地点相关活动标记中的应用。作为微格式系列文章的第四篇,前文已分别介绍了 rel 属性用于定义链接关系、XFN 微格式增强链接的人际关系描述以及 hCard 微格式对个人和组织信息的描述。本次将重点解析 hCalendar 如何通过结构化数据标记,提高事件信息的可读性和互操作性。 ... [详细]
  • 提升开发技能的八大策略与方法
    许多前端开发人员和客户都在寻求具备创新和技术能力的专业人才,但往往由于缺乏足够的曝光度和声誉,这些人才难以被潜在客户发现。本文将介绍八种有效策略和方法,帮助开发者提升技能并增强市场竞争力。 ... [详细]
  • TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得
    TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得 ... [详细]
  • 本文源自极分享,详细内容请参阅原文。技术债务如同信用卡负债,随着时间推移,修复成本会越来越高,因此程序员必须对此有深刻认识。此外,团队应致力于培养一种持续维护和优化代码的文化,以减少技术债务的累积。 ... [详细]
  • 作为140字符的开创者,Twitter看似简单却异常复杂。其简洁之处在于仅用140个字符就能实现信息的高效传播,甚至在多次全球性事件中超越传统媒体的速度。然而,为了支持2亿用户的高效使用,其背后的技术架构和系统设计则极为复杂,涉及高并发处理、数据存储和实时传输等多个技术挑战。 ... [详细]
  • 利用 Python 实现 Facebook 账号登录功能 ... [详细]
author-avatar
wInnIe小店
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有