IdentityServer4是基于ASP.NET Core实现的认证和受权框架,是对OpenID Connect和OAuth 2.0协议的实现。html
OpenID Connect:git
OpenID Connect由OpenID基金会于2014年发布的一个开放标准, 是创建在OAuth 2.0协议上的一个简单的身份标识层, OpenID Connect 兼容 OAuth 2.0. 实现身份认证(Authentication)
参考资料:https://openid.net/connect/
OpenID Connect文档:https://openid.net/specs/openid-connect-discovery-1_0.htmlgithub
OAuth2.0:web
OAuth2.0是一个开放的工业标准的受权协议(Authorization),它容许用户受权让第三方应用直接访问用户在某一个服务中的特定资源,但不提供给第三方帐号及密码信息
参考资料:http://www.javashuo.com/article/p-hgfzaqzn-r.html
OAuth2.0 文档:https://tools.ietf.org/html/rfc6749#page-73sql
authentication: n. 证实;鉴定;证明 authorization: n. 受权,承认;批准,委任
前者是身份识别,鉴别你是谁;后者是受权许可,告诉你能够作什么。
举个例子:你吭哧吭哧写了一天的代码,急于回家吃上一口媳妇作的热饭。当你走到小区门口的时候你须要刷小区的门禁卡才能进入到小区里面,而后再找到你家在哪一栋楼,几单元几号,而后掏出钥匙开门才能回到家。在这个过程当中刷小区的门禁就是认证你是这个小区的人,拿你家的钥匙开门就是受权的过程,若是你的认证不经过,那就不存在受权。数据库
咱们先来了解一下OAuth2.0中的几个关键概念:api
一个可以访问受保护资源的实体。当资源全部者是一我的时,它被称为终端用户安全
托管受保护资源的服务器,可以使用访问令牌接受和响应受保护的资源请求服务器
表明资源全部者和其受权的应用程序来保护资源请求。术语客户端并不意味着任何特定的实现特征(例如,应用程序是否在服务器、桌面或其余设备上执行)app
在成功验证资源全部者并得到受权以后,服务器向客户端发出访问令牌。(受权服务器是用来管理Resource Owner,Resource Server,Client的中间人)
场景:小李想要打印(美图快印)本身三年来发布在新浪微博相册中和女友的照片,有没有什么方法他既不告诉工做人员本身的新浪微博用帐号和密码又可以方便快捷的把照片给到美图快印呢?(排除存U盘这种手工操做)
Authorization Server和Resource Server可使独立的服务提供商,也能够是在一块儿的,好比例子中新浪微博既做受权服务器也用来存储用户的图片资源。咱们能够看到OAuth2解决的问题是:经过Authorization Server能够提供一个访问的凭据(token)给client(美图快印的工做人员),使得client能够在不知道Resource Owner以及Resource Server的用户名和密码的状况下访问到Resource Owner受保护的资源,它是一个完美的中间人。
OAuth2.0详细内容请参考:http://www.javashuo.com/article/p-hgfzaqzn-r.html
基于OpenID Connect实现的独立的认证服务实现对多平台(web, native, mobile, services)的集中认证
为各类类型的客户机颁发api访问令牌,例如服务器到服务器、web应用程序、spa和native/mobile程序
支持外部身份提供者,如Azure Active Directory、Google、Facebook等
IdentityServer4的许多方面能够定制以知足您的须要,由于它是一个框架,而不是SaaS服务,因此能够经过编写代码来调整实现,以适应不一样的场景
使用许可的Apache2开源协议,容许在其之上构建商业产品,也做为.NET基金会支持的项目 (https://dotnetfoundation.org/projects?type=project&ps=10&pn=6)
官方能够对使用者提供部分的免费商业支持
IdentityServer
身份认证服务器是一个实现了OpenID Connect和OAuth 2.0协议的身份提供者,它负责向客户端发布安全令牌
User
使用注册客户端访问资源的用户
Client
客户端从标识服务器请求令牌,要么用于认证用户(请求身份令牌),要么用于访问资源(请求访问令牌)
客户端必须首先在身份服务器上注册,而后才能请求令牌
这里的客户端能够是web应用程序、native mobile, desktop applications, SPA 等程序
Resource
资源是你想要用身份认证服务器保护的东西,如:用户的身份数据或api
每一个资源都有一个唯一的名称,客户端使用这个名称来指定他们想要访问的资源
关于用户的身份数据标识(也称为claim),例如姓名或电子邮件地址
Identity Token
身份令牌表明身份验证过程的结果
Access Token
访问令牌受权客户端以容许访问哪些API资源,访问令牌包含客户端和用户的信息
咱们先来看一个简单的例子,咱们有三个API ,Order, Product, Inventory,咱们利用IdentityServer4来实现对着三个API的认证和受权。首先咱们须要一个实现认证和受权的服务,而后外部要想访问咱们的API就必须经过统一的认证和受权服务的任何才能够,不然就是返回401: UnAuthorized ,未经受权的访问。咱们既能够将身份信息存储到内存中,也能够将其持久化到数据库中,此处咱们使用内存模式快速的演示实现(示例代码中也支持存储到DB中,使用SqlLite + EF Core)
首先咱们须要安装IdentityServer4的Nuget包,而后在ConfigureServices方法中添加以下代码来初始化须要保护的API资源信息,代码以下:
public void ConfigureServices(IServiceCollection services) { // config data in memory services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(InitMemoryData.GetApiResources()) .AddInMemoryClients(InitMemoryData.GetClients()) .AddTestUsers(InitMemoryData.GetUsers()); // config in DB //services.AddDbContext<IdentityServerDbContext>(options => // options.UseSqlite(sqliteConnection)); }
InitMemoryData 中的配置信息以下:
// scopes define the API resources in your system public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("inventoryapi", "this is inventory api"), new ApiResource("orderapi", "this is order api"), new ApiResource("productapi", "this is product api") }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { // client credentials client return new List<Client> { new Client { ClientId = "inventory", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("inventorysecret".Sha256()) }, AllowedScopes = { "inventoryapi" } }, new Client { ClientId = "order", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("ordersecret".Sha256()) }, AllowedScopes = { "orderapi" } }, new Client { ClientId = "product", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("productsecret".Sha256()) }, AllowedScopes = { "productapi" } } }; }
咱们给IdentityServer4设置启动端口5000,认证服务的地址就是:http://localhost:5000
而后认证Server端的代码就行了,接下来咱们须要在API添加受权服务的配置,配置都很相似,咱们以OrderAPI为例:
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); services.AddAuthentication("Bearer") .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ApiName = "orderapi"; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseAuthentication(); app.UseMvc(); }
这里咱们配置的Authority地址就是认证受权的地址,AddAuthentication中的Bearer是Jwt Token的一种,具体可参考文章:http://www.javashuo.com/article/p-xtvsqjsu-e.html
在controller中添加简单代码来返回API的信息:
[Route("[controller]")] [Authorize] public class OrderController : ControllerBase { // GET api/order [HttpGet] public IActionResult Get() { var userIdentitys = from c in User.Claims select new UserIdentity { Type = c.Type, Value = c.Value }; var result = new UserIdentityModel() { Description = "Access user order api successfully", UserIdentitys = userIdentitys.ToList() }; return new JsonResult(result); } }
设置当前API的端口为:5002
Product和Inventory中的配置和这个相似,端口信息以此设置为5001,5003,一切就绪,让咱们来测试一下结果:
启动IdentotyServer,以及三个API,咱们使用Postman来请求api,下面站点就是IdentityServer的页面了:
接着咱们来直接访问OrderAPI就会发现返回 401 ,这说明目前咱们的API已经受保护了,没有认证服务颁发的token,是直接访问不了的。
咱们输入地址:http://localhost:5000/.well-known/openid-configuration 能够查看咱们当前认证受权服务的配置信息:
如今还差一步就能够访问咱们的OrderAPI了,那就是:客户端传入必要的信息给认证服务,生成必定格式的token,而后携带着这个token来访问咱们的服务
传入的三个参数分别是grant_type , client_sercret, client_id这几个参数分别表明了咱们申请token时的受权方式是客户端受权,密匙,clientid信息。咱们在前面介绍过IdentityServer4是对OAuth2.0的实现,因此具体参数的含义请参考以前OAuth2.0文章中的详细介绍
http://www.javashuo.com/article/p-hgfzaqzn-r.html
此时咱们能够看到认证服务给咱们返回了有效token,指定过时时间3600s ,token的类型是Bearer,而后咱们再携带这这个token去访问服务试试看:
咱们能够看到此时API 返回了咱们期待的正确结果,若是在1小时后再携带着这个token去访问API就会提示token已过时,须要从新生成才可以继续访问。看完这个例子是否是很简单,很清爽呢