Spring Security3.1登陆验证 替换 usernamepasswordfilter
一、前言
????? 在上一篇http://blog.csdn.net/k10509806/archive/2011/04/28/6369131.aspx文章中,提到的MyUserDetailServiceImpl获取用户权限,在用户没有登陆的时候,Spring Security会让我们自动跳转到默认的登陆界面,但在实际应用绝大多数是用我们自己的登陆界面的,其中就包括一些我们自己的逻辑,比如验证码。所以本人又研究一下,终于摸清了一些如何配置自己的登陆界面的办法。在这里献丑了。
?
二、Spring Security的过滤器
??????通过DEBUG可以看到Spring Security的Filter的顺序
Security filter chain: [
? ConcurrentSessionFilter
? SecurityContextPersistenceFilter
? LogoutFilter
? MyUsernamePasswordAuthenticationFilter
? RequestCacheAwareFilter
? SecurityContextHolderAwareRequestFilter
? RememberMeAuthenticationFilter
? AnonymousAuthenticationFilter
? SessionManagementFilter
? ExceptionTranslationFilter
? MySecurityFilter
? FilterSecurityInterceptor
]
Spring Security的登陆验证用的就是MyUsernamePasswordAuthenticationFilter,所以要实现我们自己的验证,可以写一个类并继承MyUsernamePasswordAuthenticationFilter类,重写attemptAuthentication方法。
?
三、applicationContext-Security.xml配置
?
[xhtml]?view plaincopy- <?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/>????????
- ????<http?pattern="/js/**"?security="none"/>??
- ????<http?pattern="/resource/**"?security="none"></http>??
- ????<http?pattern="/login.jsp"?security="none"/>??
- ??????
- ????<http?use-expressions="true"?entry-point-ref="authenticationProcessingFilterEntryPoint">??
- ????????<logout/>??
- ????????<!--?实现免登陆验证?-->??
- ????????<remember-me?/>??
- ????????<session-management?invalid-session-url="/timeout.jsp">??
- ????????????<concurrency-control?max-sessions="10"?error-if-maximum-exceeded="true"?/>??
- ????????</session-management>??
- ??????????
- ????????<custom-filter?ref="loginFilter"?position="FORM_LOGIN_FILTER"??/>??
- ????????<custom-filter?ref="securityFilter"?before="FILTER_SECURITY_INTERCEPTOR"/>??
- ????</http>??
- ??????
- ????<!--?登录验证器?-->??
- ????<beans:bean?id="loginFilter"??
- ????????class="com.huaxin.security.MyUsernamePasswordAuthenticationFilter">??
- ????????<!--?处理登录的action?-->??
- ????????<beans:property?name="filterProcessesUrl"?value="/j_spring_security_check"></beans:property>??
- ????????????????<!--?验证成功后的处理-->??
- ????????<beans:property?name="authenticationSuccessHandler"?ref="loginLogAuthenticationSuccessHandler"></beans:property>??
- ????????????????<!--?验证失败后的处理-->??
- ????????<beans:property?name="authenticationFailureHandler"?ref="simpleUrlAuthenticationFailureHandler"></beans:property>??
- ????????<beans:property?name="authenticationManager"?ref="myAuthenticationManager"></beans:property>??
- ????????<!--?注入DAO为了查询相应的用户?-->??
- ????????<beans:property?name="usersDao"?ref="usersDao"></beans:property>??
- ????</beans:bean>??
- ????<beans:bean?id="loginLogAuthenticationSuccessHandler"??
- ????????class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">??
- ????????<beans:property?name="defaultTargetUrl"?value="/index.jsp"></beans:property>??
- ????</beans:bean>??
- ????<beans:bean?id="simpleUrlAuthenticationFailureHandler"??
- ????????class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">??
- ????????<!--?可以配置相应的跳转方式。属性forwardToDestination为true采用forward?false为sendRedirect?-->??
- ????????<beans:property?name="defaultFailureUrl"?value="/login.jsp"></beans:property>??
- ????</beans:bean>??
- ??????
- ????<!--?认证过滤器?-->??
- ????<beans:bean?id="securityFilter"?class="com.huaxin.security.MySecurityFilter">??
- ????????<!--?用户拥有的权限?-->??
- ????????<beans:property?name="authenticationManager"?ref="myAuthenticationManager"?/>??
- ????????<!--?用户是否拥有所请求资源的权限?-->??
- ????????<beans:property?name="accessDecisionManager"?ref="myAccessDecisionManager"?/>??
- ????????<!--?资源与权限对应关系?-->??
- ????????<beans:property?name="securityMetadataSource"?ref="mySecurityMetadataSource"?/>??
- ????</beans:bean>??
- ????<!--?实现了UserDetailsService的Bean?-->??
- ????<authentication-manager?alias="myAuthenticationManager">??
- ????????<authentication-provider?user-service-ref="myUserDetailServiceImpl"?/>??
- ????</authentication-manager>??
- ??????
- ????<beans:bean?id="myAccessDecisionManager"?class="com.huaxin.security.MyAccessDecisionManager"></beans:bean>??
- ????<beans:bean?id="mySecurityMetadataSource"?class="com.huaxin.security.MySecurityMetadataSource">??
- ????????<beans:constructor-arg?name="resourcesDao"?ref="resourcesDao"></beans:constructor-arg>??
- ????</beans:bean>??
- ????<beans:bean?id="myUserDetailServiceImpl"?class="com.huaxin.security.MyUserDetailServiceImpl">??
- ????????<beans:property?name="usersDao"?ref="usersDao"></beans:property>??
- ????</beans:bean>??
- ??????
- ????<!--?未登录的切入点?-->??
- ????<beans:bean?id="authenticationProcessingFilterEntryPoint"?class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">??
- ????????<beans:property?name="loginFormUrl"?value="/login.jsp"></beans:property>??
- ????</beans:bean>??
- </beans:beans>??
?
这里特别要说明一下,我们的<http>标签不能配置auto-config,因为这样配置后,依然会采用Spring Security的Filter Chain会与下面我们配的custom-filter冲突,最好会抛异常。还有配置一个切入点entry-point-ref="authenticationProcessingFilterEntryPoint",为了在未登陆的时候,跳转到哪个页面,不配也会抛异常。
?<custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"? /> position表示替换掉Spring Security原来默认的登陆验证Filter。
?
四、MyUsernamePasswordAuthenticationFilter
?
[java]?view plaincopy- package?com.huaxin.security;??
- ??
- import?javax.servlet.http.HttpServletRequest;??
- import?javax.servlet.http.HttpServletResponse;??
- import?javax.servlet.http.HttpSession;??
- ??
- import?org.apache.commons.lang.xwork.StringUtils;??
- import?org.springframework.security.authentication.AuthenticationServiceException;??
- import?org.springframework.security.authentication.UsernamePasswordAuthenticationToken;??
- import?org.springframework.security.core.Authentication;??
- import?org.springframework.security.core.AuthenticationException;??
- import?org.springframework.security.web.WebAttributes;??
- import?org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;??
- ??
- import?com.huaxin.bean.Users;??
- import?com.huaxin.dao.UsersDao;??
- ??
- /*?
- ?*??
- ?*?UsernamePasswordAuthenticationFilter源码?
- ????attemptAuthentication?
- ????????this.getAuthenticationManager()?
- ????????????ProviderManager.java?
- ????????????????authenticate(UsernamePasswordAuthenticationToken?authRequest)?
- ????????????????????AbstractUserDetailsAuthenticationProvider.java?
- ????????????????????????authenticate(Authentication?authentication)?
- ????????????????????????????P155?user?=?retrieveUser(username,?(UsernamePasswordAuthenticationToken)?authentication);?
- ????????????????????????????????DaoAuthenticationProvider.java?
- ????????????????????????????????????P86?loadUserByUsername?
- ?*/??
- public?class?MyUsernamePasswordAuthenticationFilter?extends?UsernamePasswordAuthenticationFilter{??
- ????public?static?final?String?VALIDATE_CODE?=?"validateCode";??
- ????public?static?final?String?USERNAME?=?"username";??
- ????public?static?final?String?PASSWORD?=?"password";??
- ??????
- ????private?UsersDao?usersDao;??
- ????public?UsersDao?getUsersDao()?{??
- ????????return?usersDao;??
- ????}??
- ????public?void?setUsersDao(UsersDao?usersDao)?{??
- ????????this.usersDao?=?usersDao;??
- ????}??
- ??
- ????@Override??
- ????public?Authentication?attemptAuthentication(HttpServletRequest?request,?HttpServletResponse?response)?throws?AuthenticationException?{??
- ????????if?(!request.getMethod().equals("POST"))?{??
- ????????????throw?new?AuthenticationServiceException("Authentication?method?not?supported:?"?+?request.getMethod());??
- ????????}??
- ????????//检测验证码??
- ????????checkValidateCode(request);??
- ??????????
- ????????String?username?=?obtainUsername(request);??
- ????????String?password?=?obtainPassword(request);??
- ??????????
- ????????//验证用户账号与密码是否对应??
- ????????username?=?username.trim();??
- ??????????
- ????????Users?users?=?this.usersDao.findByName(username);??
- ??????????
- ????????if(users?==?null?||?!users.getPassword().equals(password))?{??
- ????/*?
- ??????????????在我们配置的simpleUrlAuthenticationFailureHandler处理登录失败的处理类在这么一段?
- ????????这样我们可以在登录失败后,向用户提供相应的信息。?
- ????????if?(forwardToDestination)?{?
- ????????????request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,?exception);?
- ????????}?else?{?
- ????????????HttpSession?session?=?request.getSession(false);?
- ?
- ????????????if?(session?!=?null?||?allowSessionCreation)?{?
- ????????????????request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION,?exception);?
- ????????????}?
- ????????}?
- ?????*/??
- ????????????throw?new?AuthenticationServiceException("用户名或者密码错误!");???
- ????????}??
- ??????????
- ????????//UsernamePasswordAuthenticationToken实现?Authentication??
- ????????UsernamePasswordAuthenticationToken?authRequest?=?new?UsernamePasswordAuthenticationToken(username,?password);??
- ????????//?Place?the?last?username?attempted?into?HttpSession?for?views??
- ??????????
- ????????//?允许子类设置详细属性??
- ????????setDetails(request,?authRequest);??
- ??????????
- ????????//?运行UserDetailsService的loadUserByUsername?再次封装Authentication??
- ????????return?this.getAuthenticationManager().authenticate(authRequest);??
- ????}??
- ??????
- ????protected?void?checkValidateCode(HttpServletRequest?request)?{???
- ????????HttpSession?session?=?request.getSession();??
- ??????????
- ????????String?sessionValidateCode?=?obtainSessionValidateCode(session);???
- ????????//让上一次的验证码失效??
- ????????session.setAttribute(VALIDATE_CODE,?null);??
- ????????String?validateCodeParameter?=?obtainValidateCodeParameter(request);????
- ????????if?(StringUtils.isEmpty(validateCodeParameter)?||?!sessionValidateCode.equalsIgnoreCase(validateCodeParameter))?{????
- ????????????throw?new?AuthenticationServiceException("验证码错误!");????
- ????????}????
- ????}??
- ??????
- ????private?String?obtainValidateCodeParameter(HttpServletRequest?request)?{??
- ????????Object?obj?=?request.getParameter(VALIDATE_CODE);??
- ????????return?null?==?obj???""?:?obj.toString();??
- ????}??
- ??
- ????protected?String?obtainSessionValidateCode(HttpSession?session)?{??
- ????????Object?obj?=?session.getAttribute(VALIDATE_CODE);??
- ????????return?null?==?obj???""?:?obj.toString();??
- ????}??
- ??
- ????@Override??
- ????protected?String?obtainUsername(HttpServletRequest?request)?{??
- ????????Object?obj?=?request.getParameter(USERNAME);??
- ????????return?null?==?obj???""?:?obj.toString();??
- ????}??
- ??
- ????@Override??
- ????protected?String?obtainPassword(HttpServletRequest?request)?{??
- ????????Object?obj?=?request.getParameter(PASSWORD);??
- ????????return?null?==?obj???""?:?obj.toString();??
- ????}??
- ??????
- ??????
- }??
?
有时间,大家看看源码吧。
?
五、login.jsp
?
[php]?view plaincopy- <body>??
- ??<span?style="color:red"><%=session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)?%></span>??
- ???<form?action="j_spring_security_check"?method="post">??
- ????Account:<Input?name="username"/><br/>??
- ????Password:<input?name="password"?type="password"/><br/>??
- ????<input?value="submit"?type="submit"/>??
- ???</form>??
- ?</body>??
?
?
六、完了,源码大家可以看下我上一篇文章http://blog.csdn.net/k10509806/archive/2011/04/28/6369131.aspx。