spring security实现传参

      本人现在开发的项目中用到了spring security3.1.2来实现登陆验证,但默认的过滤器UsernamePasswordAuthenticationFilter只能传递两个参数j_username和j_password,如果要传递验证码或首次打开的页面URL参数,只能重写UsernamePasswordAuthenticationFilter。

     对于不了解spring security3.1的童鞋,请先阅读本人写的另一篇博客Spring Security权限管理,下面将在这篇博客的基础上,添加一个功能:传递登陆之前的页面URL。公司现在做的项目,如果没有登陆或会话超时,都要求重新登陆,登陆之后会直接跳转到首页,而不是跳转到原来打开的页面,这很恶心。于是本人决心对项目进行改造,使得可以传递最后打开页面的URL,登陆之后直接跳转到这个URL去。

1.重写UsernamePasswordAuthenticationFilter

package com.web.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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;

/**
 * 验证登陆用户名和密码的拦截器,记录最后登陆页面
 * @author brushli
 * @date 2014-10-30
 */
public class MyUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
    public static final String SPRING_SECURITY_FORM_LAST_LOGIN_URL_KEY = "lastLoginUrl";
    public static final String SPRING_SECURITY_SPLIT = ",";

    /**
     * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler}
     */
    @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 String lastLoginUrlParameter = SPRING_SECURITY_FORM_LAST_LOGIN_URL_KEY;
    private String split = SPRING_SECURITY_SPLIT;
    private boolean postOnly = true;

    //~ Constructors ===================================================================================================

    public MyUsernamePasswordAuthenticationFilter() {
        super("/j_spring_security_check");
    }

    //~ Methods ========================================================================================================

    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);
        String lastLoginUrl = obtainLastLoginUrl(request);

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

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

        username = username.trim();
        username = username + SPRING_SECURITY_SPLIT + lastLoginUrl;

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

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

    /**
     * Enables subclasses to override the composition of the password, such as by including additional values
     * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
     * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
     * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the password that will be presented in the <code>Authentication</code> request token to the
     *         <code>AuthenticationManager</code>
     */
    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(passwordParameter);
    }

    /**
     * Enables subclasses to override the composition of the username, such as by including additional values
     * and a separator.
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the username that will be presented in the <code>Authentication</code> request token to the
     *         <code>AuthenticationManager</code>
     */
    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(usernameParameter);
    }
    
    protected String obtainLastLoginUrl(HttpServletRequest request) {
        return request.getParameter(lastLoginUrlParameter);
    }

    /**
     * Provided so that subclasses may configure what is put into the authentication request's details
     * property.
     *
     * @param request that an authentication request is being created for
     * @param authRequest the authentication request object that should have its details set
     */
    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    /**
     * Sets the parameter name which will be used to obtain the username from the login request.
     *
     * @param usernameParameter the parameter name. Defaults to "j_username".
     */
    public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
    }

    /**
     * Sets the parameter name which will be used to obtain the password from the login request..
     *
     * @param passwordParameter the parameter name. Defaults to "j_password".
     */
    public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
    }

    /**
     * Defines whether only HTTP POST requests will be allowed by this filter.
     * If set to true, and an authentication request is received which is not a POST request, an exception will
     * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
     * will be called as if handling a failed authentication.
     * <p>
     * Defaults to <tt>true</tt> but may be overridden by subclasses.
     */
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getUsernameParameter() {
        return usernameParameter;
    }

    public final String getPasswordParameter() {
        return passwordParameter;
    }

	public String getLastLoginUrlParameter() {
		return lastLoginUrlParameter;
	}

	public void setLastLoginUrlParameter(String lastLoginUrlParameter) {
		Assert.hasText(lastLoginUrlParameter, "lastLoginUrl Parameter must not be empty or null");
		this.lastLoginUrlParameter = lastLoginUrlParameter;
	}

	public String getSplit() {
		return split;
	}

	/**
	 * The string to separate username and lastLoginUrl 
	 * @param split
	 */
	public void setSplit(String split) {
		this.split = split;
	}
}

2.修改AuthenticationFailHandlerImpl

package com.service.impl;
 
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.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

public class AuthenticationFailHandlerImpl extends SimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler {

	/**
	 * 登录时密码或账号不正确时的操作
	 */
	@Override
	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
		String lastLoginUrl = request.getParameter("lastLoginUrl");
		if(lastLoginUrl == null){
			lastLoginUrl = "";
		}
		
		lastLoginUrl = lastLoginUrl.replace("?", "%3F");
		lastLoginUrl = lastLoginUrl.replace("&", "%26");
		logger.info("lastLoginUrl=" + lastLoginUrl);
		response.sendRedirect(request.getContextPath() + "/login.jsp?result=0&lastLoginUrl="+lastLoginUrl);
		return;
	}

}

3.修改AuthenticationSuccessHandlerImpl

package com.service.impl;

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.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import com.bean.UserLoginDetails;

/**
 * Authentication Handler for redirect the original inputed URL after login system.
 */
public class AuthenticationSuccessHandlerImpl extends SimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

	/**
	 * 登录时通过密码验证后进行的操作
	 */
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
		//查询当前登录用户有权限的功能
		if(!SecurityContextHolder.getContext().getAuthentication().getPrincipal().equals("anonymousUser")){
			UserLoginDetails userDetails = (UserLoginDetails) authentication.getPrincipal();
			HttpSession session = request.getSession();
			session.setAttribute("userDetails", userDetails);
			
			logger.info("lastLoginUrl=" + userDetails.getLastLoginUrl());
			if(userDetails.getLastLoginUrl() != null && !"".equals(userDetails.getLastLoginUrl().trim())){
				response.sendRedirect(userDetails.getLastLoginUrl());
			}else{
				response.sendRedirect(request.getContextPath() + "/myPage/myPage.jsp");
			}
		}
		return;
	}
}

4.修改WebUserDetailsService

package com.service.impl;

import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.bean.Role;
import com.bean.User;
import com.bean.UserLoginDetails;
import com.service.UserService;
import com.web.filter.MyUsernamePasswordAuthenticationFilter;

/**
 * 用户登录是准备阶段的业务逻辑
 */
public class WebUserDetailsService implements UserDetailsService {
	protected static final Logger logger = LoggerFactory.getLogger(WebUserDetailsService.class);
	
	private UserService userService;

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		boolean accountNonExpired = true;  
        boolean credentialsNonExpired = true;  
        boolean accountNonLocked = true;
        boolean enabled = true;
        
        String[] usernameLastLoginUrl = username.split(MyUsernamePasswordAuthenticationFilter.SPRING_SECURITY_SPLIT);
		String lastLoginUrl = "";
		if(usernameLastLoginUrl.length == 1){
			username = usernameLastLoginUrl[0];
		}else if(usernameLastLoginUrl.length == 2){
			username = usernameLastLoginUrl[0];
			lastLoginUrl = usernameLastLoginUrl[1];
		}
		
        User user = null;
        try {
			user = userService.getUserByUsername(username);
		} catch (Exception e) {
			e.printStackTrace();
		}
        
        if(user == null){
			throw new UsernameNotFoundException("User account : " + username + " not found!");
		}
        
        //封装该用户具有什么角色
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); 
        if(user.getRoles() != null && !user.getRoles().isEmpty()){
        	for(Role role : user.getRoles()){
        		GrantedAuthority ga = new SimpleGrantedAuthority(role.getName());
        		authorities.add(ga);       
        	}
        }
        
        UserLoginDetails userLoginDetails =  new UserLoginDetails(user.getUsername(), user.getPassword(), authorities, 
        		accountNonExpired, accountNonLocked, credentialsNonExpired, enabled, lastLoginUrl);

        return userLoginDetails;
	}
}

5.修改UserLoginDetails

package com.bean;

import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserLoginDetails implements UserDetails {

	private static final long serialVersionUID = 1L;
	
	private String username;
	private String password;
	private Set<GrantedAuthority> authorities = null;
	private boolean accountNonExpired = false;			//账号未过期
    private boolean accountNonLocked = false;			//账号未锁定
    private boolean credentialsNonExpired = false;		//证书未过期
    private boolean enabled = false;					//是否可用 
    private final String lastLoginUrl;					//最后登陆的页面
    
    public UserLoginDetails(String username, String password, Set<GrantedAuthority> authorities, boolean accountNonExpired, 
    		boolean accountNonLocked, boolean credentialsNonExpired, boolean enabled, String lastLoginUrl){
    	this.username = username;
    	this.password = password;
    	this.authorities = authorities;
    	this.accountNonExpired = accountNonExpired;
    	this.accountNonLocked = accountNonLocked;
    	this.credentialsNonExpired = credentialsNonExpired;
    	this.enabled = enabled;
    	this.lastLoginUrl = lastLoginUrl;
    }
    
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return authorities;
	}
	public String getPassword() {
		return password;
	}
	public String getUsername() {
		return username;
	}
	public boolean isAccountNonExpired() {
		return accountNonExpired;
	}
	public boolean isAccountNonLocked() {
		return accountNonLocked;
	}
	public boolean isCredentialsNonExpired() {
		return credentialsNonExpired;
	}
	public boolean isEnabled() {
		return enabled;
	}

	public String getLastLoginUrl() {
		return lastLoginUrl;
	}
	
}

6.修改配置文件

<?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" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/security 
			http://www.springframework.org/schema/security/spring-security-3.1.xsd
			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.0.xsd">
	
	<!-- 访问login.jsp页面不需要权限验证  -->
	<http pattern="/login.jsp" security="none"/>

	<!-- This is where we configure Spring-Security  -->
	<!-- <http auto-config="true" use-expressions="true"> -->
	<http auto-config="false" use-expressions="true" access-denied-page="/deny.jsp" entry-point-ref="authenticationEntryPoint">
		<intercept-url pattern="/admin/**" access="hasRole('admin')"/><!-- 用户只有拥有角色admin才能访问/admin目录下面的资源 -->
		<intercept-url pattern="/myPage/**"	access="hasAnyRole('admin', 'myRole')"/><!-- 用户拥有角色admin,myRole中的任意一个就能访问/myPage目录下面的资源 -->
		
		<!-- <form-login
				login-page="/login.jsp"
				authentication-failure-url="/deny.jsp" 
				default-target-url="/index.jsp" 
				authentication-success-handler-ref="authenticationSuccessHandlerImpl"
				authentication-failure-handler-ref="authenticationFailHandlerImpl"
		/> -->
		
		<!-- 
	    	配置自定义的Filter,并且将其放在FORM_LOGIN_FILTER节点,就会替换掉原来的FORM_LOGIN_FILTER节点  
	    	设置auto-config="false",不然会报已经存在同样的过滤器错误,同时还要删除<form-login>
	   	-->
		<custom-filter ref="loginProcessFilter" position ="FORM_LOGIN_FILTER" />
				
		<logout invalidate-session="true" logout-success-url="/login.jsp" />
				
         <!-- 增加一个自定义的filter,放在FILTER_SECURITY_INTERCEPTOR之前,  实现用户、角色、权限、资源的数据库管理。  -->  
        <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
        
	</http>
	
	<beans:bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
		<beans:property name="loginFormUrl" value="/login.jsp"></beans:property>  
	</beans:bean>
	
	<beans:bean id="loginProcessFilter" class="com.web.filter.MyUsernamePasswordAuthenticationFilter">
		<beans:property name="authenticationManager" ref="authenticationManagerService"></beans:property>
		<beans:property name="authenticationSuccessHandler" ref="authenticationSuccessHandlerImpl"></beans:property>
		<beans:property name="authenticationFailureHandler" ref="authenticationFailHandlerImpl"></beans:property>
	</beans:bean>
	
	<beans:bean id="securityFilter" class="com.filter.SecurityInterceptorFilter">  
		<beans:property name="authenticationManager" ref="authenticationManagerService"/>  
		<beans:property name="accessDecisionManager" ref="accessDecisionManagerService"/>  
		<beans:property name="securityMetadataSource" ref="securityMetadataSourceService"/>  
	</beans:bean>
	
	
	<beans:bean id="securityMetadataSourceService" class="com.service.impl.SecurityMetadataSourceService" init-method="loadAllResources">
	    <beans:property name="userService" ref="userService"/>
	</beans:bean>
	
	<!-- Declare an authentication-manager to use a custom userDetailsService -->
	<authentication-manager alias="authenticationManagerService">
		<authentication-provider user-service-ref="webUserDetailsService">	
		    <password-encoder hash="md5" />
		</authentication-provider>
	</authentication-manager>
	
	<beans:bean id="accessDecisionManagerService" class="com.service.impl.AccessDecisionManagerService" /> 
	
</beans:beans>

7.程序运行结果

我们打开首页:http://localhost:8082/springSecurity/index.jsp

程序会自动跳转到登陆页面:http://localhost:8082/springSecurity/login.jsp?lastLoginUrl=/springSecurity/index.jsp

后面会附带首页的URL,输入正确的用户名和密码后,就会自动跳转到首页

spring security实现传参_第1张图片

spring security实现传参_第2张图片

7.项目的代码结构

spring security实现传参_第3张图片

8.项目源代码下载

包括整个项目的源代码和MYSQL建库脚本

http://download.csdn.net/detail/brushli/7865697

 

你可能感兴趣的:(spring,Security,传递参数,登陆验证)