asp.net core系列 Identityserver4 各种令牌分析
IDToken (ID令牌):在ids4中有包含认证用户的id token里面包含了用户身份信息。当登录成功后,由授权服务器返回到客户端,如下所示:
把id_token在https://jwt.io/中解析后如下图所示:
accesstoken(访问令牌):也称令牌,由授权服务器颁发给客户端,表示客户端已被授予权限。客户端不需要也不能查看令牌内容,只需持有令牌访问受保护的资源。也就是说访问令牌是用来访问资源的。在Oauth2.0中没有规定访问令牌的内容应该是什么样的,授权服务器和受保护的资源自行协商令牌的含义,令牌可以被签名、加密,或者签名又被加密,客户端仍然不需要关心令牌的内容。令牌可以是随机字符串,或结构化的jwt或saml断言,在ids4中Oauth2.0使用了jwt的结构存储格式。如下图所示:
refreshtoken(刷新令牌):也是由授权服务器颁发给客户端的令牌,该令牌不会被发送给受保护的资源,客户端使用刷新令牌向授权服务器请求新的令牌,而不需要用户的参与。因为访问令牌随时可能失效如用户撤销,过期,或者其他系统导致令牌失败。 如果刷新令牌也失效了,只能让用户再次授权给客户端。
oauth协议的设计目的是: 用户通过oauth将他们在受保护资源(web api)上的部分权限委托给客户端应用。用户的凭据没有暴露给客户端,身份认证的过程中所用的信息是独立于客户端交互的。客户端没有开发者秘钥,无法随意访问任何资源。这一过程是客户端请求访问令牌,用户对客户端授权,然后由客户端管理令牌,用户管理客户端应用。 客户端可以是web应用,原生应用,javascript应用。
后端信道: 这里的信道是指oauth中角色与组件之间的交互方式,oauth是一个基于htttp的协议,但是oauth中的交互并不总是通过简单的http请求和响应来完成。后端信道通信是指http的请求和响应使用常规的http机制来通信:头部,查询参数,http方法。类似于使用httpclient发送请求一样。授权服务器提供了一个授权端点 /authorize,供客户端请求访问令牌和刷新令牌,客户端直接向该端点发出请求,携带一组表单格式的参数,授权服务器解析并处理这些参数,然后授权服务器返回一个代表令牌的json对象。
前端信道:这是一种间接通信方法,而不是后端信道的直接通信方法,它将web浏览器作为媒介,使用http请求实现两个系统间的间接通信。实现了跨安全域工作,信息隔离(身份认证在授权服务器如:OpenID connect)。前端信道需要用到web浏览器和http重定向,是交互式认证授权常用的方法,如企业网使用微信或支付宝登录,都是通过前端信道。 由于使用web浏览器数据可能被篡改,OpenID connect要求客户端或者授权服务器对前端信息中消息签名,通过这样增加安全机制。
client_id和client_secret: 一个是客户端标识,一个是共享秘钥,都由授权服务器分配。
使用授权码许可类型获取令牌流程是:当客户端发送授权请求后,让用户跳转到授权服务器的授权端点 比如http://localhost:9000/authorize,用户同意授权后会重定向回客户端,在客户端的callback函数参数code中读取服务器返回的授权码,拿到授权码再以http post方法发送至令牌端点,将授权码以表单形式参数放入请求正文。在identityserver4的javascript应用中相应配置代码如下:
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "code",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
state参数:该参数是跨站保护,该参数是一个随机值,由客户端自动生成,当授权服务器收到一个带有state参数的授权请求时,它必须总是将该state参数和授权码一起原样返回给客户端。这样客户端就能进行前后对比。如果不一致,客户端会终止所有授权请求处理,并向用户展示错误页面。
bearer令牌:使用http authorization头部传递令牌,http头的值 以关键词bearer开头,后面一个空格,再跟令牌(accesstoken)值本身,bearer和authorization关键词本身不区分大小写。令牌值本身是区分大小写的。当客户端请求资源服务器时,携带bearer格式令牌,资源服务器验证令牌会从授权服务器存储令牌的数据库中查找对比。对于小型的oAUTH系统,资源服务器和授权服务器一般都是一台主机上的二个进程。
令牌内省(token introspection):资源服务器和授权服务器一般都是共享数据库,这是一种常见的OAuth部署模式。但不是唯一选择,使用令牌内省的web 协议,它可以由授权服务器提供接口,让资源服务器能够在运行时检查令牌的状态,这便利资源服务器可以像客户端那样将令牌本身视为不透明的,代价是使用更多流量。
资源服务器返回不同用户的数据结果:客户端与受保护资源之间建立的连接上,并没有资源拥有者的登录或身份认证信息,但是生成的令牌中会包含资源拥有者的信息。 在ids4的资源服务器中,在控制器的action中可以通过User.Claims来访问,如下所示:
/// <summary>
/// 获取客户端用户的Claims返回,目前能获取到用户的id
/// </summary>
/// <returns></returns>
public IActionResult Get()
{
//获取用户ID:type=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"
// var identity = this.HttpContext.User.Identity;
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
授权端点 authorize:用户在oAuth授权过程中的第一站是授权端点,授权端点是一个前端信道端点,客户端会将用户浏览器重定向至该端点,以发出授权请求,是一个get请求。 请求uri中带上client_Id和redirect_uri,授权服务器会检查是否有注册该客户端,接点检查传入的redirect_uri是否与客户端注册信息中的一致。客户端通过检查,则出现登录页。response_type 包括了code 和id_token(请求授权码和身份令牌),这二个参数的值都会在登录成功后返回给客户端。
get /connect/authorize/callback?client_id=skoruba_identity_admin&redirect_uri=http://localhost:9000/signin-oidc&response_type=code id_token&scope=openid profile email roles&response_mode=form_post&nonce=637070886395343594.YjViZjExNmYtZDBlNi00YmM2LThlNmYtMzA3MmZlNWQ5NDJjYzY1ZTUxMTItNzExZC00OTFiLTlhNmItNTNhOTlkNzQxMWUx&state=CfDJ8LUNoV1TRFtOmLY5xfhloC28L7zlEkdSDp_pNDiJXyR-RXl7RTcsd_EumRLWXK7le8bnnZlxnCsaHZ8qnjTkDqyqaMD2qGgxi193ellDLYRfl0ebV79niFn7vkbz5HLBbt3ERzTD00TIT9Zsf4oy5e8eMJbiSuu1Gg3sZCTxz6mHv5V3nXpCFHMY0oaO4Aig5_mikiGPGVmfXOWZJ-5KaBt6JZLsdXKJ4YUJ97RVI14aOUHwLMddY4iz5M9dSJSgka9pxeksNpz4XGMcGp4KTOr6CjtjWmvvi91mC_qGbgZoJA-bHZjupBYVtcJUu7oS3g&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0
授权码许可类型Code:授权服务器会生成一个授权码并返回给客户端,授权服务器还会将这个授权码存储起来,以便接下来在客户端访问令牌端点时还能找到它。,如下图登录成功后,授权服务器生成code并返回给客户端,并把state在传回来。这里获取了response_type 为code id_token的值
scope授权范围:在ids4中包括用户资源的scope和api资源scope,分别对应着openID Connect用户身份认证t和oauth2.0资源保护, 是用来限制每个客户端对服务器上可访问的范围,这限制客户端只能在受保护资源上执行特定操作,这个粒度非常大,不是用来实现用户权限(api增删改查)。用户权限是做法是,根据用户id,按需查找权限来判断能否操作该api接口的增删改查。客户端可以在向授权端点请求时,使用scope参数来请求期望的权限范围的子集,这个参数是一个字符串,以空格分隔的权限范围列表。如下图所示,该客户端(client_id:js)它希望能请求的权限范围有openid profile api1,前二个 openid profile子集是用户资源的scope,而api1是api资源的scope:
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "code",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
当客户端在授权服务器中加入白名单,添加了scope后, 客户端请求授权端点,使用了没有的scope子集,请求到授权服务器后,服务器会验证,报如下错误:提示client有错误的scope。