[Practice Note] 1.OAuth四种授权模式实现
2020-01-14 10:04
288 查看
OAuth授权模式
- 授权码模式 authorization code
- 简化模式 implicit grant type
- 密码模式 resource owner password credentials
- 客户端模式 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只能被用一次
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- 实现OAuth2.0服务端【授权码模式(Authorization Code)】
- IdentityServer4 实现自定义 GrantType 授权模式
- 实现OAuth2.0服务端【授权码模式(Authorization Code)】
- 基于 IdentityServer3 实现 OAuth 2.0 授权服务【客户端模式(Client Credentials Grant)】
- IdentityServer4 实现自定义 GrantType 授权模式
- asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)
- Oauth2认证模式之授权码模式实现
- 基于 IdentityServer3 实现 OAuth 2.0 授权服务【密码模式(Resource Owner Password Credentials)】
- 本文是笔者根据数据库编程经验,利用C++语言的模板、继承、授权、多态等面向对象特性,借鉴命令模式,实现了对象在关系数据中的存储,降低应用系统与数据库之间的耦合,提高开发效率。
- 认证鉴权与API权限控制在微服务架构中的设计与实现:授权码模式
- 常见设计模式的解析和实现(C++)之十-Proxy模式
- 设计模式C++实现——策略模式
- [shiro学习笔记]第二节 shiro与web融合实现一个简单的授权认证
- 分布式系统应用中生成全局唯一ID的算法(snowflake)----java 实现,单例模式
- 夜间模式的实现
- 在ASP.NET中实现Model-View-Controller模式
- 剑指offer学习--实现单例模式
- 单例模式的几种实现方式
- 设计模式C++实现(3)——适配器模式
- 使用easyUI的Tree实现授权功能