小操作:springsecurity的简单搭建

Spring Security 自定义权限管理
本文介绍如何使用 Spring Security 实现自定义权限管理,包括配置 pom 文件、web.xml 和 applicationContext-security.xml,以及自定义 FilterSecurityInterceptor、AccessDecisionManager、InvocationSecurityMetadataSource 的实现方式。

原理:就是拦截器拦截请求

(只可意会,明白其中原理,代码太多,model和dao没有全部粘贴,如果不符合你的需求,不必往下看)
pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
 
	<groupId>com.zmc</groupId>
	<artifactId>SpringSecurityDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
 
	<name>SpringSecurityDemo</name>
	<url>http://maven.apache.org</url>
 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java-version>1.7</java-version>
		<org.springframework-version>3.2.2.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.1</org.slf4j-version>
	</properties>
 
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.3</version>
		</dependency>
		<!-- Spring security -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>3.1.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-crypto</artifactId>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

首先web.xml中增加拦截器

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

在spring的安全配置问件中配置问件applicationContext-security.xml增加

<?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:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/jdbc
           http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.2.xsd">

	<global-method-security pre-post-annotations="enabled">
	</global-method-security>

	<!-- 登录页面、提示无权限、申请权限页面不过滤 -->
	<http pattern="/login.do**" security="none" />
	<http pattern="/common/**" security="none" />
	<http pattern="/register.do**" security="none" />

	<http use-expressions="true" auto-config="true"> <!-- 当访问被拒绝时,会转到403.jsp -->
		<anonymous enabled="true" username="guest"
			granted-authority="ROLE_ANONYMOUS" />

		<!--  <form-login login-page="/login.do?method=index"
			default-target-url="/login.do?method=loginok"
			always-use-default-target="true" authentication-failure-url="/login.do?method=loginFailure" />-->
		<form-login login-page="/login.do?method=index" authentication-success-handler-ref="expaiSuccessHandler"
			always-use-default-target="false" authentication-failure-handler-ref="expaiSuccessHandler" />

		<logout logout-url="/j_spring_security_logout"
		 invalidate-session="true" success-handler-ref="myLogoutSuccessHandler"/>

		<!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了,这个filter位于FILTER_SECURITY_INTERCEPTOR之前 -->
		<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR" />

		<!-- <session-management invalid-session-url="/login.do?method=timeout">
			<concurrency-control max-sessions="10"
				error-if-maximum-exceeded="true" />
		</session-management>
		 -->
		<session-management>
			<concurrency-control max-sessions="10"
				error-if-maximum-exceeded="true" />
		</session-management>
		
		<remember-me key="e37f4b31-0c45-11dd-bd0b-0800200c9a66" user-service-ref="myUserDetailService"/>
	</http>
	

	<!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,我们的所有控制将在这三个类中实现,解释详见具体配置 -->
	<beans:bean id="myFilter"
		class="com.wxx.topicmanager.security.MyFilterSecurityInterceptor">
		<!-- 用户是否拥有所请求资源的权限 -->
		<beans:property name="authenticationManager" ref="authenticationManager" />
		<!-- 用户拥有的权限 -->
		<beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
		<!-- 资源与权限对应关系 -->
		<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
	</beans:bean>

	<!-- 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 -->
	<authentication-manager alias="authenticationManager">
		<authentication-provider user-service-ref="myUserDetailService">
			<password-encoder ref="myPasswordEncode">
				<!-- <salt-source user-property="salt" /> -->
			</password-encoder>
		</authentication-provider>
		<!-- 如果用户的密码采用加密的话,可以加点“盐” <password-encoder hash="md5" /> -->
	</authentication-manager>

	<beans:bean id="myPasswordEncode"
		class="com.wxx.topicmanager.security.MyPasswordEncode">
		<beans:constructor-arg name="algorithm" value="md5"></beans:constructor-arg>
	</beans:bean>

	<beans:bean id="myUserDetailService"
		class="com.wxx.topicmanager.security.MyUserDetailsService">
	</beans:bean>

	<beans:bean id="securityMetadataSource"
		class="com.wxx.topicmanager.security.MyInvocationSecurityMetadataSource"
		scope="prototype">
	</beans:bean>

	<beans:bean id="myAccessDecisionManager"
		class="com.wxx.topicmanager.security.MyAccessDecisionManager" scope="prototype">

	</beans:bean>
	<beans:bean id="expaiSuccessHandler" class="com.wxx.topicmanager.security.MyAuthenticationSuccessHandler"></beans:bean>
	<beans:bean id="myLogoutSuccessHandler" class="com.wxx.topicmanager.security.MyLogoutSuccessHandler"></beans:bean>

</beans:beans>

相关引入类

MyFilterSecurityInterceptor.java
import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
		implements Filter {
	/**
	 * 记录调试信息的Logger
	 */
	private static Logger m_Logger = Logger
			.getLogger(MyFilterSecurityInterceptor.class);
	private FilterInvocationSecurityMetadataSource securityMetadataSource;

	private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();

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

	/** */
	/**
	 * Method that is actually called by the filter chain. Simply delegates to
	 * the {@link #invoke(FilterInvocation)} method.
	 * 
	 * @param request
	 *            the servlet request
	 * @param response
	 *            the servlet response
	 * @param chain
	 *            the filter chain
	 * 
	 * @throws IOException
	 *             if the filter chain fails
	 * @throws ServletException
	 *             if the filter chain fails
	 */
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		m_Logger.debug("MyFilterSecurityInterceptor.doFilter: I am called.");
		m_Logger.debug("AccessDecisionManager:"
				+ this.getAccessDecisionManager());

		Authentication oAuthentication = SecurityContextHolder.getContext()
				.getAuthentication();

		m_Logger.debug("oAuthentication:" + oAuthentication);
		// 从session中获取登录用户角色信息,添加到Spring security 权限中
		if (SecurityContextHolder.getContext().getAuthentication() == null) {
			// Spring security没有登录,可以根据实际项目需要执行自己特定的逻辑,默认是不需要的.
//			System.out.println("用户没有登录.");
		}

		FilterInvocation fi = new FilterInvocation(request, response, chain);
		invoke(fi);
	}

	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

	public Class<? extends Object> getSecureObjectClass() {
		return FilterInvocation.class;
	}

	public void invoke(FilterInvocation fi) throws IOException,
			ServletException {
		// 在执行doFilter之前,进行权限的检查,而具体的实现已经交给accessDecisionManager
		super.setAccessDecisionManager(getAccessDecisionManager());
		InterceptorStatusToken token = beforeInvocation(fi);
		fi.getRequest().setAttribute(
				"__spring_security_filterSecurityInterceptor_filterApplied",
				true);
		try {
			m_Logger.debug("before MyFilterSecurityInterceptor.invoke."
					+ "accessDecisionManager:" + getAccessDecisionManager());
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			m_Logger.debug("after MyFilterSecurityInterceptor.invoke.");
		} catch (RuntimeException ex) {
			ex.printStackTrace();
			throw ex;
		} finally {
			afterInvocation(token, null);
		}
	}

	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

	public void setSecurityMetadataSource(
			FilterInvocationSecurityMetadataSource newSource) {
		this.securityMetadataSource = newSource;
	}

	public void destroy() {
	}

	public void init(FilterConfig arg0) throws ServletException {
	}
}

MyPasswordEncode.java

import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;
import com.wxx.project.util.PasswordUtil;
public class MyPasswordEncode extends MessageDigestPasswordEncoder {

	public MyPasswordEncode(String algorithm) {
		super(algorithm);
	}
	@Override
	// 如果返回true,则验证通过。
	public boolean isPasswordValid(String savePass, String submitPass,
			Object salt) {
		// return savePass.equalsIgnoreCase(Util.MD5WithSalt(submitPass,
		// salt.toString()));
		String md5SubmitPass = "";
		try {	
			md5SubmitPass = PasswordUtil.generatePassword(submitPass);
		} catch (Exception ex) {
			throw new RuntimeException("MD5加密失败,失败原因:" + ex);
		}
		return savePass.equalsIgnoreCase(md5SubmitPass);
	}
}

MyUserDetailsService.java

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.topicmanager.bswuser.BswUser;
import com.wxx.topicmanager.bswuser.BswUserService;
import com.wxx.topicmanager.user.AuthorityService;
import com.wxx.topicmanager.user.TRole;

/**
 * 实现Spring acegi权限模型要求的用户实现接口
 * 
 * @author wang.chungang@trs.com.cn
 * 
 */
@Service
public class MyUserDetailsService implements UserDetailsService {
	/**
	 * 记录调试信息的Logger
	 */
	private static Logger m_Logger = Logger.getLogger(MyUserDetailsService.class);
	@Autowired
	private BswUserService bswUserService;
	@Autowired
	private AuthorityService authorityService;

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.core.userdetails.UserDetailsService#
	 * loadUserByUsername(java.lang.String)
	 */
	public UserDetails loadUserByUsername(String _sUserName)
			throws UsernameNotFoundException {
		CustomerContextHolder
				.setCustomerType(CustomerContextHolder.SESSION_FACTORY_BSW);
		m_Logger.debug("MyUseDetailsService.loadUserByUsername(): I am called. UserName["
						+ _sUserName + "]");
		BswUser aUser = bswUserService.findByName(_sUserName);
		if (aUser == null) {
			throw new UsernameNotFoundException("用户[" + _sUserName + "]不存在");
		}

		// 将用户转换成Spring规范的用户
		User aSprignUser = convertToSpringUser(aUser);
		return aSprignUser;
	}

	/**
	 * 将系统用户翻译成spring用户
	 * 
	 * @param aUser
	 * @return
	 */
	private User convertToSpringUser(BswUser aUser) {
		// 生成用户的全部角色集合
		Collection<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
		CustomerContextHolder
				.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
		List<TRole> listRoles = authorityService.getRoles(aUser);
		for (TRole tRole : listRoles) {

			String sRoleName = tRole.getRoleName();
			auths.add(new GrantedAuthorityImpl(sRoleName));
		}

		// 所有用户均添加访客角色
		// GrantedAuthorityImpl auth2 = new GrantedAuthorityImpl(
		// Constants.defaultRolename);
		// auths.add(auth2);
		User aSpringUser = new User(aUser.getLoginname(), aUser.getPassword(),
				true, true, true, true, auths);
		return aSpringUser;
	}
}

MyInvocationSecurityMetadataSource.java

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.CMyString;
import com.wxx.topicmanager.user.RoleService;
import com.wxx.topicmanager.user.TPower;
import com.wxx.topicmanager.user.TRole;

/**
 * 此类用于角色对应的资源数据的管理
 * 
 */
@ComponentScan
public class MyInvocationSecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {

	Logger logger = Logger.getLogger(getClass());

	private PathMatcher urlMatcher = new AntPathMatcher();

	// 角色
	@Autowired
	private RoleService roleService;

	// 解析Url的匹配器,框架提供两种选择:一个是AntUrlPathMatcher,另一个是正则匹配
	// private UrlMatcher urlMatcher = new RegexUrlPathMatcher();

	// private UrlMatcher urlMatcher = new AntUrlPathMatcher();

	// 该map集合用于存放资源-角色的对应关系
	public static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	// /**
	// * 该构造方法有spring容器调用
	// */
	// public MyInvocationSecurityMetadataSource() {
	// this.loadResourceDefine();
	// }

	// TODO 通过定时任务将数据库中的资源和角色对应关系同步到系统内存常量resourceMap,保证管理员动态修改资源和角色及时生效

	/**
	 * 加载相关的资源权限的定义,将定义的权限构建ConfigAttribute对象,存入
	 * Collection<T>泛型集合中,并将该集合作为value。将角色拥有的资源的url作为key,由此
	 * 构建一个map集合将资源对应的角色信息对应存放。
	 */
	public void loadResourceDefine() {
		long startTime = System.currentTimeMillis();
		resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
		// roleManager = (RoleManager) this.getBean("roleManager");

		List<TRole> roles = roleService.getAll(); // 获取所有的角色
		// 此集合的作用是存放角色信息的ConfigAttribute实现类对象

		Collection<ConfigAttribute> attributes = null;
		for (TRole role : roles) {
			ConfigAttribute attribute = new SecurityConfig(role.getRoleName());

			// 遍历角色对应的权限
			List<TPower> listPowers = roleService.getPowers(role);
			for (TPower power : listPowers) {
				// 遍历每个Power
				String sPowerURL = power.getPowerURL();
				if (StringUtils.isNotEmpty(sPowerURL)) {
					if (resourceMap.containsKey(sPowerURL)) {
						Collection<ConfigAttribute> value = resourceMap
								.get(sPowerURL);
						value.add(attribute);
						resourceMap.put(sPowerURL, value);
					} else {
						attributes = new ArrayList<ConfigAttribute>();
						attributes.add(attribute);
						resourceMap.put(sPowerURL, attributes);
					}
				}
			}
		}

		long endTime = System.currentTimeMillis();
		logger.debug("resourceMap:" + resourceMap);
		logger.info("初始化资源URL和角色对应关系耗时" + (endTime - startTime) / 1000.0
				+ "秒,包含记录" + resourceMap.size() + "条");
	}

	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	public Collection<ConfigAttribute> getAttributes(Object arg)
			throws IllegalArgumentException {
		CustomerContextHolder
		.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
		if (resourceMap == null) {
			loadResourceDefine();
		}
		long startTime = System.currentTimeMillis();
		int count = 0;// url匹配次数
		try {
			Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

			// 获取URL的web应用程序的特定片段,不包括任何服务器的名称、上下文路径或servlet的路径
			String url = ((FilterInvocation) arg).getRequestUrl(); // 当前访问的URL
			Set<String> keySet = resourceMap.keySet();
			for (String key : keySet) {
				count++;
				// pathMatchesUrl方法判断请求的URL是否匹配,如果有匹配的,则返回保存有角色信息的ConfigAttribute对象的集合
				// 支持用-号作为路径的排除
				String[] arURLPatterns = CMyString.split(key, ";");
				boolean bExcluded = false;
				for (String sURLPattern : arURLPatterns) {
					if (!sURLPattern.startsWith("-"))
						continue;
					// 验证是否是排除当前角色
					String sExcludeURLPattern = sURLPattern.substring(1,
							sURLPattern.length());
					if (urlMatcher.match(sExcludeURLPattern, url)) {
						bExcluded = true;
						break;
					}
				}
				if (bExcluded)
					continue;

				if (!bExcluded) {
					// 没有被排除,继续验证当前角色是否匹配
					for (String sURLPattern : arURLPatterns) {
						if (sURLPattern.startsWith("-"))
							continue;

						if (urlMatcher.match(sURLPattern, url)) {
							Collection<ConfigAttribute> tempAttributes = resourceMap
									.get(key);
							logger.debug("sURLPattern:" + sURLPattern
									+ ";url:" + url + ";tempAttributes:"
									+ tempAttributes);
							attributes.addAll(tempAttributes);
						}
					}
				}
			}

			// 返回
			if (attributes.isEmpty()) {
				// 如果没有匹配的返回null,表示此url直接放行
				return null;
			}

			// 返回用户的所有的角色
			logger.debug("url:[" + url + "]; roles:[" + attributes + "]");
			return attributes;
		} catch (Exception e) {
			e.printStackTrace();
			throw new IllegalArgumentException(e);
		} finally {
			long costTime = System.currentTimeMillis() - startTime;
			logger.debug("权限url匹配" + count + "次,耗时:" + costTime / 1000.0 + "秒");
		}
	}

	public boolean supports(Class<?> arg0) {
		return true;
	}
}

MyAccessDecisionManager.java

import java.util.Collection;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

/**
 * 自定义决策管理器,用于判断用户请求的资源是否和用户角色匹配
 * SecurityAccessDecisionManager实现AccessDecisionManager接口,拥有spring security 访问控制
 * 
 */
public class MyAccessDecisionManager implements AccessDecisionManager {
	/**
	 * 记录调试信息的Logger
	 */
	private static Logger m_Logger = Logger.getLogger(MyAccessDecisionManager.class);
	/**
	 * 此构造方法有spring容器调用
	 */
	public MyAccessDecisionManager() {
	}

	/**
	 * 传递参数的访问控制决策。如果访问被拒绝作为身份验证不持有所需的权限或ACL权限,则抛出AccessDeniedException 异常
	 * 
	 * @param authentication
	 *            该方法的调用者。Authentication接口表示验证身份的主体令牌,由此可以获取 已经验证用户的用户及角色信息
	 * @param obj
	 *            称之为担保对象。可以是一个要求保护的方法或者URL
	 * @param attributes
	 *            与保护对象相关联的配置属性.ConfigAttribute接口是用来存储安全系统相关的配置属性。
	 *            由FilterInvocationSecurityMetadataSource实现类中获取
	 */
	public void decide(Authentication authentication, Object obj,
			Collection<ConfigAttribute> attributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		// 如果请求url不需要权限控制,则所有用户均可以访问
		if (attributes == null) {
			return;
		}
		Iterator<ConfigAttribute> configIterator = attributes.iterator();

		// 获取当前登录用户具有的角色信息
		Collection<GrantedAuthority> grantedAuthorities = (Collection<GrantedAuthority>) authentication
				.getAuthorities();
		m_Logger.debug("当前页面需要角色[attributes=" + attributes
				+ "];用户授权角色[grantedAuthorities=" + grantedAuthorities + "]");

		// 对请求的资源所需权限进行遍历。
		while (configIterator.hasNext()) {
			ConfigAttribute attribute = configIterator.next();
			String needRole = ((SecurityConfig) attribute).getAttribute();
			
			// 对当前登录用户的角色信息进行遍历
			for (GrantedAuthority authority : grantedAuthorities) {
				// 获取当前登录用户的角色名称并与其请求资源所需角色的名称进行比对。如果匹配返回
				if (needRole.equals(authority.getAuthority())) {
					return;
				}
			}
		}
		/*
		 * 如果当前登录用户的角色名次与其请求资源所需角色的名称不匹配,抛出AccessDeniedException异常, 最终有spring
		 * security处理此异常,并根据安全配置文件的配置返回相关错误提示页面
		 */
		throw new AccessDeniedException("no right");
	}

	public boolean supports(ConfigAttribute attribute) {
		// TODO Auto-generated method stub
		return true;
	}

	public boolean supports(Class<?> cls) {
		// TODO Auto-generated method stub
		return true;
	}

}

MyAuthenticationSuccessHandler.java

import java.io.IOException;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.RequestUtils;
import com.wxx.topicmanager.bswuser.BswUser;
import com.wxx.topicmanager.bswuser.BswUserService;
import com.wxx.topicmanager.entities.Operlog;
import com.wxx.topicmanager.service.OperlogService;
import com.wxx.topicmanager.util.RemoteAddrUtil;

public class MyAuthenticationSuccessHandler implements
		AuthenticationSuccessHandler, AuthenticationFailureHandler {
	@Autowired
	private OperlogService operlogService;
	@Autowired
	private BswUserService userService;

	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication auth)
			throws IOException, ServletException {
		RequestUtils requestUtils = new RequestUtils(request);
		// 记录日志
		CustomerContextHolder
				.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
		User loginUser = (User) auth.getPrincipal();
		Operlog operlog = new Operlog();
		operlog.setOpertype(Operlog.TYPE_LOGIN);
		operlog.setOperdesc("用户[" + loginUser.getUsername() + "]登录成功!");
		operlog.setRemoteip(RemoteAddrUtil.getRemoteAddr(request));
		try {
			operlogService.saveUserOperLog(operlog, loginUser.getUsername());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		int nMode = requestUtils.getParameterAsInt("mode", 0);
		if (nMode == 1) {
			response.sendRedirect("login.do?method=loginok");
			// request.getRequestDispatcher("/WEB-INF/jsps/login/login-ok.jsp").forward(request,
			// response);
		} else if (nMode == 2) {
			response.sendRedirect("client/client.do?method=loginok");
			/*response.setContentType("text/html");
			// 设置字符编码为UTF-8, 这样支持汉字显示
			response.setCharacterEncoding("GBk");
			CustomerContextHolder
					.setCustomerType(CustomerContextHolder.SESSION_FACTORY_BSW);
			BswUser bswUser = userService.findByName(loginUser.getUsername());
			response.getWriter().println(
					"{\"rtState\":0,\"rtMsg\":\"登录成功\",\"rtData\":{\"userName\":\""
							+ bswUser.getLoginname() + "\",\"id\":\""
							+ bswUser.getUserid() + "\",\"cnName\":\""
							+ bswUser.getUsername()
							+ "\",\"isLeader\":false,\"mac\":\""
							+ bswUser.getMac() + "\"}}");*/
		} else {
			response.sendRedirect("index.do?method=index");
		}

	}

	@Override
	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
		request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION",
				exception);
		RequestUtils requestUtils = new RequestUtils(request);
		int nMode = requestUtils.getParameterAsInt("mode", 0);
		if (nMode == 2) {
			request.getRequestDispatcher(
					"login.do?method=loginFailure&mode=" + nMode).forward(
					request, response);
		} else {
			response.sendRedirect("login.do?method=loginFailure&mode=" + nMode);
		}
	}

}

MyLogoutSuccessHandler.java

import java.io.IOException;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;

import com.wxx.dao.base.hibernate.CustomerContextHolder;
import com.wxx.project.util.RequestUtils;
import com.wxx.topicmanager.entities.Operlog;
import com.wxx.topicmanager.service.OperlogService;
import com.wxx.topicmanager.util.RemoteAddrUtil;

public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler
		implements LogoutSuccessHandler {
	@Autowired
	private OperlogService operlogService;

	@Override
	public void onLogoutSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		RequestUtils requestUtils = new RequestUtils(request);
		int nMode = requestUtils.getParameterAsInt("mode", 0);
		User loginUser = (User) authentication.getPrincipal();
		// 记录日志
		CustomerContextHolder
				.setCustomerType(CustomerContextHolder.SESSION_FACTORY_TOPIC);
		Operlog operlog = new Operlog();
		operlog.setOpertype(Operlog.TYPE_LOGOUT);
		operlog.setOperdesc("用户退出");
		operlog.setRemoteip(RemoteAddrUtil.getRemoteAddr(request));
		try {
			operlogService.saveUserOperLog(operlog, loginUser.getUsername());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (nMode == 2) {
			response.setContentType("text/html");
			// 设置字符编码为UTF-8, 这样支持汉字显示
			response.setCharacterEncoding("GBk");
			try {
				response.getWriter().println(
						"{\"rtState\":0,\"rtMsg\":\"已注销\"}");
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else if (nMode == 3) {
			response.sendRedirect("mobile-login.do?method=login");
		} else {
			super.onLogoutSuccess(request, response, authentication);
		}
	}
}

也可以简化applicationContext-security.xml中的FilterSecurityInterceptor

<?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">
		<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来增加过滤器,before="FILTER_SECURITY_INTERCEPTOR"表示在SpringSecurity默认的过滤器之前执行。 -->
		<custom-filter ref="filterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR" />
	</http>
	
	<!-- 数据源 -->
	<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="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>
	
	<!-- 授权管理器 -->
	<beans:bean class="com.zmc.demo.MyAccessDecisionManager" id="accessDecisionManager">
	</beans:bean>
	<!--认证管理-->
	<authentication-manager alias="authenticationManager">
		<authentication-provider>
			<jdbc-user-service data-source-ref="dataSource" id="usersService"
				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>

相关代码

MyFilterInvocationSecurityMetadataSource .java

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import javax.servlet.http.HttpServletRequest;
 
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.RequestMatcher;
 
 
/**
 * @classname MyFilterInvocationSecurityMetadataSource
 * @author ZMC
 * @time 2017-1-10
 */
public class MyFilterInvocationSecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource, InitializingBean {
 
	private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = null;
	// 资源权限集合
	private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
	
	//查找数据库权限和资源关系
	private JdbcRequestMapBulider builder;
	
	/*
	 * (non-Javadoc)
	 * @see
	 * org.springframework.security.access.SecurityMetadataSource#getAttributes
	 * (java.lang.Object)
	 * 更具访问资源的地址查找所需要的权限
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		final HttpServletRequest request = ((FilterInvocation) object)
				.getRequest();
 
		Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
				.entrySet()) {
			if (entry.getKey().matches(request)) {
				attrs = entry.getValue();
				break;
			}
		}
		return attrs;
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.security.access.SecurityMetadataSource#
	 * getAllConfigAttributes()
	 * 获取所有的权限
	 */
	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
		for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
				.entrySet()) {
			allAttributes.addAll(entry.getValue());
		}
		System.out.println("总共有这些权限:"+allAttributes.toString());
		return allAttributes;
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.security.access.SecurityMetadataSource#supports(java
	 * .lang.Class)
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}
	//绑定requestMap
	protected Map<RequestMatcher, Collection<ConfigAttribute>> bindRequestMap() {
		
		return builder.buildRequestMap();
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		this.requestMap = this.bindRequestMap();
	}
 
	public void refreshResuorceMap() {
		this.requestMap = this.bindRequestMap();
	}
 
	//get方法
	public JdbcRequestMapBulider getBuilder() {
		return builder;
	}
	
	//set方法
	public void setBuilder(JdbcRequestMapBulider builder) {
		this.builder = builder;
	}
 
}

MyAccessDecisionManager .java

import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
 
/**
 * @classname MyAccessDecisionManager
 * @author ZMC
 * @time 2017-1-10
 * 
 */
public class MyAccessDecisionManager implements AccessDecisionManager  {
	
	
	
	/* (non-Javadoc)
	 * @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
	 * 该方法决定该权限是否有权限访问该资源,其实object就是一个资源的地址,authentication是当前用户的
	 * 对应权限,如果没登陆就为游客,登陆了就是该用户对应的权限
	 */
	@Override
	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		if(configAttributes == null) {  
            return;
        }  
        //所请求的资源拥有的权限(一个资源对多个权限)  
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();  
        while(iterator.hasNext()) {  
            ConfigAttribute configAttribute = iterator.next();  
            //访问所请求资源所需要的权限  
            String needPermission = configAttribute.getAttribute();  
            System.out.println("访问"+object.toString()+"需要的权限是:" + needPermission);  
            //用户所拥有的权限authentication  
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for(GrantedAuthority ga : authorities) {  
                if(needPermission.equals(ga.getAuthority())) {  
                    return;
                }  
            }
        }
        //没有权限  
        throw new AccessDeniedException(" 没有权限访问! ");  
		
	}
 
	@Override
	public boolean supports(ConfigAttribute attribute) {
		// TODO Auto-generated method stub
		return true;
	}
 
	@Override
	public boolean supports(Class<?> clazz) {
		// TODO Auto-generated method stub
		return true;
	}   	     
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangxingxin2704

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值