【Spring Security】 Oauth2 Scope

Metadata

title: 【Spring Security】 Oauth2 Scope
date: 2023-02-05 15:54
tags:
  - 行动阶段/完成
  - 主题场景/组件
  - 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
  - 细化主题/Module/SpringSecurity
categories:
  - SpringSecurity
keywords:
  - SpringSecurity
description: 【Spring Security】 Oauth2 Scope

【Spring Security】 Oauth2 Scope

概念

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 定义了一些可用作用域

名称 描述
(无作用域) 授予对公共信息的只读访问权限(包括用户个人资料信息、公共仓库信息和 gist)
repo 授予对仓库(包括私有仓库)的完全访问权限。这包括对仓库和组织的代码、提交状态、 仓库和组织项目、邀请、协作者、添加团队成员身份、部署状态以及仓库web挂钩的读取/ 写入权限。还授予管理用户项目的权限。
repo:status “授予对公共和私有仓库提交状态的读/写权限。仅在授予其他用户或服务对私有仓库提交状 态的访问权限而不授予对代码的访问权限时,才需要此作用域。”
repo_deployment “授予对公共和私有仓库的部署状态的访问权限。仅在授予其他用户或服务对部署状态的访 问权限而不授予对代码的访问权限时,才需要此作用域。”
public_repo 将访问权限限制为公共仓库。这包括对公共仓库和组织的代码、提交状态、仓库项目、协 作者以及部署状态的读取/写入权限。标星公共仓库也需要此权限。

流程

使用案例

授权码模式入门参考文档

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 添加更小粒度的控制。