MyEclipse中Spring Security 3.0.3无废话配置(第二章)。

上回说到数据库验证首先看以往的验证配置:

<authentication-manager>  
    <authentication-provider>  
        <user-service>  
            <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />  
            <user name="bob" password="bobspassword" authorities="ROLE_USER" />  
        </user-service>  
    </authentication-provider>  
</authentication-manager>

现在我们要用数据库来验证。

第一步:建立数据库

这里只用两张表即可解决问题,也许你看过很多建立五张到七张的,那是实际在项目中才这样做,我们做试验的话在这里只建立两张表:1.用户表2.资源表。

用户表四个字段:ID、USERNAME、PASSWORD、ROLE(权限)

资源表有三个字段:ID、资源路径、访问权限

资源路径就是一个URL地址,访问权限就是这个资源需要什么样子的权限才能访问。

第二步:权限提供

package cn.com.fri.security.supports;

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

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
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.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.fri.security.dao.interfaces.IAuthoritiesDAO;
import cn.com.fri.security.dao.interfaces.IResourcesDAO;
import cn.com.fri.security.vo.Authorities;
import cn.com.fri.security.vo.Resources;

/**
 * 
 * 此类在初始化时,应该取到所有资源及其对应角色的定义
 * 
 */
@Service("securityMetadataSource")
public class MyInvocationSecurityMetadataSource implements
		FilterInvocationSecurityMetadataSource {
	@Autowired
	private IAuthoritiesDAO authoritiesDAO;
	@Autowired
	private IResourcesDAO resourcesDAO;

	private UrlMatcher urlMatcher = new AntUrlPathMatcher();;
	private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	public MyInvocationSecurityMetadataSource() {
	}

	/**
	 * 在此处,将数据库中所有的资源以及对应的权限加入到内存中
	 */
	@PostConstruct
	public void loadResourceDefine() {
		resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
		Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
		System.out.println(authoritiesDAO);
		List<Authorities> auth = authoritiesDAO.findAll();
		for (Authorities au : auth) {
			ConfigAttribute ca = new SecurityConfig(au.getAuthorname());
			atts.add(ca);
		}
		List<Resources> res = resourcesDAO.findAll();
		for (Resources resources : res) {
			resourceMap.put(resources.getResourcesstring(), atts);
		}
		System.out.println("权限加载完毕");
	}

	// According to a URL, Find out permission configuration of this URL.
	@Transactional(readOnly = true)
	public Collection<ConfigAttribute> getAttributes(Object object)
			throws IllegalArgumentException {
		// guess object is a URL.
		String url = ((FilterInvocation) object).getRequestUrl();
		Iterator<String> ite = resourceMap.keySet().iterator();
		while (ite.hasNext()) {
			String resURL = ite.next();
			if (urlMatcher.pathMatchesUrl(url, resURL)) {
				return resourceMap.get(resURL);
			}
		}
		return null;
	}

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

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

	public void setAuthoritiesDAO(IAuthoritiesDAO authoritiesDAO) {
		this.authoritiesDAO = authoritiesDAO;
	}

	public void setResourcesDAO(IResourcesDAO resourcesDAO) {
		this.resourcesDAO = resourcesDAO;
	}

}

找个类的作用就是在服务器启动的时候将存放在数据库表中的资源与访问权限加载到内存中去。

第三步:编写路径验证类:

package cn.com.fri.security.supports;

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.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
@Service("myAccessDecisionManagerBean")
public class MyAccessDecisionManager implements AccessDecisionManager {

	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes)
			throws AccessDeniedException, InsufficientAuthenticationException {
		if (configAttributes == null) {
			return;
		}
		
		Iterator<ConfigAttribute> ite = configAttributes.iterator();
		while (ite.hasNext()) {
			ConfigAttribute ca = ite.next();
			String needRole = ((SecurityConfig) ca).getAttribute();
			for (GrantedAuthority ga : authentication.getAuthorities()) {
				if (needRole.equals(ga.getAuthority())) { // ga is user's role.
					return;
				}
			}
		}
		throw new AccessDeniedException("no right");
	}

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

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

}

此类用户判断当前登录的用户是否有权限访问某个路径。而这里路径的访问权限就是从内存中取出来的。

第四步:登录验证:

package cn.com.fri.security.supports;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
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 org.springframework.transaction.annotation.Transactional;

import cn.com.fri.security.dao.interfaces.IUsersDAO;
import cn.com.fri.security.vo.Authorities;
import cn.com.fri.security.vo.CustomUser;
import cn.com.fri.security.vo.Users;

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private IUsersDAO dao;

	@Transactional(readOnly = true)
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {
		System.out.println(username);
		Users user = dao.findByUsername(username).get(0);//此查询由自己去实现
		if (user == null)
			throw new UsernameNotFoundException("user not found");
		Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

		List<Authorities> auths = dao.findAutByUsername(username);//此查询由自己去实现如果用户表中已经包含了权限,就无需进行这一步操作了。
		System.out.println(">>>>>>获得的权限有:" + auths.size() + "个");
		for (Authorities a : auths) {
			GrantedAuthorityImpl authorityImpl = new GrantedAuthorityImpl(a
					.getAuthorname());
			authorities.add(authorityImpl);
		}
		CustomUser u = new CustomUser(username, user.getPsw(), user
				.getEnabled(), true, true, true, authorities);
		u.setUser(user);
		return u;
	}
}

在这个类中,用户一旦登录,我们就能获取到用户的权限,将用户权限放入到CustomUser中,然后返回这个CustomUser即可。CustomUser为继承自org.springframework.security.core.userdetails.User的一个类,继承的原因是为了让原来的User能够拥有更多的属性,例如用户的中文名称等等。里面的List<Authorities>auths=dao.findAutByUsername(username);这一步是因为我是按照七张表的结构去建立的,因此如果你只有两张表,这里完全可以省略。你甚至可以在这里写死(试验用)如下:

GrantedAuthorityImpl authorityImpl = new GrantedAuthorityImpl("ROLE_ADMIN");
authorities.add(authorityImpl);

第五步:配置SS3的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"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/security 
	http://www.springframework.org/schema/security/spring-security-3.0.xsd">
	<http auto-config='true' access-denied-page="/common/403.jsp">
		<!-- 以下为不需要权限就能访问的资源**意思就是包括子目录也同样适用 -->
		<intercept-url pattern="/css/**" filters="none" />
		<intercept-url pattern="/app/**" filters="none" />
		<intercept-url pattern="/images/**" filters="none" />
		<intercept-url pattern="/swf/**" filters="none" />
		<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
		<form-login login-page='/login.jsp' default-target-url="/index.do"
			always-use-default-target="true" />
		<!-- 配置退出登录 -->
		<logout invalidate-session="true" logout-url="/logout"
			logout-success-url="/login.jsp" />
		<session-management invalid-session-url="/error.html">
			<concurrency-control max-sessions="1"
				expired-url="/error.html" />
		</session-management>
		<!--
			配置登录验证,包括权限的启动都在这里:
		-->
		<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myFilter" />
	</http>
	
	<!-- 配置登录验证的类 -->
	<beans:bean id="daoAuthenticationProvider"
		class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
		<beans:property name="userDetailsService" ref="userDetailsService" />
	</beans:bean>

	<beans:bean id="authenticationManager"
		class="org.springframework.security.authentication.ProviderManager">
		<beans:property name="providers">
			<beans:list>
				<beans:ref local="daoAuthenticationProvider" />
			</beans:list>
		</beans:property>
	</beans:bean>
	
	<authentication-manager>
		<authentication-provider user-service-ref="userDetailsService">
		</authentication-provider>
	</authentication-manager>


	<!--
		一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性,
		我们的所有控制将在这三个类中实现,这里使用了annocation,所以ref后面的参数在XML配置中是没有的,详细看这三个类。
	-->
	<beans:bean id="myFilter"
		class="cn.com.fri.security.supports.MyFilterSecurityInterceptor">
		<beans:property name="authenticationManager" ref="authenticationManager" />
		<beans:property name="accessDecisionManager" ref="myAccessDecisionManagerBean" />
		<beans:property name="securityMetadataSource" ref="securityMetadataSource" />
	</beans:bean>

</beans:beans>

由于东西较多,所以再一开始的时候建议大家去下载官方的说明文档和中文翻译的说明文档。

在此我们的配置结束。这一部分虽然代码比较多,但是其实一点不复杂,仔仔细细的看一遍就会了。

相关推荐