我有一个 SpringMVC Web 应用程序,它需要通过发送用户名和密码来使用 Spring Security 对 RESTful Web 服务进行身份验证。当用户登录时,需要为用户的浏览器设置一个 cookie,并且在随后的调用中,用户会话通过使用 cookie 与另一个 RESTful Web 服务进行验证。
我一直在到处寻找,但我找不到一个很好的例子来说明如何做到这一点,我所有的尝试都是徒劳的。
这是我的想法:
我可以声明两个身份验证提供程序,第一个检查 cookie,如果它由于任何原因失败,它会转到第二个检查用户名和密码(如果该请求中没有用户名和密码,也会失败) .
两种服务每次都返回用户的权限,spring security是“无状态的”。
另一方面,我质疑自己这种方法是否正确,因为很难找到一个例子或其他有同样问题的人。这种方法是错误的吗?
我想这样做而不只是 JDBC 身份验证的原因是因为我的整个 Web 应用程序是无状态的,并且始终通过包装“请愿队列”的 RESTful Web 服务访问数据库,我希望尊重用户身份验证和验证也。
到目前为止我尝试了什么?我可以粘贴长长的 springSecurity-context.xml,但我现在只列出它们:
- 使用带有 authenticationSuccessHandler 的自定义 authenticationFilter。显然不起作用,因为此时用户已经登录。
- 实现入口点引用过滤器。
- 在 BASIC_AUTH_FILTER 位置做一个自定义过滤器
- 制作一个自定义身份验证提供程序(苦苦挣扎,没有运气!)。当我得到一些答案时,我正在重试。
- 当我决定写一个问题时,我开始使用 CAS。也许将来我可以考虑在我的 web 应用程序中使用 CAS 服务器,但就目前而言,这感觉有点矫枉过正。
提前致谢!
顺便说一句,我正在使用 Spring Security 3.1.4 和 Spring MVC 3.2.3
编辑:感谢@coder ANSWER,我能够做到
以下是我所做的一些说明,我将尝试记录所有这些并将其发布在这里或很快的某个博客文章中:
<http use-expressions="true" create-session="stateless" entry-point-ref="loginUrlAuthenticationEntryPoint"
authentication-manager-ref="customAuthenticationManager">
<custom-filter ref="restAuthenticationFilter" position="FORM_LOGIN_FILTER" />
<custom-filter ref="restPreAuthFilter" position="PRE_AUTH_FILTER" />
<intercept-url pattern="/signin/**" access="permitAll" />
<intercept-url pattern="/img/**" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
</http>
<authentication-manager id="authManager" alias="authManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<beans:bean id="restPreAuthFilter" class="com.company.CustomPreAuthenticatedFilter">
<beans:property name="cookieName" value="SessionCookie" />
<beans:property name="checkForPrincipalChanges" value="true" />
<beans:property name="authenticationManager" ref="authManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="com.company.CustomPreAuthProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="userDetailsService" class="com.company.CustomUserDetailsService" />
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/signin" />
</beans:bean>
<beans:bean id="customAuthenticationManager"
class="com.company.CustomAuthenticationManager" />
<beans:bean id="restAuthenticationFilter"
class="com.company.CustomFormLoginFilter">
<beans:property name="filterProcessesUrl" value="/signin/authenticate" />
<beans:property name="authenticationManager" ref="customAuthenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
自定义实现是这样的:
// Here, the idea is to write authenticate method and return a new UsernamePasswordAuthenticationToken
public class CustomAuthenticationManager implements AuthenticationManager { ... }
// Write attemptAuthentication method and return UsernamePasswordAuthenticationToken
public class CustomFormLoginFilter extends UsernamePasswordAuthenticationFilter { ... }
// Write getPreAuthenticatedPrincipal and getPreAuthenticatedCredentials methods and return cookieName and cookieValue respectively
public class CustomPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter { ... }
// Write authenticate method and return Authentication auth = new UsernamePasswordAuthenticationToken(name, token, grantedAuths); (or null if can't be pre-authenticated)
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider{ ... }
// Write loadUserByUsername method and return a new UserDetails user = new User("hectorg87", "123456", Collections.singletonList(new GrantedAuthorityImpl("ROLE_USER")));
public class CustomUserDetailsService implements UserDetailsService { ... }