论坛首页 Java企业应用论坛

springMVC+spring security3对于登入后处理的实现

浏览 8702 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-04-27   最后修改:2010-09-21

直接进入主题吧,有个需求就是记录用户登入的ip和时间及总共的登入次数。这个需求很普通,但是在应用了springsecurity后,我还真一下子找不到该在哪处下手。

 

习惯性的看了下security的filters

filters ArrayList<E>  (id=124)
elementData Object[11]  (id=140)
[0] SecurityContextPersistenceFilter  (id=142)
[1] LogoutFilter  (id=144)
[2] UsernamePasswordAuthenticationFilter  (id=146)
[3] DefaultLoginPageGeneratingFilter  (id=151)
[4] BasicAuthenticationFilter  (id=153)
[5] RequestCacheAwareFilter  (id=155)
[6] SecurityContextHolderAwareRequestFilter  (id=157)
[7] AnonymousAuthenticationFilter  (id=159)
[8] SessionManagementFilter  (id=161)
[9] ExceptionTranslationFilter  (id=163)
[10] FilterSecurityInterceptor  (id=165)
modCount 11
size 11

 对security稍微熟习的就知道主要的验证工作是在

UsernamePasswordAuthenticationFilter  (id=146

 这个filter中完成的。

 

下面简单说一下这个流程:

 

在这个filter中,主要执行attemptAuthentication这个方法

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
      ......

        return this.getAuthenticationManager().authenticate(authRequest);
    }

 注意最后一句,在此处得到AuthenticationManager对象 ,也就是我们在xml文件中定义的

<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="websiteUserDetailsService" />
 </authentication-manager>
 

而AuthenticationManager中最要紧的便是那些providers ,此处是源码

public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;

        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }

            logger.debug("Authentication attempt using " + provider.getClass().getName());

            try {
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            } catch (AccountStatusException e) {
                // SEC-546: Avoid polling additional providers if auth failure is due to invalid account status
                eventPublisher.publishAuthenticationFailure(e, authentication);
                throw e;
            } catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result == null && parent != null) {
            // Allow the parent to try.
            try {
                result = parent.authenticate(authentication);
            } catch (ProviderNotFoundException e) {
                // ignore as we will throw below if no other exception occurred prior to calling parent and the parent
                // may throw ProviderNotFound even though a provider in the child already handled the request
            } catch (AuthenticationException e) {
                lastException = e;
            }
        }

        if (result != null) {
            eventPublisher.publishAuthenticationSuccess(result);
            return result;
        }

        // Parent was null, or didn't authenticate (or throw an exception).

        if (lastException == null) {
            lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
                        new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
        }

        eventPublisher.publishAuthenticationFailure(lastException, authentication);

        throw lastException;
    }

我们通过在xml中配置:

<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="websiteUserDetailsService" />
		<authentication-provider>
			<jdbc-user-service data-source-ref=""/>
		</authentication-provider>
		<authentication-provider>
			<ldap-user-service/>
		</authentication-provider>
  </authentication-manager>

 来提供多个provider

 

 

好了接下来就是关键了:

 

首先我们在<http/>中添加自己的filter,注意是before,如果是position要报错

<custom-filter before="FORM_LOGIN_FILTER" ref="authenticationProcessingFilter"/>

 

然后添加:

<beans:bean id="authenticationProcessingFilter" class="com.mxdba.app.common.springsecurity.MyUsernamePasswordAuthenticationFilter">
	  <beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"></beans:property>
	  <beans:property name="authenticationFailureHandler" ref="authenticationFailureHandler"></beans:property>
	  <beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
</beans:bean>
 

这里的

authenticationSuccessHandler

 就是我们可以动刀子的地方了,注意! 在AbstractAuthenticationProcessingFilter中

private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();

 AuthenticationSuccessHandler的字段名是successHandler,但是其set方法是setAuthenticationSuccessHandler,所以property的name是authenticationSuccessHandler

 

然后便是定义我们自己的bean了

<beans:bean id="authenticationSuccessHandler" class="com.mxdba.app.common.springsecurity.MyAuthenticationSuccessHandler"></beans:bean>

 重写onAuthenticationSuccess方法:

public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws ServletException, IOException {
		
		XXXXX//(具体业务逻辑)
		super.onAuthenticationSuccess(request, response, authentication);
	}

 

注意!!!! 这样还不行

 

因为我的authenticationProcessingFilter,是继承自UsernamePasswordAuthenticationFilter,也就是说我其实把UsernamePasswordAuthenticationFilter给废掉了,因为在UsernamePasswordAuthenticationFilter中不是success就是failure。(这个是我自己猜想的)

 

我发现

authentication-failure-url="/login"

 这个已经不起作用了,所以我需要重新在xml申明

<beans:bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
  		<beans:property name="defaultFailureUrl">
  			<beans:value>/login</beans:value>
  		</beans:property>
  </beans:bean>
 

这样就基本OK了

 

 

 

   发表时间:2010-04-29  
首先说一下,你的思路是对的,做一个过滤器来加入security,实现记录ip和登陆次数,不过看你的描述好像是一个外网项目。
外网项目的权限一般来说比较灵活,我之前也用acegi做了一个外网项目,做了很久,需求也是经常变,最后还是改了很多acegi的源码才实现的。项目完工后,acegi也是面目全非了,得出的结论就是外网项目不如自己来控制权限方便。
0 请登录后投票
   发表时间:2010-05-03  
感谢 caoyangx 的回复

对于你所说的可能算是一个仁者见仁智者见智的问题了。

我在没有接触security3之前,也看过acegi的书,当时的感觉是“重”。书中有那么一句让我印象很深刻“学acegi,可以顺便很好的复习一下spring的知识”。

不过时隔没多久security3就出来了,我看了pdf,感觉相比acegi来说,轻多了,无论是从配置还是思路上,更清晰,更明了。

我的想法是,springsecurity相当于一层外皮,因为他是filter,其实最大的好处就是对业务逻辑的侵入很小!

所以在粗颗粒的控制完全可以使用springsecurity,而与业务逻辑有相当多的耦合的部分,也完全可以自己再加以实现
0 请登录后投票
   发表时间:2010-09-06  
上面配置也是对的。

我觉得在<form-login> 配置authentication-success-handler-ref 属性比较好!

<form-login login-page="/login" authentication-failure-url="/login?error" default-target-url="/" authentication-success-handler-ref="authenticationSuccessHandler" />
0 请登录后投票
   发表时间:2010-09-18  
引用

注意!!!! 这样还不行

因为我的authenticationProcessingFilter,是继承自UsernamePasswordAuthenticationFilter,也就是说我其实把UsernamePasswordAuthenticationFilter给废掉了,因为在UsernamePasswordAuthenticationFilter中不是success就是failure。(这个是我自己猜想的)


spring中的配置里面,有namespaceParser之类的,如果有 在 <http /> 中配置 auto-config="true"的话,会自动产生 <form-login /> <remember-me />等filters,这样<form-login />就自动的配置了UsernamePasswordAuthenticationFilter ,如果这个设置不去掉的话,再加上自定义的UsernamePasswordAuthenticationFilter这个时候才会报错吧?
0 请登录后投票
   发表时间:2010-09-18  
gogomarine

你说的问题我看了下我当初的配置文件,在<http/>中我没有加auto-config="true",不知是否一定需要显示设置auto-config="false"才可以,有空我去试试,感谢
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics