您的位置:首页 > 其它

[Practice Note] 1.OAuth四种授权模式实现

2020-01-14 10:04 288 查看

OAuth授权模式

  1. 授权码模式 authorization code
  2. 简化模式 implicit grant type
  3. 密码模式 resource owner password credentials
  4. 客户端模式 client credentials grant

四种授权模式代码实现

1.在api项目中添加Nuget引用
1)Microsoft.Owin.Security (添加该引用下面的会默认添加)
2)Microsoft.Owin.Cookies
3)Microsoft.Owin
4)Microsoft.Owin.Host.SystemWeb

2.添加OAuth文件,添加授权类

3.实现类方法
MyOAuthAuthorizationServeProvider.cs类继承 OAuthAuthorizationServerProvider,实现重构授权Provider

代码中的TestUserRepository 是定义的全局用户类,可根据自己项目的情况实现(可以直接查询数据库),实现身份校验

namespace EVMTest.OAuth
{
public class MyOAuthAuthorizationServeProvider : OAuthAuthorizationServerProvider
{
/// <summary>
/// grant_type password,code
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
#region 客户端模式校验

string clientid;
string clientsecret;
if (!context.TryGetBasicCredentials(out clientid, out clientsecret))
context.TryGetFormCredentials(out clientid, out clientsecret);

if (!string.IsNullOrEmpty(context.ClientId))
{
if (clientid != TestUserRepository.client.ClientId)
{
context.SetError("Invalid_clientid", "clientid is not valid");
return Task.FromResult<object>(null);
}

if (!string.IsNullOrWhiteSpace(clientsecret))
context.OwinContext.Set("ClientSecret", clientsecret);

//context.Validated(clientid);
//return base.ValidateClientAuthentication(context);
}
#endregion

context.Validated();
return Task.FromResult<object>(null);
//return base.ValidateClientAuthentication(context);
}

/// <summary>
/// 验证重定向redirect_url  用于验证被注册的url
/// grant_type authorization_code
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == TestUserRepository.client.ClientId)
{
context.Validated(TestUserRepository.client.RedirectUrl);
}
}

/// <summary>
/// code
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
if (context.AuthorizeRequest.ClientId == TestUserRepository.client.ClientId
&& (context.AuthorizeRequest.IsAuthorizationCodeGrantType    //授权码模式
|| context.AuthorizeRequest.IsImplicitGrantType))            //简单模式
{
context.Validated();
}
else
{
context.Rejected();
}
//return base.ValidateAuthorizeRequest(context);
}

/// <summary>
/// 授权码模式与简单模式 code
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
//简单模式调用生成token
if (context.AuthorizeRequest.IsImplicitGrantType)
{
var identity = new ClaimsIdentity(new GenericIdentity(context.Request.Query["client_id"], OAuthDefaults.AuthenticationType));
context.OwinContext.Authentication.SignIn(identity);
context.RequestCompleted();
}

if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
{
switch (context.AuthorizeRequest.State)
{
case "login":
context.Response.Redirect("http://www.baidu.com");
context.RequestCompleted();
break;
case "validate":
var clientid = context.Request.Query["client_id"];
var redirecturl = context.Request.Query["redirect_url"];
var identity = new ClaimsIdentity(new GenericIdentity(clientid, OAuthDefaults.AuthenticationType));

var authorizecodecontext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
identity,
new AuthenticationProperties(new Dictionary<string, string> { { "client_id", clientid }, { "redirect_url", redirecturl } })
{ IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan) }
)
);

//生成授权码Code
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizecodecontext);
//请求地址携带Code授权码
context.Response.Redirect(redirecturl + "?code=" + Uri.EscapeDataString(authorizecodecontext.Token));
context.RequestCompleted();
break;
}
}

//return base.AuthorizeEndpoint(context);
}

/// <summary>
/// 用户密码模式 token
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

//账号密码校验
var finduser = TestUserRepository.userlist.Where(_ => _.NickName == context.UserName && _.PassWord == context.Password).FirstOrDefault();
if (finduser != null)
{

var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType));
//var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("username", context.UserName));
identity.AddClaim(new Claim("role", "user"));

//var identity = new ClaimsIdentity(
//    new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
//    context.Scope.Select(_ => new Claim("urn:oauth:scope", _))
//    );

context.Validated(identity);
}
else
{
context.SetError("Invalid_grant", "username or passwprd is incorrect");
return Task.FromResult<object>(null);
}

return Task.FromResult<object>(0);
}

public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);

var ticket = new AuthenticationTicket(identity,new AuthenticationProperties() { AllowRefresh=true});
context.Validated(ticket);
return base.GrantClientCredentials(context);
}

//public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
//{

//    return base.GrantRefreshToken(context);
//}

/////////////////////////////
//重构TokenEndpointResponse方法可以服务端获取,管理token
public ovveride TokenEndPointResponse(...)
{

}
}
}

MyRefreshTokenProvider.cs类继承AuthenticationTokenProvider,实现token刷新、获取,
上面的MyAuthenticationCodeProvider.cs与该类一致,可忽略

namespace EVMTest.OAuth
{
public class MyRefreshTokenProvider:AuthenticationTokenProvider
{
private static ConcurrentDictionary<string, string> _refreshtokens = new ConcurrentDictionary<string, string>();

/// <summary>
/// 生成refresh_token
/// </summary>
/// <param name="context"></param>
public override void Create(AuthenticationTokenCreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(20);

context.SetToken(Guid.NewGuid().ToString("n"));
//
_refreshtokens[context.Token] = context.SerializeTicket();
//base.Create(context);
}

/// <summary>
/// refresh_token解析成access_token
/// </summary>
/// <param name="context"></param>
public override void Receive(AuthenticationTokenReceiveContext context)
{
string value;
if (_refreshtokens.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);

}
}

public override async Task CreateAsync(AuthenticationTokenCreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(20);

context.SetToken(Guid.NewGuid().ToString("n"));
_refreshtokens[context.Token] = context.SerializeTicket();
// return base.CreateAsync(context);
}

}
}

完成上面的token提供、刷新类后,修改配置文件Startup.Auth.cs

ConfigureAuth方法中添加代码

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
//允许http请求
AllowInsecureHttp = true,
//获取token地址 http://localhost://端口/oauht2/token
TokenEndpointPath = new PathString("/oauth2/token"),
//授权码请求地址  http://localhost://端口/oauht2/oauthrize
AuthorizeEndpointPath = new PathString("/oauth2/oauthorize"),
//授权码授权
AuthorizationCodeProvider = new MyRefreshTokenProvider(),
Provider = new MyOAuthAuthorizationServeProvider(),
//token过期时间
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
//token提供类,create&refresh
RefreshTokenProvider = new MyRefreshTokenProvider(),
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{

});

在控制器头部、控制器方法头部加 [Authorize]特性,授权控制全部或某一方法的调用请求
控制器头部加 [Authorize]特性,内部方法可以加上 [AllowAnonymous]特性,忽略授权

namespace EVMTest.Controllers
{
[Authorize]
public class HomeController : MyController
{
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Title = "Home Page";

return JsonOK("");
}
/// <summary>
/// 测试Owin授权
/// </summary>
/// <returns></returns>
//[Authorize]
public JsonResult TestAuthorize()
{
var a = HttpContext.GetOwinContext().Authentication;
string name = a.User.Identity.Name;

return JsonOK(name);
}
}
}

获取token

1.授权码模式

1) 授权码获取:
Get路径:http://localhost:端口号/oauth2/oauthorize
参数:{"grant_type":"authorization_code","response_type":"code","client_id":"C1","redirect_url":"http://www.baidu.com","state":"login"}
返回Url:http://www.baidu.com?code=XXXXXXX

2)根据授权码获取Token
Post路径:http://localhost:端口号/oauth2/token
参数:{"grant_type":"authorization_code","Code":"上个请求获取到的Code","redirect_url":"http://www.baidu.com","client_id":"C1"}

2.简单模式

Get路径:http://localhost:端口号/oauth2/token
参数:{"grant_type":"authorization_code","response_type":"token","client_id":"C1","redirect_url":"http://www.baidu.com","state":"validate"}

3.密码模式

Post路径:http://localhost:端口号/oauth2/token
参数:{"grant_type":"password","username":"Alis","password":"123456"}

4.客户端模式

Post路径:http://localhost:端口号/oauth2/token
参数:{"grant_type":"client_credentials","client_id":"C1","client_secret":"123456"}

Token刷新:

根据上面每次token返回的refresh_token,调用
Get路径:http://localhost:端口号/oauth2/token
参数:{"grant_type":"refresh_token","refresh_token":"返回的token值"}    //refersh_token只能被用一次
  • 点赞
  • 收藏
  • 分享
  • 文章举报
Gen?? 发布了17 篇原创文章 · 获赞 0 · 访问量 261 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: