Spring Security教程(六)

这里接着上篇的自定义过滤器,这里主要的是配置自定义认证处理的过滤器,并加入到FilterChain的过程。在我们自己不在xml做特殊的配置情况下,security默认的做认证处理的过滤器为UsernamePasswordAuthenticationFilter,通过查看源码知道,做认证处理的方法为attemptAuthentication,这个方法的主要作用就是将用户输入的账号和密码,封装成一个UsernamePasswordAuthenticationToken对象,然后通过setDetails方法将这个对象储存起来,然后调用this.getAuthenticationManager().authenticate(authRequest)方法返回一个Authentication对象。其中这个过程this.getAuthenticationManager().authenticate(authRequest)又调用的其他的许多类,这里简单的讲解下:

UsernamePasswordAuthenticationFilter-->ProviderManager-->AbstractUserDetailsAuthenticationProvider-->DaoAuthenticationProvider-->JdbcDaoImpl
当输入用户名和密码后,点击登陆到达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。

一 自定义MyUsernamePasswordAuthenticationFilter

这个类可以继承UsernamePasswordAuthenticationFilter,然后重写attemptAuthentication方法,这个方法是登陆的入口方法。
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并将其加入到FilterChain中去

MyUsernamePasswordAuthenticationFilter有filterProcessesUrl属性为登陆的过滤的地址,authenticationManager为authentication-manager标签中配置的东西,authenticationSuccessHandler为验证成功后跳转的处理器,authenticationFailureHandler为验证失败的处理器。另外还要配置一个出登陆引导的处bean:LoginUrlAuthenticationEntryPoint
配置代码如下:
<?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>
其他的一些配置在 教程五有详细的讲解。

三 结果

因为处理验证的过滤器不一样,其他的和教程五一样,结果这里就不展示了,参考教程五

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值