您的位置:首页 > 运维架构

IdentityServer4 实现 OpenID Connect 和 OAuth 2.0

2017-01-14 07:49 971 查看
关于OAuth2.0的相关内容,点击查看:ASP.NETWebApiOWIN实现OAuth2.0OpenID是一个去中心化的网上身份认证系统。对于支持OpenID的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为OpenID身份提供者(identityprovider,IdP)的网站上注册。OpenID是去中心化的,任何网站都可以使用OpenID来作为用户登录的一种方式,任何网站也都可以作为OpenID身份提供者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。OpenID相关基本术语:最终用户(EndUser):想要向某个网站表明身份的人。标识(Identifier):最终用户用以标识其身份的URL或XRI。身份提供者(IdentityProvider,IdP):提供OpenIDURL或XRI注册和验证服务的服务提供者。依赖方(RelyingParty,RP):想要对最终用户的标识进行验证的网站。以上概念来自:https://zh.wikipedia.org/wiki/OpenID针对.NETCore跨平台,微软官方并没有针对OAuth2.0的实现(
Microsoft.AspNetCore.Authentication.OAuth
组件,仅限客户端),IdentityServer4实现了ASP.NETCore下的OpenIDConnect和OAuth2.0,IdentityServer4也是微软基金会成员。阅读目录:OpenID和OAuth的区别客户端模式(ClientCredentials)密码模式(resourceownerpasswordcredentials)简化模式-WithOpenID(implicitgranttype)简化模式-WithOpenID&OAuth(JS客户端调用)混合模式-WithOpenID&OAuth(HybridFlow)ASP.NETCoreIdentityandUsingEntityFrameworkCoreforconfigurationdata开源地址:https://github.com/yuezhongxin/IdentityServer4.Demo

1.OpenID和OAuth的区别

简单概括:OpenID:authentication(认证),用户是谁?OAuth:authorization(授权),用户能做什么?其实,OAuth的密码授权模式和OpenID有些类似,但也不相同,比如用户登录落网选择微博快捷登录方式,大致的区别:OAuth:用户在微博授权页面输入微博的账号和密码,微博验证成功之后,返回access_token,然后落网拿到access_token之后,再去请求微博的用户API,微博授权中心验证access_token,如果验证通过,则返回用户API的请求数据给落网。OpenID:落网可以没有用户的任何实现,落网需要确认一个URL标识(可以是多个),然后用户登录的时候,选择一个URL进行登录(比如微博),跳转到微博OpenID登录页面,用户输入微博的账号和密码,微博验证成功之后,按照用户的选择,返回用户的一些信息。可以看到,OAuth首先需要拿到一个授权(access_token),然后再通过这个授权,去资源服务器(具体的API),获取想要的一些数据,上面示例中,用户API只是资源服务器的一种(可以是视频API、文章API等等),在这个过程中,OAuth最重要的就是获取授权(四种模式),获取到授权之后,你就可以通过这个授权,做授权范围之类的任何事了。而对于OpenID来说,授权和它没任何关系,它只关心的是用户,比如落网,可以不进行用户的任何实现(具体体现就是数据库没有User表),然后使用支持OpenID的服务(比如微博),通过特定的URL标识(可以看作是OpenID标识),然后输入提供服务的账号和密码,返回具体的用户信息,对于落网来说,它关心的是用户信息,仅此而已。上面其实是OAuth的授权,所以会有“获得以下权限”提示,如果是OpenID的话,“权限”应该改为“用户信息”。支持OpenID的服务列表:http://openid.net/get-an-openid/OpenID流程图(来自UsingOpenID):

2.客户端模式(ClientCredentials)

简单概述:客户端提供ClientId和ClientSecret给认证授权服务,验证如果成功,返回access_token,客户端拿到access_token,访问API资源服务。

2.1认证授权服务配置

创建ASP.NETCore站点,Startup配置修改如下:
p
IdentityServer4中
AddInMemory
的相关配置,都是Mock的(代码配置),也可以把这些配置存储在数据库中,这个后面再讲。
AddInMemoryApiResources
增加的API资源服务(List集合),也就此认证授权服务所管辖的API资源,比如上面配置的api1,这个会在客户端调用的时候用到,如果不一致,是不允许访问的,另外,Clinet中配置的
AllowedScopes={"api1"}
,表示此种授权模式允许的API资源集合(前提是需要添加
ApiResource
)。配置很简单,我们也可以访问
http://localhost:5000/.well-known/openid-configuration
,查看具体的配置信息:

2.2API资源服务配置

API资源服务站点,需要添加程序包:
"IdentityServer4.AccessTokenValidation":"1.0.1"
添加一个
ValuesController
[Route("[controller]")]
[Authorize]publicclassValuesController:ControllerBase{
  [HttpGet]  publicIActionResultGet()  {   
 returnContent("helloworld");
  }
}

2.3单元测试

需要添加程序包:
"IdentityModel":"2.0.0"
单元测试代码:
很简单,和我们之前用ASP.NETWebApiOWIN实现OAuth2.0一样,只不过配置和调用简化了很多,因为IdentityServer4替我们做了很多工作。

3.密码模式(resourceownerpasswordcredentials)

简单概述:客户端提供UserName和Password给认证授权服务,验证如果成功,返回access_token,客户端拿到access_token,访问API资源服务。

3.1认证授权服务配置

创建ASP.NETCore站点,Startup配置修改如下:
publicclassStartup{  publicvoidConfigureServices(IServiceCollectionservices)  {     //configureidentityserverwithin-memorystores,keys,clientsandscopes    services.AddIdentityServer()      .AddTemporarySigningCredential()      .AddInMemoryApiResources(newList<ApiResource>      {        newApiResource("api1","MyAPI")      })      .AddInMemoryClients(newList<Client>      {        //resourceownerpasswordgrantclient        newClient        {          ClientId="ro.client",          AllowedGrantTypes=GrantTypes.ResourceOwnerPassword,          ClientSecrets=          {                     newSecret("secret".Sha256())          },          AllowedScopes={"api1"}        }      })      .AddTestUsers(newList<TestUser>      {        newTestUser        {          SubjectId="1",          Username="xishuai",          Password="123"        }      });  }  publicvoidConfigure(IApplicationBuilderapp,ILoggerFactoryloggerFactory)  {    loggerFactory.AddConsole(LogLevel.Debug);    app.UseDeveloperExceptionPage();    app.UseIdentityServer();  }}
和客户端模式不同的是,
AllowedGrantTypes
授权模式改为了
ResourceOwnerPassword
,然后增加了测试用户(用来验证用户名和密码),也可以存储在数据库中。

3.2API资源服务配置

API资源服务站点,需要添加程序包:
"IdentityServer4.AccessTokenValidation":"1.0.1"
添加一个
IdentityController
[Route("[controller]")][Authorize]publicclassIdentityController:ControllerBase{  [HttpGet]  publicIActionResultGet()  {    returnnewJsonResult(fromcinUser.Claimsselectnew{c.Type,c.Value});  }}

3.3单元测试

需要添加程序包:
"IdentityModel":"2.0.0"
单元测试代码:

4.简化模式-WithOpenID(implicitgranttype)

简化模式在IdentityServer4中的实现,就是OpenIDConnect。简单概述:客户端确定URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据,并使此用户为登录状态,可以在客户端进行注销用户。

4.1认证授权服务配置

创建ASP.NETCore站点,Startup配置修改如下:
AddInMemoryIdentityResources
AllowedScopes
所配置的,是客户端允许访问的用户信息,具体查看:RequestingClaimsusingScopeValuesClientId很重要,必须和客户端一一对应,所以想要使用OpenID认证服务的客户端,需要向提供OpenID认证服务的机构,申请一个ClientId,OpenID认证服务会统一发放一个用户登录的URL。
TestUser
中的
Claims
配置,其实就是
IdentityServerConstants.StandardScopes.Profile
。另外,还有用户登录的一些操作代码,这边就不贴了,可以查看具体的实现:ImplicitServer.Web

4.2客户端服务配置

创建ASP.NETCore站点,添加程序包:
"Microsoft.AspNetCore.Authentication.Cookies":"1.0.*","Microsoft.AspNetCore.Authentication.OpenIdConnect":"1.0.*"
Startup配置修改如下:
[code]UseOpenIdConnectAuthentication
配置中的
Authority
,就是OpenID认证服务的URL。[/code]添加一个
HomeController
访问Secure页面,跳转到认证服务地址,进行账号密码登录,Logout用于用户的注销操作。

4.3Web测试

5.简化模式-WithOpenID&OAuth(JS客户端调用)

简单概述:客户端确定URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据和access_token,并使此用户为登录状态,可以在客户端进行注销用户,客户端可以拿到access_token,去访问授权范围之内的API资源。需要注意的是:因为简化模式,所以access_token是作为URL参数返回的。

5.1认证授权服务配置

创建ASP.NETCore站点,Startup配置修改如下:
publicclassStartup{    publicvoidConfigureServices(IServiceCollectionservices)  {    //configureidentityserverwithin-memorystores,keys,clientsandscopes    services.AddIdentityServer()      .AddTemporarySigningCredential()      .AddInMemoryIdentityResources(newList<IdentityResource>      {        newIdentityResources.OpenId(),        newIdentityResources.Profile(),      })      .AddInMemoryApiResources(newList<ApiResource>      {        newApiResource("api1","MyAPI")      })      .AddInMemoryClients(newList<Client>      {        //OpenIDConnectimplicitflowclient(MVC)        newClient        {          ClientId="js",          ClientName="JavaScriptClient",          AllowedGrantTypes=GrantTypes.Implicit,          AllowAccessTokensViaBrowser=true,          RedirectUris={"http://localhost:5022/callback.html"},          PostLogoutRedirectUris={"http://localhost:5022/index.html"},          AllowedCorsOrigins={"http://localhost:5022"},          AllowedScopes=          {            IdentityServerConstants.StandardScopes.OpenId,            IdentityServerConstants.StandardScopes.Profile,            "api1"          }        }      })      .AddTestUsers(newList<TestUser>      {        newTestUser        {          SubjectId="1",          Username="xishuai",          Password="123",          Claims=newList<Claim>          {                  newClaim("name","xishuai"),            newClaim("website","http://xishuai.cnblogs.com")          }        }      });  }    publicvoidConfigure(IApplicationBuilderapp,ILoggerFactoryloggerFactory)  {    loggerFactory.AddConsole(LogLevel.Debug);    app.UseDeveloperExceptionPage();    app.UseIdentityServer();  }}
因为涉及到访问API资源操作,需要需要添加
AddInMemoryApiResources
配置,
AllowedScopes
也需要添加对应的API资源名称,
AllowAccessTokensViaBrowser=true
的配置的作用就是,可以在浏览器地址中访问access_token。更多实现代码,点击查看:ImplicitServerWithJS.Web

5.2API资源服务配置

API资源服务站点,需要添加程序包:
"IdentityServer4.AccessTokenValidation":"1.0.1","Microsoft.AspNetCore.Cors":"1.1.0"
Startup配置修改如下:
因为JS需要跨域访问API资源服务,所以需要增加CORS配置。添加一个
IdentityController
[Route("[controller]")][Authorize]publicclassIdentityController:ControllerBase{  [HttpGet]  publicIActionResultGet()  {    returnnewJsonResult(fromcinUser.Claimsselectnew{c.Type,c.Value});  }}

5.3JSWeb站点测试

创建一个ASP.NETCore站点,添加
oidc-client.js
前端组件,测试JS代码:
///<referencepath="oidc-client.js"/>functionlog(){  document.getElementById('results').innerText='';  Array.prototype.forEach.call(arguments,function(msg){    if(msginstanceofError){      msg="Error:"+msg.message;    }    elseif(typeofmsg!=='string'){      msg=JSON.stringify(msg,null,2);    }    document.getElementById('results').innerHTML+=msg+'\r\n';  });}document.getElementById("login").addEventListener("click",login,false);document.getElementById("api").addEventListener("click",api,false);document.getElementById("logout").addEventListener("click",logout,false);varconfig={  authority:"http://localhost:5003",  client_id:"js",  redirect_uri:"http://localhost:5022/callback.html",  response_type:"id_tokentoken",  scope:"openidprofileapi1",  post_logout_redirect_uri:"http://localhost:5022/index.html",};  varmgr=newOidc.UserManager(config);mgr.getUser().then(function(user){  if(user){    log("Userloggedin",user.profile);  }  else{    log("Usernotloggedin");  }});functionlogin(){  mgr.signinRedirect();}functionapi(){  mgr.getUser().then(function(user){    varurl="http://localhost:5012/identity";    varxhr=newXMLHttpRequest();    xhr.open("GET",url);    xhr.onload=function(){      log(xhr.status,JSON.parse(xhr.responseText));    }    xhr.setRequestHeader("Authorization","Bearer"+user.access_token);    xhr.send();  });}functionlogout(){  mgr.signoutRedirect();}
测试过程(注意下URL中的参数):

6.混合模式-WithOpenID&OAuth(HybridFlow)

混合模式(HybridFlow)是一种新的模式,是简化模式(implicitflow)和验证码模式(authorizationcodeflow)的混合。简单概述:客户端确定URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据和access_token,并使此用户为登录状态,可以在客户端进行注销用户,客户端可以拿到access_token,去访问授权范围之内的API资源。和上面的简化模式流程差不多,不过access_token不是通过浏览器获取的,而是通过后台服务获取。

6.1认证授权服务配置

创建ASP.NETCore站点,Startup配置修改如下:
AllowedGrantTypes
配置改为
HybridAndClientCredentials
AllowOfflineAccess
需要设置为
true
。更多实现代码,点击查看:HybridServer.Web

6.2API资源服务配置

API资源服务站点,需要添加程序包:
"IdentityServer4.AccessTokenValidation":"1.0.1"
Startup配置修改如下:
添加一个
IdentityController
[Route("[controller]")][Authorize]publicclassIdentityController:ControllerBase{  [HttpGet]  publicIActionResultGet()  {    returnnewJsonResult(fromcinUser.Claimsselectnew{c.Type,c.Value});  }}

6.3客户端服务配置

创建ASP.NETCore站点,添加程序包:
"Microsoft.AspNetCore.Authentication.Cookies":"1.0.*","Microsoft.AspNetCore.Authentication.OpenIdConnect":"1.0.*","IdentityModel":"2.0.0"
Startup配置修改如下:
CallApiUsingClientCredentials
是通过客户端模式获取access_token,
CallApiUsingUserAccessToken
是通过上下文获取保存的access_token,其实和浏览器URL中获取是一样的意思,但需要配置
SaveTokens=true

6.4Web测试

7.ASP.NETCoreIdentityandUsingEntityFrameworkCoreforconfigurationdata

使用ASP.NETCoreIdentity,就是用户管理不由OpenID认证服务进行提供,ASP.NETCoreIdentity就相当于用户的一个管理者,比如用户的存储等。我没做这一块的示例,配置比较简单:
publicvoidConfigureServices(IServiceCollectionservices){  services.AddDbContext<ApplicationDbContext>(options=>    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));  services.AddIdentity<ApplicationUser,IdentityRole>()    .AddEntityFrameworkStores<ApplicationDbContext>()    .AddDefaultTokenProviders();  services.AddMvc();  services.AddTransient<IEmailSender,AuthMessageSender>();  services.AddTransient<ISmsSender,AuthMessageSender>();   //AddsIdentityServer  services.AddIdentityServer()    .AddTemporarySigningCredential()    .AddInMemoryIdentityResources(Config.GetIdentityResources())    .AddInMemoryApiResources(Config.GetApiResources())    .AddInMemoryClients(Config.GetClients())    .AddAspNetIdentity<ApplicationUser>();}
详细使用:UsingASP.NETCoreIdentity关于IdentityServer4的配置信息,可以使用EntityFrameworkCore进行存储,配置如下:
publicvoidConfigureServices(IServiceCollectionservices){  services.AddMvc();   varconnectionString=@"server=(localdb)\mssqllocaldb;database=IdentityServer4.Quickstart;trusted_connection=yes";     varmigrationsAssembly=typeof(Startup).GetTypeInfo().Assembly.GetName().Name;    //configureidentityserverwithin-memoryusers,   //butEFstoresforclientsandresources  services.AddIdentityServer()    .AddTemporarySigningCredential()    .AddTestUsers(Config.GetUsers())    .AddConfigurationStore(builder=>      builder.UseSqlServer(connectionString,options=>        options.MigrationsAssembly(migrationsAssembly)))    .AddOperationalStore(builder=>      builder.UseSqlServer(connectionString,options=>        options.MigrationsAssembly(migrationsAssembly)));}
详细使用:UsingEntityFrameworkCoreforconfigurationdata最后,简要总结下使用IdentityServer4的几种应用场景:客户端模式(ClientCredentials):和用户无关,用于应用程序与API资源的直接交互场景。密码模式(resourceownerpasswordcredentials):和用户有关,一般用于第三方登录。简化模式-WithOpenID(implicitgranttype):仅限OpenID认证服务,用于第三方用户登录及获取用户信息,不包含授权。简化模式-WithOpenID&OAuth(JS客户端调用):包含OpenID认证服务和OAuth授权,但只针对JS调用(URL参数获取),一般用于前端或无线端。混合模式-WithOpenID&OAuth(HybridFlow):推荐使用,包含OpenID认证服务和OAuth授权,但针对的是后端服务调用。开源地址:https://github.com/yuezhongxin/IdentityServer4.Demo参考资料:IdentityServer4IdentityServer4.SamplesWelcometoIdentityServer4WelcometoOpenIDConnectOpenID学习笔记OAuth和OpenID的区别OAuth、OAuth与OpenID区别和联系使用OpenID、OAuth和FacebookConnect武装你的站点OpenIDConnect身份认证标准推出,获谷歌微软支持
原文地址:http://www.cnblogs.com/xishuai/p/identityserver4-implement-openid-connect-and-oauth2.html.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐