一、OAuth 2.0协议 关于Scope的说明
1.概念
Scope是 OAuth 2.0 中的一种机制,用于限制应用程序对用户帐户的访问。应用程序可以请求一个或多个范围,然后该信息会在同意屏幕中呈现给用户,并且颁发给应用程序的访问令牌将仅限于授予的范围。
OAuth 规范允许授权服务器或用户根据请求修改授予应用程序的范围,尽管在实践中这样做的服务示例并不多。
OAuth 没有为范围定义任何特定值,因为它高度依赖于服务的内部架构和需求。
GitHub 文档描述
通过作用域,您可以准确指定所需的访问权限类型。 作用域限制 OAuth 令牌的访问权限。 它们不会授予超出用户权限范围的任何额外权限。
在 GitHub 上设置 OAuth 应用程序时,请求的作用域会在授权表单上显示给用户。
例如通过授权码获取访问令牌后,可以通过以下方式查询当前作用域:
$ curl -H "Authorization: token OAUTH-TOKEN" https://api.github.com/users/codertocat -I
HTTP/2 200
X-OAuth-Scopes: repo, user \\ 列出令牌已授权的作用域。
X-Accepted-OAuth-Scopes: user \\ 列出操作检查的作用域。
GitHub定义了一些可用作用域
1. 添加作用域
在数据库或者内存中,给当前客户端添加了三个作用域。
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder.encode("secret"))
// 授权类型: 授权码、刷新令牌、密码、客户端、简化模式、短信验证码 "refresh_token"
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "sms_code")
// 授权范围,也可根据这个范围标识,进行鉴权
.scopes("admin:org","write:org","read:org")
.accessTokenValiditySeconds(300)
.refreshTokenValiditySeconds(3000)
// 授权码模式 授权页面是否自动授权
//.autoApprove(false)
// 拥有的权限
.authorities("add:user")
// 允许访问的资源服务 ID
//.resourceIds("oauth2-resource-server001-demo")
// 注册回调地址
.redirectUris("http://localhost:20000/code");
}
2. 授权页面选择scope
授权使用不带scope参数访问授权码端点:
http://localhost:20000/oauth/authorize?client_id=client&client_secret=secret&response_type=code
可以看到授权页面会弹出所有的scope范围
如果携带了scope参数,则表明只需要对当前作用域进行授权:
http://localhost:20000/oauth/authorize?client_id=client&client_secret=secret&response_type=code&scope=admin:org
如果当前客户端没有配置scope作用域,申请的时候也没有传递scope参数,则会报错:
Handling OAuth2 error: error="invalid_scope", error_description="Empty scope (either the client or the user is not allowed the requested scopes)"
3.资源服务器对于scope的访问控制
授权获取到授权码以后,申请访问令牌,通过访问令牌访问资源服务器。先看下这时认证信息都有些啥
可以看到当前令牌对应的认证信息,包含授权码模式中的用户及Oauth信息,OAuth2Request对象,就保存了授权时的scope信息,如果没有授权的scope则不会出现在这里。
这样资源服务器也就获取到了scope数据,那么具体应该怎么使用scope进行访问控制呢?
之前有分析过Security 基于注解的权限控制@PreAuthorize等注解的使用方法,在spring-security-oauth也提供了相应的表达式,我们只需要在注解中使用oauth2相关的表达式就可以了。
在源码中,可以看到Oauth2相关的表达式写法,其中就有对scope作用域的访问控制。
以下例子,可以使用hasScope,表示当前Oauth应用,毕竟具有admin:user的作用域,否则会拒绝访问.
@GetMapping("/resource")
@PreAuthorize("#oauth2.hasScope('admin:user')") //
public String resource(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication.toString());
return "访问到了resource 资源 ";
}
可以看到当前没有admin:user,所以去访问资源的服务器时会报错。
二、总结
通过以上案例分析,Spring Security Oauth2除了使用resourceId对服务级别进行控制,也能基于scope 添加更小粒度的控制。