这里接着上篇的自定义过滤器,这里主要的是配置自定义认证处理的过滤器,并加入到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>