SpringSecurity3.0配置前后台登录

 

    在此,谢谢http://blog.csdn.net/k10509806/article/details/6436987这篇文章的作者,我参考了其中Filter部分的内容,再次感谢!

 

    先说两句没用的话,本想在CSDN上发表这篇文章了,谁曾想第一天开通博客,居然不让用,要等三天时间,要知道,对于程序员来说,时间宝贵,况且又有个什么密码泄漏事件,想来想去还是来iteye吧。

    第一次在iteye上面写东西,不知道最终会是什么效果,期待中...... 

      最近想在闲暇时间,写个CMS玩玩, 需要用到前后台页面登录和登出,因为使用SpringSecurity的时间也不长,相对来说对与这种需求还是有一点点复杂的,所以用了几个小时的研究,终于满足了这种需求。  其实原理很简单,就是重写实现类,根据访问的路径判断页面是前台还是后台,实现跳转到不同登录页面和登出成功页面。

 

     我用的环境是Spring3.0和SpringSecurity3.0,MyEclipse自动引包就行了。

 

    需要注意的是,这里的大部分Java代码,请看springsecurity3的源码,我是在源码的基础之上,进行重构的,我所写的部分,大多都写有注释,对于spring源码,因为很忙,我也没有细看,不明白的,留言好了。

 

     先看一下配置的http节点。

 

 <http auto-config="false" entry-point-ref="loginUrlEntryPoint">
    <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
    <intercept-url pattern="/admin/jsp/login/login.jsp* filters="none" />
    <intercept-url pattern="/login.jsp*" filters="none" />
    <intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER" />
    <custom-filter position="FORM_LOGIN_FILTER" ref="loginFilter" />
    <custom-filter position="LOGOUT_FILTER" ref="logoutFilter" />
</http>

 

   很简单,关键部分我用红色标明了,其中的 loginUrlEntryPoint 是实现的AuthenticationEntryPoint接口,用来判断是前台登录还是后台登录, loginFilter 是继承的AbstractAuthenticationProcessingFilter类,实现登录验证的功能。

 

    下面看  loginUrlEntryPoint 的说明。

    配置如下:

 

<beans:bean id="loginUrlEntryPoint" class="service.LoginUrlEntryPoint" />

   代码如下:

    package service;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

public class LoginUrlEntryPoint implements AuthenticationEntryPoint {

	public void commence(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException authException)
			throws IOException, ServletException {
		String targetUrl = null;
		String url = request.getRequestURI();

		if (url.indexOf("admin") != -1) {	//判断url中是否包含admin,如果包含,则判断为后台,否则为前台
			targetUrl = "/admin/jsp/login/login.jsp";
			request.getSession().setAttribute("loginType", "back");	//前登录状态保存到session中,在后面这个值会频繁用到
		} else {
			targetUrl = "/login.jsp";
			request.getSession().setAttribute("loginType", "front");//同理
		}

		targetUrl = request.getContextPath() + targetUrl;
		response.sendRedirect(targetUrl);
	}
}

下面再看一下loginFilter的配置:

 

<beans:bean id="loginFilter" class="service.LoginFilter">
	<beans:constructor-arg name="validateUrl" type="java.lang.String" value="/j_spring_security_check" />
	<beans:property name="usernameParameter" value="username"></beans:property>
	<beans:property name="passwordParameter" value="password"></beans:property>
	<beans:property name="authenticationManager" ref="authenticationManager" />
	<beans:property name="authenticationFailureHandler" ref="failureHandler" />
	<beans:property name="authenticationSuccessHandler" ref="successHandler" />
</beans:bean>
 

    作一下简要解释,这里的validateUrl参数,是定义执行提交身份验证的URL,提供自定义,和登录页面的action对应即可。usernameParameter是登录页面的用户输入框的id,passwordParameter是密码输入框的id,重点是authenticationFailureHandler和authenticationSuccessHandler,分别是验证成功和失败。

 

    先看一下loginFilter的代码:

 

 

package service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.Assert;

public class LoginFilter extends AbstractAuthenticationProcessingFilter {

	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";

	@Deprecated
	public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";

	private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
	private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
	private boolean postOnly = true;
	
	/**
	 * 构造方法,参数为执行验证的url,在spring配置中进后构造注入
	 */
	public LoginFilter(String validateUrl) {
		super(validateUrl);
	}

	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: "
							+ request.getMethod());
		}

		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();

		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		setDetails(request, authRequest);

		return this.getAuthenticationManager().authenticate(authRequest);
	}

	protected String obtainPassword(HttpServletRequest request) {
		return request.getParameter(passwordParameter);
	}

	protected String obtainUsername(HttpServletRequest request) {
		return request.getParameter(usernameParameter);
	}

	protected void setDetails(HttpServletRequest request,
			UsernamePasswordAuthenticationToken authRequest) {
		authRequest.setDetails(authenticationDetailsSource
				.buildDetails(request));
	}

	public void setUsernameParameter(String usernameParameter) {
		Assert.hasText(usernameParameter,
				"Username parameter must not be empty or null");
		this.usernameParameter = usernameParameter;
	}

	public void setPasswordParameter(String passwordParameter) {
		Assert.hasText(passwordParameter,
				"Password parameter must not be empty or null");
		this.passwordParameter = passwordParameter;
	}

	public void setPostOnly(boolean postOnly) {
		this.postOnly = postOnly;
	}

	public final String getUsernameParameter() {
		return usernameParameter;
	}

	public final String getPasswordParameter() {
		return passwordParameter;
	}
}

 

 

    上面这段代码主要实现了验证url和用户名id、密码id的设置。

 

    再看一下failureHandler的xml配置:

 

<beans:bean id="failureHandler" class="service.LoginFailureHandler">
	<beans:property name="defaultBackTargetUrl"
		value="/admin/jsp/login/login.jsp?error=1" />
	<beans:property name="defaultFrontTargetUrl" value="/login.jsp?error=1" />
</beans:bean>

 

    两个属性分别是前台登录失败和后台登录失败的跳转url,我写的是到登录页面,通过error参数判断是验证失败。

 

    下面看一下failureHandler的代码:

 

package service;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;

public class LoginFailureHandler implements AuthenticationFailureHandler {
	protected final Log logger = LogFactory.getLog(getClass());

	private String defaultFailureUrl;
	private boolean forwardToDestination = false;
	private boolean allowSessionCreation = true;
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	private String defaultBackTargetUrl;	//定义后台登录验证失败跳转地址
	private String defaultFrontTargetUrl;	//定义前台登录验证失败跳转地址
	//定义set方法
	public void setDefaultBackTargetUrl(String defaultBackTargetUrl) {
		this.defaultBackTargetUrl = defaultBackTargetUrl;
	}

	public void setDefaultFrontTargetUrl(String defaultFrontTargetUrl) {
		this.defaultFrontTargetUrl = defaultFrontTargetUrl;
	}

	public LoginFailureHandler() {
	}

	public LoginFailureHandler(String defaultFailureUrl) {
		setDefaultFailureUrl(defaultFailureUrl);
	}

	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {

		saveException(request, exception);
		setFailureTarget(request);	//调用前后台判断方法
		if (forwardToDestination) {
			logger.debug("Forwarding to " + defaultFailureUrl);
			request.getRequestDispatcher(defaultFailureUrl).forward(request,
					response);
		} else {
			logger.debug("Redirecting to " + defaultFailureUrl);
			redirectStrategy.sendRedirect(request, response, defaultFailureUrl);
		}
	}

	protected final void setFailureTarget(HttpServletRequest request) {
		String loginType = (String) request.getSession().getAttribute(
				"loginType");	//获取登录状态,这个值是在前面判断登录入口放入session的
		if (loginType.equals("back")) {
			setDefaultFailureUrl(this.defaultBackTargetUrl);	//如果是后台,前后台跳转地址设置到defaultFailureUrl
		} else if (loginType.equals("front")) {
			setDefaultFailureUrl(this.defaultFrontTargetUrl);	//如果是前台,与后台类似
		}
	}

	protected final void saveException(HttpServletRequest request,
			AuthenticationException exception) {
		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);
			}
		}
	}

	public void setDefaultFailureUrl(String defaultFailureUrl) {
		Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl), "'"
				+ defaultFailureUrl + "' is not a valid redirect URL");
		this.defaultFailureUrl = defaultFailureUrl;
	}

	protected boolean isUseForward() {
		return forwardToDestination;
	}

	public void setUseForward(boolean forwardToDestination) {
		this.forwardToDestination = forwardToDestination;
	}

	public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
		this.redirectStrategy = redirectStrategy;
	}

	protected RedirectStrategy getRedirectStrategy() {
		return redirectStrategy;
	}

	protected boolean isAllowSessionCreation() {
		return allowSessionCreation;
	}

	public void setAllowSessionCreation(boolean allowSessionCreation) {
		this.allowSessionCreation = allowSessionCreation;
	}
}

 

     这里的关键代码已经给了明确注释,很简单,容易理解。这一步骤能够实现登录失败分别跳转到前台、后台的失败页面。

    再看一下successHandler的配置:

 

<beans:bean id="successHandler" class="service.LoginSuccessHandler">
	<beans:property name="defaultBackTargetUrl" value="/admin/index.jsp"></beans:property>
	<beans:property name="defaultFrontTargetUrl" value="/index.jsp"></beans:property>
	<beans:property name="alwaysUseDefaultTargetUrl" value="true"></beans:property>
</beans:bean>

 

    和failureHandler含义一样,不解释了。

 

    下面再看一下successHandler的代码:

 

package service;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class LoginSuccessHandler extends
		AbstractAuthenticationTargetUrlRequestHandler implements
		AuthenticationSuccessHandler {

	private String defaultFrontTargetUrl; // 前台登录成功的跳转地址
	private String defaultBackTargetUrl; // 后台登录成功的跳转地址

	// set方法 spring调用
	public void setDefaultFrontTargetUrl(String defaultFrontTargetUrl) {
		this.defaultFrontTargetUrl = defaultFrontTargetUrl;
	}

	public void setDefaultBackTargetUrl(String defaultBackTargetUrl) {
		this.defaultBackTargetUrl = defaultBackTargetUrl;
	}

	public LoginSuccessHandler() {
	}

	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		String loginType = (String) request.getSession().getAttribute(
				"loginType"); // 获取登录状态,这个值是在前面判断登录入口放入session的
		if (loginType.equals("back")) {
			setDefaultTargetUrl(this.defaultBackTargetUrl); // 如果是后台,前后台跳转地址设置到defaultFailureUrl
		} else if (loginType.equals("front")) {
			setDefaultTargetUrl(this.defaultFrontTargetUrl); // 如果是前台,与后台类似
		}
		handle(request, response, authentication);
		clearAuthenticationAttributes(request);

	}

	protected final void clearAuthenticationAttributes(
			HttpServletRequest request) {
		HttpSession session = request.getSession(false);
		if (session == null) {
			return;
		}
		session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
	}

}

    同样,不解释了,看简单的几条注释。

 

 

    下面实现登出功能,logoutFilter的配置:

 

<beans:bean id="logoutFilter" class="service.LogoutFilter">
	<beans:constructor-arg name="frontLogoutSuccessUrl"
		value="/index.jsp"></beans:constructor-arg>
	<beans:constructor-arg name="backLogoutSuccessUrl"
		value="/admin/index.jsp"></beans:constructor-arg>
	<beans:constructor-arg>
		<beans:list>
			<beans:bean
				class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
		</beans:list>
	</beans:constructor-arg>
	<beans:property name="filterProcessesUrl" value="/j_spring_security_logout" />
</beans:bean>

 这里通过构造注入,分别注入前后台登出成功的url和SecurityContextLogoutHandler,filterProcessesUrl的意思是执行登出的url,默认就是/j_spring_security_logout,不管是前台还是后台,只要执行filterProcessesUrl所对应的url,就会分别跳转到所配置的登出成功页面,下面看一下logoutFilter的代码部分:

 

 

package service;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

public class LogoutFilter extends GenericFilterBean {

	private String filterProcessesUrl = "/j_spring_security_logout";
	private final List<LogoutHandler> handlers;
	private LogoutSuccessHandler logoutSuccessHandler;
	private SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler;	//作临时变量用
	
	private String frontLogoutSuccessUrl;	//前台登出成功跳转页面
	private String backLogoutSuccessUrl;	//后台登出成功跳转页面

	public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,
			LogoutHandler... handlers) {
		Assert.notEmpty(handlers, "LogoutHandlers are required");
		this.handlers = Arrays.asList(handlers);
		Assert.notNull(logoutSuccessHandler,
				"logoutSuccessHandler cannot be null");
		this.logoutSuccessHandler = logoutSuccessHandler;	
	}

	public LogoutFilter(String frontLogoutSuccessUrl,String backLogoutSuccessUrl, LogoutHandler... handlers) {
		Assert.notEmpty(handlers, "LogoutHandlers are required");
		this.handlers = Arrays.asList(handlers);
		Assert.isTrue(
				!StringUtils.hasLength(frontLogoutSuccessUrl)
						|| UrlUtils.isValidRedirectUrl(frontLogoutSuccessUrl),
						frontLogoutSuccessUrl + " isn't a valid redirect URL");
		Assert.isTrue(
				!StringUtils.hasLength(backLogoutSuccessUrl)
						|| UrlUtils.isValidRedirectUrl(backLogoutSuccessUrl),
						backLogoutSuccessUrl + " isn't a valid redirect URL");
		
		SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
		if (StringUtils.hasText(frontLogoutSuccessUrl) && StringUtils.hasText(backLogoutSuccessUrl)) {
			this.frontLogoutSuccessUrl = frontLogoutSuccessUrl;
			this.backLogoutSuccessUrl = backLogoutSuccessUrl;
		}
		this.urlLogoutSuccessHandler = urlLogoutSuccessHandler;//赋值给临时变量,以便于后面判断是前台登录还是后台登录
	}

	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		
		String loginType = (String) request.getSession().getAttribute("loginType");//获取登录类型
		
		if(loginType!=null) {	//这句必须有,否则报空指针
			if (loginType.equals("back")) {
				urlLogoutSuccessHandler.setDefaultTargetUrl(this.backLogoutSuccessUrl);
			} else if (loginType.equals("front")) {
				urlLogoutSuccessHandler.setDefaultTargetUrl(this.frontLogoutSuccessUrl);
			}
		}
		logoutSuccessHandler = urlLogoutSuccessHandler;	//前临时变量赋值给LogoutSuccessHandler,供后面代码调用
		if (requiresLogout(request, response)) {
			Authentication auth = SecurityContextHolder.getContext()
					.getAuthentication();

			if (logger.isDebugEnabled()) {
				logger.debug("Logging out user '" + auth
						+ "' and transferring to logout destination");
			}

			for (LogoutHandler handler : handlers) {
				handler.logout(request, response, auth);
			}

			logoutSuccessHandler.onLogoutSuccess(request, response, auth);

			return;
		}

		chain.doFilter(request, response);
	}

	protected boolean requiresLogout(HttpServletRequest request,
			HttpServletResponse response) {
		String uri = request.getRequestURI();
		int pathParamIndex = uri.indexOf(';');

		if (pathParamIndex > 0) {
			uri = uri.substring(0, pathParamIndex);
		}

		int queryParamIndex = uri.indexOf('?');

		if (queryParamIndex > 0) {
			uri = uri.substring(0, queryParamIndex);
		}

		if ("".equals(request.getContextPath())) {
			return uri.endsWith(filterProcessesUrl);
		}

		return uri.endsWith(request.getContextPath() + filterProcessesUrl);
	}

	public void setFilterProcessesUrl(String filterProcessesUrl) {
		Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl),
				filterProcessesUrl + " isn't a valid value for"
						+ " 'filterProcessesUrl'");
		this.filterProcessesUrl = filterProcessesUrl;
	}

	protected String getFilterProcessesUrl() {
		return filterProcessesUrl;
	}
}

 

    这里也不解释了,真的没什么好说的,说多了都是废话。

 

    可以看出来,几乎所有的代码都有一个共性,就是从session中取出loginType判断前后台,再根据这个值,将跳转地       址赋值给springsecurity,最终实现了所给的需求。

    写的有点混乱,对于写这种文章实在没什么经验,也是一种锻炼。

    最后,希望这些能帮助到朋友们。

 

 

    我的邮箱是:zhbf5156@163.com ,有问题,欢迎一起探讨。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值