精华帖 (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了
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-04-29
首先说一下,你的思路是对的,做一个过滤器来加入security,实现记录ip和登陆次数,不过看你的描述好像是一个外网项目。
外网项目的权限一般来说比较灵活,我之前也用acegi做了一个外网项目,做了很久,需求也是经常变,最后还是改了很多acegi的源码才实现的。项目完工后,acegi也是面目全非了,得出的结论就是外网项目不如自己来控制权限方便。 |
|
返回顶楼 | |
发表时间:2010-05-03
感谢 caoyangx 的回复
对于你所说的可能算是一个仁者见仁智者见智的问题了。 我在没有接触security3之前,也看过acegi的书,当时的感觉是“重”。书中有那么一句让我印象很深刻“学acegi,可以顺便很好的复习一下spring的知识”。 不过时隔没多久security3就出来了,我看了pdf,感觉相比acegi来说,轻多了,无论是从配置还是思路上,更清晰,更明了。 我的想法是,springsecurity相当于一层外皮,因为他是filter,其实最大的好处就是对业务逻辑的侵入很小! 所以在粗颗粒的控制完全可以使用springsecurity,而与业务逻辑有相当多的耦合的部分,也完全可以自己再加以实现 |
|
返回顶楼 | |
发表时间: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" /> |
|
返回顶楼 | |
发表时间:2010-09-18
引用 注意!!!! 这样还不行 因为我的authenticationProcessingFilter,是继承自UsernamePasswordAuthenticationFilter,也就是说我其实把UsernamePasswordAuthenticationFilter给废掉了,因为在UsernamePasswordAuthenticationFilter中不是success就是failure。(这个是我自己猜想的) spring中的配置里面,有namespaceParser之类的,如果有 在 <http /> 中配置 auto-config="true"的话,会自动产生 <form-login /> <remember-me />等filters,这样<form-login />就自动的配置了UsernamePasswordAuthenticationFilter ,如果这个设置不去掉的话,再加上自定义的UsernamePasswordAuthenticationFilter这个时候才会报错吧? |
|
返回顶楼 | |
发表时间:2010-09-18
gogomarine
你说的问题我看了下我当初的配置文件,在<http/>中我没有加auto-config="true",不知是否一定需要显示设置auto-config="false"才可以,有空我去试试,感谢 |
|
返回顶楼 | |
浏览 8702 次