這裏接着上篇的自定義過濾器,這裏主要的是配置自定義認證處理的過濾器,並加入到FilterChain的過程。java
在咱們本身不在xml作特殊的配置狀況下,security默認的作認證處理的過濾器爲UsernamePasswordAuthenticationFilter,經過查看源碼知道,作認證處理的方法爲attemptAuthentication,這個方法的主要做用就是將用戶輸入的帳號和密碼,封裝成一個UsernamePasswordAuthenticationToken對象,而後經過setDetails方法將這個對象儲存起來,而後調用this.getAuthenticationManager().authenticate(authRequest)方法返回一個Authentication對象。其中這個過程this.getAuthenticationManager().authenticate(authRequest)又調用的其餘的許多類,這裏簡單的講解下:mysql
UsernamePasswordAuthenticationFilter-->ProviderManager-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider-->JdbcDaoImplweb
根據這個順序我畫了個圖方便記憶spring
當輸入用戶名和密碼後,點擊登錄到達UsernamePasswordAuthenticationFilter的attemptAuthentication方法,這個方法是登錄的入口,而後其調用ProviderManager中的authenticate方法,而ProviderManager委託給AbstractUserDetailsAuthenticationProvider的authenticate作,而後AbstractUserDetailsAuthenticationProvider又調用DaoAuthenticationProvider中的retrieveUser,在DaoAuthenticationProvider類的retrieveUser方法中,由於要經過輸入的用戶名獲取到一個UserDetails,因此其調用JdbcDaoImpl中的loadUserByUsername方法,該方法給它的調用者返回一個查詢到的用戶(UserDetails),最終AbstractUserDetailsAuthenticationProvider的authenticate方法中會獲得一個UserDetails對象user,而後接着執行preAuthenticationChecks.check(user)和additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);其中前面這個方法是判斷,查詢的用戶是否可用或被鎖等,後面的則是判斷查詢到的user對象的密碼是否和authentication(這個對象其實就是存儲用戶輸入的用戶名和密碼)的密碼同樣,若同樣則表示登錄成功,若錯誤,則throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);Bad credentials這個消息就是登錄失敗後的信息。初步的講解了登錄過程當中類的調用,那麼下面這個例子就是自定義一個MyUsernamePasswordAuthenticationFilter來代替默認的 UsernamePasswordAuthenticationFilter。sql
package com.zmc.demo; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; 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.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.util.StringUtils; /** * @classname MyUsernamePasswordAuthenticationFilter * @author ZMC * @time 2017-1-13 * */ public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public static final String USERNAME = "j_username"; public static final String PASSWORD = "j_password"; /** * @Description:用戶登陸驗證方法入口 * @param :args * @return * @throws Exception */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = this.obtainUsername(request); String password = this.obtainPassword(request); // 加密密碼(根據「密碼{用戶名})進行加密 // String sh1Password = password + "{" + username + "}"; // PasswordEncoder passwordEncoder = new // StandardPasswordEncoderForSha1(); // String result = passwordEncoder.encode(sh1Password); // UserInfo userDetails = (UserInfo) // userDetailsService.loadUserByUsername(username); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } /** * @Description:獲取密碼 * @param :args * @return * @throws Exception */ @Override protected String obtainPassword(HttpServletRequest request) { // TODO Auto-generated method stub Object obj = request.getParameter(PASSWORD); return null == obj ? "" : obj.toString(); } /** * @Description:獲取用戶名 * @param :args * @return * @throws Exception */ @Override protected String obtainUsername(HttpServletRequest request) { // TODO Auto-generated method stub Object obj = request.getParameter(USERNAME); return null == obj ? "" : obj.toString().trim().toLowerCase(); } }
上述的代碼這樣寫其實和默認的UsernamePasswordAuthenticationFilter並無什麼區別,可是這裏主要是學會將自定義的Filter加入到security中的FilterChain中去,實際上這個方法中,通常會直接驗證用戶輸入的和經過用戶名從數據庫裏面查到的用戶的密碼是否一致,若是不一致,就拋異常,不然繼續向下執行。數據庫
MyUsernamePasswordAuthenticationFilter有filterProcessesUrl屬性爲登錄的過濾的地址,authenticationManager爲authentication-manager標籤中配置的東西,authenticationSuccessHandler爲驗證成功後跳轉的處理器,authenticationFailureHandler爲驗證失敗的處理器。另外還要配置一個出登錄引導的處bean:LoginUrlAuthenticationEntryPoint
配置代碼以下:session
<?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/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http pattern="/login.jsp" security="none"></http> <http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint"> <!-- <form-login login-page="/login.jsp" default-target-url="/index.jsp" authentication-failure-url="/login.jsp?error=true" /> --> <logout invalidate-session="true" logout-success-url="/login.jsp" logout-url="/j_spring_security_logout" /> <custom-filter ref="myUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER" /> <!-- 經過配置custom-filter來增長過濾器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默認的過濾器以前執行。 --> <custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" /> </http> <beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <beans:property name="loginFormUrl" value="/login.jsp" /> </beans:bean> <!-- 數據源 --> <beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- 此爲c3p0在spring中直接配置datasource c3p0是一個開源的JDBC鏈接池 --> <beans:property name="driverClass" value="com.mysql.jdbc.Driver" /> <beans:property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springsecuritydemo?useUnicode=true&characterEncoding=UTF-8" /> <beans:property name="user" value="root" /> <beans:property name="password" value="" /> <beans:property name="maxPoolSize" value="50"></beans:property> <beans:property name="minPoolSize" value="10"></beans:property> <beans:property name="initialPoolSize" value="10"></beans:property> <beans:property name="maxIdleTime" value="25000"></beans:property> <beans:property name="acquireIncrement" value="1"></beans:property> <beans:property name="acquireRetryAttempts" value="30"></beans:property> <beans:property name="acquireRetryDelay" value="1000"></beans:property> <beans:property name="testConnectionOnCheckin" value="true"></beans:property> <beans:property name="idleConnectionTestPeriod" value="18000"></beans:property> <beans:property name="checkoutTimeout" value="5000"></beans:property> <beans:property name="automaticTestTable" value="t_c3p0"></beans:property> </beans:bean> <beans:bean id="builder" class="com.zmc.demo.JdbcRequestMapBulider"> <beans:property name="dataSource" ref="dataSource" /> <beans:property name="resourceQuery" value="select re.res_string,r.name from role r,resc re,resc_role rr where r.id=rr.role_id and re.id=rr.resc_id" /> </beans:bean> <beans:bean id="myUsernamePasswordAuthenticationFilter" class="com.zmc.demo.MyUsernamePasswordAuthenticationFilter "> <beans:property name="filterProcessesUrl" value="/j_spring_security_check" /> <beans:property name="authenticationManager" ref="authenticationManager" /> <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler" /> <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler" /> </beans:bean> <beans:bean id="loginLogAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> <beans:property name="targetUrlParameter" value="/index.jsp" /> </beans:bean> <beans:bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <beans:property name="defaultFailureUrl" value="/login.jsp" /> </beans:bean> <!-- 認證過濾器 --> <beans:bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> <!-- 用戶擁有的權限 --> <beans:property name="accessDecisionManager" ref="accessDecisionManager" /> <!-- 用戶是否擁有所請求資源的權限 --> <beans:property name="authenticationManager" ref="authenticationManager" /> <!-- 資源與權限對應關係 --> <beans:property name="securityMetadataSource" ref="securityMetadataSource" /> </beans:bean> <!-- acl領域模型 --> <beans:bean class="com.zmc.demo.MyAccessDecisionManager" id="accessDecisionManager"> </beans:bean> <!-- --> <authentication-manager alias="authenticationManager"> <authentication-provider> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username,password,status as enabled from user where username = ?" authorities-by-username-query="select user.username,role.name from user,role,user_role where user.id=user_role.user_id and user_role.role_id=role.id and user.username=?" /> </authentication-provider> </authentication-manager> <beans:bean id="securityMetadataSource" class="com.zmc.demo.MyFilterInvocationSecurityMetadataSource"> <beans:property name="builder" ref="builder"></beans:property> </beans:bean> </beans:beans>