【问题标题】:Using Cookie for Authentication with Restful service in Spring在 Spring 中使用 Cookie 和 Restful 服务进行身份验证
【发布时间】:2013-05-06 22:38:02
【问题描述】:

我使用 Spring Security 来保护使用 Spring 构建的 RESTful Web 服务,但过去请求使用身份验证标头进行身份验证。现在,当用户的凭据在 cookie 中时,我需要保护这样的应用程序。

我正在尝试找出从 cookie 中提取用户信息并将其放入身份验证标头中的最佳方法,或者理想情况下仅将 cookie 的值用于UserDetailsService 中的loadUserByUsername 方法豆子。

到目前为止,我已经提出了一些关于如何做到这一点的想法:

  1. 我可以添加一个HandlerInterceptor 以使用cookie 将身份验证标头添加到请求中。但是,并非所有请求都需要身份验证,因此如果我这样做了,我希望将其应用于某些方法或请求映射而不是其他方法,我不知道该怎么做。

  2. 我可以实现AuthenticationEntryPoint,如this tutorial 所示。在那个教程中,虽然我真的不明白为什么他有response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" ); 作为commence 方法的主体。在我的用例中,我会在那里使用 cookie 添加身份验证标头吗?

  3. 我可以在所有控制器方法的第一行中使用自定义方法执行安全检查。似乎当您遵循 Spring Security 的理想路径时,它是一个非常好的解决方案。不过,我需要做的似乎偏离了这一点,我的要求实际上很简单。也许我最好还是用简单的老式方法来做这件事?

  4. 我不知道/没有想到的更好的主意!

感谢您的帮助和想法!

【问题讨论】:

  • 注意:将用户密码存储在 cookie 中是不安全的。您可以在不将其存储在 cookie 中的情况下解决您的问题。
  • @Michael 我很欣赏这个建议,但实际上 cookie 中没有密码。我需要使用该 cookie 的值进行身份验证这一事实超出了我的控制范围。

标签: java spring rest spring-security


【解决方案1】:

如果我正确地满足了您的要求,使用 Spring Security 的Pre-Authentication Framework Classes 实现所需的身份验证机制可能是最简单的。

一般来说,这些类是为了支持这样的场景而设计的,在这些场景中,身份验证机制由一些外部系统提供,以确保 http 请求包含可以被任何 webapp 使用的某种身份验证信息。在您的情况下,身份验证信息似乎位于可用于识别用户的 cookie 中。

假设您已经有一个 UserDetailsService 实现来按用户名查找用户,您可以非常轻松地实现您的要求:

1) 创建AbstractPreAuthenticatedProcessingFilter 的子类,实现抽象方法getPreAuthenticatedPrincipal(HttpServletRequest) 以从cookie 中提取主体(用户名)。如果请求中没有凭据,还有另一种抽象方法 (getPreAuthenticatedCredentials()) 可以简单地返回 null。抽象超类中已经实现的逻辑然后创建一个包含提取的主体的身份验证令牌,并将其提交给身份验证管理器。

2) 创建一个 PreAuthenticatedAuthenticationProvider 类型的 bean,它将从身份验证管理器接收身份验证令牌,以便完全填充它(加载用户的角色)。此类需要注入UserDetailsService 的变体,它采用整个身份验证令牌而不仅仅是用户名。您可以简单地使用UserDetailsByNameServiceWrapper 将您原来的UserDetailsService 调整到该界面:

<bean id="preAuthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
    <property name="preAuthenticatedUserDetailsService">
        <bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
            <property name="userDetailsService" ref="yourUserDetailsService"/>
        </bean>
    </property>
</bean>

3) 使用类似于the documentation 中所示的配置将事物连接在一起:

<security:http>
    ...
    <security:custom-filter position="PRE_AUTH_FILTER" ref="preAuthFilter" />
</security:http>

<bean id="preAuthFilter" class="com.whatever.YourPreAuthFilter"/>

<security:authentication-manager>
    <security:authentication-provider ref="preAuthAuthProvider" />
</security:authentication-manager>

因此,实现您的要求所需的只是一个具有单一方法的子类,以及一些额外的配置以将其与现有的支持类连接起来。

【讨论】:

  • 这是一个很好的回应!现在,虽然我将尝试扩展 UsernamePasswordAuthenticationFilter 并覆盖 attemptAuthentication 方法。我会让它首先检查 cookie,如果不存在,它会调用 super attemptAuthentication 方法。 (有两种不同类型的身份验证需要在不同的时间进行。)然后我将添加一个自定义的 AuthenticationProvider 和标准的 UserNameAuth 提供程序实现。不过,我很欣赏你的想法,它们很有帮助!
  • 我最终确实使用了几乎这个精确的解决方案。
【解决方案2】:

也许您可以实现自己的扩展 GenericFilterBean 的 cookie 过滤器

配置如下。

<security:http ... >
    ....
    <sec:custom-filter position="FORM_LOGIN_FILTER" ref="cookieAuthenticationFilter" />                                         
</security:http>  

看看BasicAuthenticationFilter。原理应该很相似。

注意:BasicAuthenticationFilter 在不同版本之间存在少许差异。因此,请确保找到合适的供参考。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-10
    • 2020-11-13
    • 2019-02-08
    • 2020-01-04
    • 2012-06-05
    • 2021-12-05
    • 2016-09-14
    相关资源
    最近更新 更多