【问题标题】:How can I allow a user override with Spring Security?如何允许用户使用 Spring Security 覆盖?
【发布时间】:2011-11-25 09:56:57
【问题描述】:

在我的 Spring MVC Web 应用程序中,某些区域只有具有足够权限的用户才能访问。我需要能够允许用户以其他用户身份登录才能使用这些页面(有点像覆盖),而不仅仅是“拒绝访问”消息。

如何使用 Spring Security 做到这一点?

这是我想要的流程,更详细一点:

  1. 用户 A 从外部应用程序进入页面 X 并通过标头进行身份验证
  2. 用户 A 无权使用页面 X,因此被带到登录屏幕并显示一条消息,指示他们必须以具有足够权限的用户身份登录才能使用此页面
  3. 用户 B 登录,并拥有足够的权限,并被带到页面 X。

注意:页面 X 有一个大而长的查询字符串需要保留。

如何使用 Spring Security 做到这一点?


这是我的 Spring Security 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/security 
            http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <debug />

    <global-method-security pre-post-annotations="enabled">
        <!-- AspectJ pointcut expression that locates our "post" method and applies 
            security that way <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" 
            access="ROLE_TELLER"/> -->
    </global-method-security>

    <!-- Allow anyone to get the static resources and the login page by not applying the security filter chain -->
    <http pattern="/resources/**" security="none" />
    <http pattern="/css/**" security="none" />
    <http pattern="/img/**" security="none" />
    <http pattern="/js/**" security="none" />

    <!-- Lock everything down -->
    <http 
        auto-config="true"
        use-expressions="true" 
        disable-url-rewriting="true">

        <!-- Define the URL access rules -->
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/about/**" access="permitAll and !hasRole('blocked')" />
        <intercept-url pattern="/users/**" access="hasRole('user')" />
        <intercept-url pattern="/reviews/new**" access="hasRole('reviewer')" />
        <intercept-url pattern="/**" access="hasRole('user')" />

        <form-login 
            login-page="/login" />

        <logout logout-url="/logout" /> 

        <access-denied-handler error-page="/login?reason=accessDenied"/>

        <!-- Limit the number of sessions a user can have to only 1 -->
        <session-management>
            <concurrency-control max-sessions="1" />
        </session-management>
    </http>

    <authentication-manager>
        <authentication-provider ref="adAuthenticationProvider" />
        <authentication-provider>
            <user-service>
                <user name="superadmin" password="superadminpassword" authorities="user" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

    <beans:bean id="adAuthenticationProvider" class="[REDACTED Package].NestedGroupActiveDirectoryLdapAuthenticationProvider">
        <beans:constructor-arg value="[REDACTED FQDN]" />
        <beans:constructor-arg value="[REDACTED LDAP URL]" />
        <beans:property name="convertSubErrorCodesToExceptions" value="true" />
        <beans:property name="[REDACTED Group Sub-Tree DN]" />
        <beans:property name="userDetailsContextMapper" ref="peerReviewLdapUserDetailsMapper" />
    </beans:bean>

    <beans:bean id="peerReviewLdapUserDetailsMapper" class="[REDACTED Package].PeerReviewLdapUserDetailsMapper">
        <beans:constructor-arg ref="UserDAO" />
    </beans:bean>

</beans:beans>

我正在使用 Spring Security 3.1 Active Directory 连接功能的略微修改版本。修改只是加载用户组的所有组,包括通过组嵌套到达的组,而不仅仅是用户直接属于的组。我还使用了一个自定义用户对象,其中嵌入了我的应用程序的用户对象,以及一个执行正常 LDAP 映射的自定义 LDAP 映射器,然后添加我的用户。

有一个尚未实现的特殊身份验证方案,其中用户基于从外部应用程序(或通过 Kerberos)以单点登录方式传递的用户名进行身份验证。

【问题讨论】:

  • 不同的用例,但与stackoverflow.com/questions/6337825/…有关。
  • @BarendGarvelink - 是的,我看到了,但我不想暂时“假装”成为另一个用户。我要做的是让他们有效地重新验证为不同的用户(本质上是注销登录),然后继续访问他们试图访问但无法访问的页面。

标签: java spring spring-mvc spring-security


【解决方案1】:

你如何检查角色?

如果您像这样在安全上下文中定义它们:

<intercept-url pattern="/adminStuff.html**" access="hasRole('ROLE_ADMIN')" />

您可以在SimpleUrlAuthenticationFailureHandler 中设置defaultFailureUrl,当具有较低权限的用户尝试访问安全 URL 时,FaliureHandler 应将您重定向到可能是您的登录页面的 defaultFailureUrl

您可以在filterFORM_LOGIN_FILTER 位置注入FaliureHandler

<bean id="myFaliureHandler" 
    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <property name="defaultFailureUrl" value="http://yourdomain.com/your-login.html"/>
</bean>

<bean id="myFilter"
   class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
   <property name="authenticationFailureHandler" ref="myFaliureHandler"/>
</bean>    

<http>
  <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>

在评论中回答 1)。

考虑到您的命名空间配置,这将比我想象的要多一些工作。

您需要做的是删除&lt;form-login&gt; 定义,而不是添加一个“自定义”UsernamePasswordAuthenticationFilter(这是处理&lt;form-login&gt; 元素的过滤器)。

您还需要删除&lt;access-denied-handler&gt;

所以你的配置看起来像:

<bean id="myFaliureHandler" 
    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <property name="defaultFailureUrl" value="http://yourdomain.com/your-login.html"/>
</bean>

<bean id="myFilter"
   class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
   <property name="authenticationFailureHandler" ref="myFaliureHandler"/>
   <!-- there are more required properties, but you can read about them in the docs -->
</bean>

<bean id="loginUrlAuthenticationEntryPoint"
   class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
   <property name="loginFormUrl" value="/login"/>
</bean>

<http entry-point-ref="authenticationEntryPoint" auto-config="false">

  <!-- your other http config goes here, just omit the form-login element and the access denied handler -->

  <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" />
</http>

如果您还没有,通常也可以查看spring docs on custom filters。我们目前在我当前的公司中使用此配置,如果用户在页面上没有所需的权限,则强制用户重新登录。

【讨论】:

  • 我有几个问题:1.) 使用命名空间配置(参见我上面的编辑),我应该在哪里挂钩? 2.) authentication 不是故障点,它是 authorization。权限不足的用户需要能够登录并执行操作,但是当他们尝试执行管理操作时,系统会提示他们使用更高权限的用户登录。这样可以吗?
  • @cdeszaq 1) 我将编辑我的 asnwer。 2) 是的,这会这样做,具有较小角色的用户将被要求再次进行身份验证。
【解决方案2】:

解决方案 1

使用您喜欢的任何方式注册应用程序范围ExeptionResolver。例如。

public class MyApplicationErrorResolver extends SimpleMappingExceptionResolver {

    @Autowired
    private List<LogoutHandler> logoutHandlers;

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request,
        HttpServletResponse response, Object handler, Exception ex) {

        if(ex instanceof AccessDeniedException) {
            for(LogoutHandler lh : logoutHandlers) {
                lh.logout(request, response, SecurityContextHolder.getContext().getAuthentication());
            }
            // Not present as a bean. So create it manually.
            SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
            logoutHandler.setInvalidateHttpSession(true);
            logoutHandler.logout(request, response, SecurityContextHolder.getContext().getAuthentication());

            return new ModelAndView(new RedirectView(request.getRequestURL().toString()));
        }

        return super.doResolveException(request, response, handler, ex);
    }
}

将其注册为 bean:

<bean class="package.path.MyApplicationErrorResolver" />

(这就是您注册它所需的全部内容)。这将适用于您的配置。但是您可能需要从配置中删除 &lt;access-denied-handler&gt; 元素。

解决方案 2

另一种方法是使用AccessDeniedHandler。例如:

public class MyAccessDeniedExceptionHandler implements AccessDeniedHandler {

    @Autowired
    private List<LogoutHandler> logoutHandlers;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        for(LogoutHandler lh : logoutHandlers) {
            lh.logout(request, response, SecurityContextHolder.getContext().getAuthentication());
        }

        SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler();
        logoutHandler.setInvalidateHttpSession(true);
        logoutHandler.logout(request, response, SecurityContextHolder.getContext().getAuthentication());

        response.sendRedirect(request.getRequestURL().toString());
    }

}

将其注册为 bean:

<bean id="accesssDeniedHandler" class="package.path.MyAccessDeniedExceptionHandler" />

并在您的配置中指定它:

<access-denied-handler ref="accesssDeniedHandler" />

【讨论】:

    猜你喜欢
    • 2014-04-14
    • 1970-01-01
    • 2021-06-09
    • 2019-01-07
    • 2015-02-02
    • 2019-03-10
    • 2018-12-10
    • 2019-03-23
    • 1970-01-01
    相关资源
    最近更新 更多