概念
OAuth2四种模式
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
授权码模式
QQ,微信,等方式
- 用户访问客户端,后者将前者导向认证服务器。
- 用户选择是否给予客户端授权。
- 假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。
- 客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
- 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
请求
response_type:表示授权类型,必选项,此处的值固定为"code"
client_id:表示客户端的ID,必选项
redirect_uri:表示重定向URI,可选项
scope:表示申请的权限范围,可选项
state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
客户端模式
对于这种方式,试用在 访问一些和用户无关的Open Api,比如一些首页数据,这些数据和用户无关,但是又不想任何人都可以调用这个WebApi,那么就可以采用这种模式。
这个流程很像账号密码模式,但是它认证的是客户端身份,而不是用户
- 客户端向认证服务器进行身份认证,并且要求一个访问令牌。
- 认证服务器认证无误后,向客户端提供访问令牌。
密码模式
这种模式是很清晰的.就跟传统的验证一样.
- 用户(确切的说是客户端)发用户名密码给资源提供者
- 资源提供者验证成功后返回访问令牌
grant_type:表示授权类型,此处的值固定为"password",必选项。
username:表示用户名,必选项。
password:表示用户的密码,必选项。
scope:表示权限范围,可选项。
配置
public class Startup
{
public void Configuration(IAppBuilder app)
{
//授权配置
var OAuthOptions = new OAuthAuthorizationServerOptions
{
//获取Token的路径
TokenEndpointPath = new PathString("/Token"),
//自定义的 校验,授权器
Provider = new ApplicationOAuthProvider(),
//我们不想让客户端直接输入凭据,需要一个服务器进行发放授权,这个就像点开QQ第三方登入弹出的登入页面.AuthorizeEndpointPath就是这种页面的地址
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
//Token 过期时间,默认20分钟
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
//在生产模式下设 AllowInsecureHttp = false
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthOptions);
}
}
通过请求TokenEndpointPath获得Access_Token
自定义OAuthProvider
Owin.OAuth提供的授权方式有
- GrantAuthorizationCode 授权码模式
- GrantResourceOwnerCredentials 密码模式
- GrantClientCredentials 客户端模式
- GrantCustomExtension 自定义模式,只要grant_type不是上述3个
UseOAuthBearerTokens 内部包含了
- UseOAuthAuthorizationServer
- UseOAuthBearerAuthentication
通过基础OAuthAuthorizationServerProvider来定义自己的授权,通过重写上述相应的方法来选择不同的授权模式.这些方法触发条件就是grant_type
,不同的值会触发不同的方法.
但在这之前必须经过ValidateClientAuthentication
.因为整个流程就是认证->授权
,认证是判断这个用户是否是本系统的,授权是给予用户相应的标记,该标记不仅标示用户身份也表示用户的权限.
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public ApplicationOAuthProvider()
{
}
///
/// 客户端验证,这是必须通过的方法,确保客户端是正确的,这里通过后才能开始授权
///
///
///
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//context.TryGetFormCredentials() 从表单中获取clientId,clientSecret
//context.TryGetBasicCredentials 从Authenticate Base解密 获取clientId,lientSecret
//在数据库中查看clientId的有效性
//在这里还没有些clientId,所以下面这句意思是,如果不是使用password认知方式,则直接验证通过,其clientId设置为12
if (context.Parameters["username"] == null && context.Parameters["password"] == null)
{
context.Validated("123");
}
return base.ValidateClientAuthentication(context);
}
///
/// 账号密码授权,grant_type:password
///
///
///
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
//生成token
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);//授权
return base.GrantResourceOwnerCredentials(context);
}
///
/// 对客户端进行授权,grant_type:client_credentials
///
///
///
public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
context.Request.Body.Seek(0, SeekOrigin.Begin);
//从报文体重获取数据
string FormStr = new StreamReader(context.Request.Body).ReadToEnd();
var FormKeyValues = HttpUtility.ParseQueryString(FormStr);
//登入
var loginresult = AsyncHelper.RunSync<LoginOutput>(()=>studentLoginApp.Login(new LoginInput()
{
StuNum = FormKeyValues["StuNum"],
Psw = FormKeyValues["Psw"]
}));
//添加Cookie到声明
oAuthIdentity.AddClaim(new Claim(loginresult.Cookie.Name, loginresult.Cookie.Value));
//添加学号
oAuthIdentity.AddClaim(new Claim(ClaimTypes.UserData, loginresult.StuNum));
//生成token
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
return base.GrantClientCredentials(context);
}
}
这里重写了3个方法
- ValidateClientAuthentication
- GrantResourceOwnerCredentials
- GrantClientCredentials
当请求Access_Token时候必定经过ValidateClientAuthentication,这个通常客户端会带上clientId,lientSecret,在此验证两个数据的正确性,得知该客户端是否有权向本网站申请授权
当通过ValidateClientAuthentication后,则会根据请求中的grant_type来调用GrantResourceOwnerCredentials(password)或GrantClientCredentials(client_credentials)
GrantClientCredentials一般用于普通资源的授权,就是人人都能使用的资源.
GrantResourceOwnerCredentials 则是对用户有要求,需要登入才能使用资源,在这个方法中可以给用户Claim添加数据,比如Role,并再资源上打上Authentication(Role=”“)来限制
获得授权码后调用资源
这个是普通资源请求,按理要加上clientId,clientSecret可以直接加到Body中,也可以加到Headers的Authorization
注意这里的Body一定要是x-www-form-urlencoded,否则后端识别不出来,在ajax的时候可能会出问题
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret)));
如果数据都正确则能获得Access_Token
在这之后的请求需要要把Access_Token添加到Authorization中
未学习
- refresh_token
- 自定义授权失败响应信息