Spring oauth2令牌请求中未实际使用的基本身份验证过滤器和身份验证入口点

Posted

技术标签:

【中文标题】Spring oauth2令牌请求中未实际使用的基本身份验证过滤器和身份验证入口点【英文标题】:Basic Authentication Filter and authentication entry point not actually used from spring oauth2 token request 【发布时间】:2014-02-22 10:42:37 【问题描述】:

我已经基于 spring 的 sparklr 示例应用程序和我在网上找到的几个示例,使用 spring oauth2 实现了资源所有者流程。我像这样使用 curl 测试了令牌请求部分,以便同时提供客户端和用户凭据:

curl -v --data "username=user1&password=user1&client_id=client1&client_secret=client1&grant_type=password" -X POST "http://localhost:8080/samplerestspringoauth2/oauth/token"

它工作正常,但是我做了以下观察:

虽然根据我看到的示例,我使用了 BasicAuthentication 过滤器,但这并没有真正用于安全过程。由于令牌请求不包含 Authentication 标头,因此 BasicAuthentication 过滤器只是跳过执行任何检查。 ClientCredentialsTokenEndpointFilter 和 authentication-server 是唯一在令牌请求期间执行安全检查的。在注意到这一点并通过调试验证后,我尝试完全删除以下部分:

<http-basic entry-point-ref="clientAuthenticationEntryPoint" />

来自配置。但后来我收到了警告:

"无法建立 AuthenticationEntryPoint。请确保 您通过命名空间配置了登录机制(例如 form-login) 或使用 'entry-point-ref' 属性”。

下一步,我在 http 命名空间中添加了 entry-point-ref="clientAuthenticationEntryPoint,并消除了警告。我测试了应用程序并正常播放。

不过,除此之外,我在调试过程中也做了如下观察: ClientCredentialsTokenEndpointFilter 在私有变量中包含自己的 OAuth2AuthenticationEntryPoint 入口点,并在由于错误的客户端凭据而失败时使用该入口点。 因此,我在基本过滤器或 http 命名空间中指定哪个入口点都没有关系。最后 ClientCredentialsTokenEndpointFilter 将使用自己的私有 OAuth2AuthenticationEntryPoint。 总结一下我的结论似乎如下:

基本过滤器未使用,可以删除,如果我们指定 而是在 http 命名空间中的端点。 指定一个基本的 过滤器,或者 http 命名空间中的端点只需要 编译器停止警告。它们没有实际用途,而且 使用的端点在内部是硬编码的 ClientCredentialsTokenEndpointFilter。

下面我放了token请求的http和endpoint配置供大家参考。为了使帖子易于阅读,我跳过了其余的配置:

<http pattern="/oauth/token" create-session="stateless"
        authentication-manager-ref="clientAuthenticationManager"
        xmlns="http://www.springframework.org/schema/security">
        <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <anonymous enabled="false" />
        <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
        <custom-filter ref="clientCredentialsTokenEndpointFilter"
            before="BASIC_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>

<bean id="clientAuthenticationEntryPoint"
        class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="springsec/client" />
        <property name="typeName" value="Basic" />
    </bean>

我还假设相同的问题也发生在原始 sparklr 应用程序(即 spring oauth2 示例应用程序)的令牌请求配置中,这非常相似。可以在https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml找到,相关部分如下:

<http pattern="/oauth/token" create-session="stateless"
                authentication-manager-ref="clientAuthenticationManager"
                xmlns="http://www.springframework.org/schema/security">
                <intercept-url pattern="/**" method="GET" access="ROLE_DENY" />
                <intercept-url pattern="/**" method="PUT" access="ROLE_DENY" />
                <intercept-url pattern="/**" method="DELETE" access="ROLE_DENY" />
                <intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
                <anonymous enabled="false" />
                <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
                <!-- include this only if you need to authenticate clients via request
                        parameters -->
                <custom-filter ref="clientCredentialsTokenEndpointFilter"
                        after="BASIC_AUTH_FILTER" />
                <access-denied-handler ref="oauthAccessDeniedHandler" />
        </http>

我希望 spring oauth2 能够更恰当地与 spring security 交互,而不必进行不必要和误导性的配置,这让我觉得我可能错过了一些东西。由于安全是一个敏感方面,我想与您分享,并询问我的结论是否正确。

【问题讨论】:

为什么不向 Spring 论坛报告这个? 你用的是什么版本,有新版本出来了,你在新版本上检查过一样吗spring.io/blog/2014/04/18/… 【参考方案1】:

/oauth/token 提供了两种不同的方式来验证请求令牌的客户端:

使用 HTTP-Basic 身份验证(当存在“http-basic”元素时)

身份验证由 org.springframework.security.web.authentication.www.BasicAuthenticationFilter 处理,并处理包含客户端 base64 编码凭据的“授权”HTTP 标头。过滤器仅在 Authorization 标头存在时执行处理。这种方法总是首先尝试。只有当用户提供了带有无效内容的“授权”标头时,才会调用在 http-basic 上定义的入口点 - 这就是为什么您在调试器中看不到调用的入口点,尝试设置授权 HTTP 标头和断点会受到打击。

在 OAuth 标准中使用 client_id 和 client_secret HTTP 参数定义

这是使用 org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter 处理的,默认情况下使用将 WWW-Authenticate 标头发送回客户端的入口点。可以自定义默认入口点(有一个 setAuthenticationEntryPoint 方法)。入口点仅在您提供 client_id 参数时使用。

这两种方法都使用不同的方式来获取客户端的用户名+密码,但是通过同一个身份验证管理器进行验证。

您在取出 元素时观察到的“无法建立 AuthenticationEntryPoint”错误来自 Spring Security 本身,而不是来自 OAuth 扩展。原因是 Spring Security 无法判断自定义过滤器 ClientCredentialsTokenEndpointFilter 内部已经配置了默认入口点。而且 Spring Security 的 HTTP 配置总是必须至少有一个入口点可用。

所以,完整的逻辑如下:

当您包含带有无效凭据的“授权”标头并且存在 元素时,系统将使用在 元素上定义的入口点。如果没有指定(属性 entry-point-ref 缺失),系统会自动为你创建一个默认的 BasicAuthenticationEntryPoint 实例并使用它。 当您包含带有无效凭据的 HTTP 参数“client_id”和“client_secret”并且存在自定义过滤器 clientCredentialsTokenEndpointFilter 时,系统将使用在 clientCredentialsTokenEndpointFilter bean 中定义的入口点(默认情况下是 OAuth2AuthenticationEntryPoint 实例) 如果“Authorization”标头和“client_id”参数都不存在并且端点需要身份验证(“IS_AUTHENTICATED_FULLY”),系统将使用上定义的入口点,如果存在,否则它将使用 http-basic 上定义的入口点(如上) 如果您既没有指定 http-basic(或 Spring 识别的其他默认身份验证方法),也没有使用 指定默认入口点,系统将失败并显示“No AuthenticationEntryPoint可以建立”,因为它至少需要一个入口点,并且它不知道在 clientCredentialsTokenEndpointFilter 中有一个可用的入口点。

关于您的观察:

>> 基本过滤器不使用,可以删除,如果我们指定 而是在 http 命名空间中的端点。

> 如果您使用 client_id + client_secret 对客户端进行身份验证,这是正确的

>> 指定基本过滤器或 http 命名空间中的端点是 只需要编译器停止警告。他们没有 实际使用,并且使用的端点在内部是硬编码的 ClientCredentialsTokenEndpointFilter。

> 部分正确,因为在缺少 client_id 的情况下将使用入口点。

配置确实令人困惑(部分原因是 OAuth 不是 Spring Security 的原生部分,而是一个扩展),但所有这些设置都是有意义的,并且在特定情况下使用。

您所做的更改没有安全隐患。

【讨论】:

它是 4 种授权类型而不是 2 种 code、implicit、password 和 client credential 答案不是处理授权类型(=获取访问令牌的机制),而是处理客户端身份验证(=如何向授权服务器验证客户端)。客户端身份验证可以是授权代码、资源所有者和客户端凭据授予(+例如断言授予类型)的一部分。对于隐式授权类型规范,明确指出“在隐式授权流程期间发布访问令牌时,授权服务器不对客户端进行身份验证”。详情请见tools.ietf.org/html/rfc6749#section-2.3。 你好@VladimírSchäfer,你的分析器真的很棒。我也被这种类型的问题困住了,我仍然无法弄清楚会发生什么。***.com/questions/24930830/…,在这个链接中,我定义了我的问题。请帮帮我。

以上是关于Spring oauth2令牌请求中未实际使用的基本身份验证过滤器和身份验证入口点的主要内容,如果未能解决你的问题,请参考以下文章

oauth2 spring-security 如果您在请求令牌或代码之前登录

Spring oauth2 验证令牌请求

使用 Spring Security OAuth2 进行访问令牌请求的 JWT 不记名交换

如何使用带有 WebClient 的 spring-security-oauth2 自定义 OAuth2 令牌请求的授权标头?

使用 Spring Oauth2 缓存访问令牌

使用 Spring Oauth2 缓存访问令牌