`
arronxu1989
  • 浏览: 2999 次
社区版块
存档分类
最新评论

Spring Security3.1 最新配置实例

阅读更多
转至http://blog.csdn.net/k10509806/article/details/6369131

这几天学习了一下Spring Security3.1,从官网下载了Spring Security3.1版本进行练习,经过多次尝试才摸清了其中的一些原理。本人不才,希望能帮助大家。还有,这次我第二次写博客啊,文体不是很行。希望能让观看者不产生疲惫的感觉,我已经心满意足了。

一、数据库结构
     先来看一下数据库结构,采用的是基于角色-资源-用户的权限管理设计。(MySql数据库)
    为了节省篇章,只对比较重要的字段进行注释。
    1.用户表Users
    CREATE TABLE `users` (
       -- 账号是否有限 1. 是 0.否
       `enable` int(11) default NULL,
       `password` varchar(255) default NULL,
       `account` varchar(255) default NULL,
       `id` int(11) NOT NULL auto_increment,
       PRIMARY KEY  (`id`)
    )

   2.角色表Roles
   CREATE TABLE `roles` (
     `enable` int(11) default NULL,
     `name` varchar(255) default NULL,
     `id` int(11) NOT NULL auto_increment,
     PRIMARY KEY  (`id`)
   )

   3 用户_角色表users_roles
   CREATE TABLE `users_roles` (
     --用户表的外键
     `uid` int(11) default NULL,
     --角色表的外键
     `rid` int(11) default NULL,
     `urId` int(11) NOT NULL auto_increment,
     PRIMARY KEY  (`urId`),
     KEY `rid` (`rid`),
     KEY `uid` (`uid`),
    CONSTRAINT `users_roles_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`),
    CONSTRAINT `users_roles_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `users` (`id`)
   )

   4.资源表resources
   CREATE TABLE `resources` (
     `memo` varchar(255) default NULL,
     -- 权限所对应的url地址
     `url` varchar(255) default NULL,
     --优先权
     `priority` int(11) default NULL,
     --类型
     `type` int(11) default NULL,
     --权限所对应的编码,例201代表发表文章
     `name` varchar(255) default NULL,
     `id` int(11) NOT NULL auto_increment,
     PRIMARY KEY  (`id`)
   )

   5.角色_资源表roles_resources
    CREATE TABLE `roles_resources` (
      `rsid` int(11) default NULL,
      `rid` int(11) default NULL,
      `rrId` int(11) NOT NULL auto_increment,
      PRIMARY KEY  (`rrId`),
      KEY `rid` (`rid`),
      KEY `roles_resources_ibfk_2` (`rsid`),
      CONSTRAINT `roles_resources_ibfk_2` FOREIGN KEY (`rsid`) REFERENCES `resources` (`id`),
      CONSTRAINT `roles_resources_ibfk_1` FOREIGN KEY (`rid`) REFERENCES `roles` (`id`)
      )

  二、系统配置
   所需要的jar包,请自行到官网下载,我用的是Spring Security3.1.0.RC1版的。把dist下的除了源码件包导入就行了。还有那些零零碎的   数据库驱动啊,log4j.jar等等,我相信在用Spring Security之前,大家已经会的了。
  1) web.xml
     <!-- Spring -->
  <context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:applicationContext.xml,classpath:applicationContext-security.xml</param-value>
  </context-param>
  
	
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!-- 权限 -->
  <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>


这里主要是配置了让容器启动的时候加载application-security.xml和Spring Security的权限过滤器代理,让其过滤所有的客服请求。
2)application-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"
    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.1.xsd">
                        
    <global-method-security pre-post-annotations="enabled" /> 
    <!-- 该路径下的资源不用过滤 -->           
    <http pattern="/js/**" security="none"/>
    <http use-expressions="true" auto-config="true">
    	
        <form-login />
        <logout/>
        <!-- 实现免登陆验证 -->
        <remember-me />
        <session-management invalid-session-url="/timeout.jsp">
            <concurrency-control max-sessions="10" error-if-maximum-exceeded="true" />
        </session-management>
		<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/>
    </http>
    <!-- 配置过滤器 -->
    <beans:bean id="myFilter" class="com.huaxin.security.MySecurityFilter">
    	<!-- 用户拥有的权限 -->
    	<beans:property name="authenticationManager" ref="myAuthenticationManager" />
    	<!-- 用户是否拥有所请求资源的权限 -->
    	<beans:property name="accessDecisionManager" ref="myAccessDecisionManager" />
    	<!-- 资源与权限对应关系 -->
    	<beans:property name="securityMetadataSource" ref="mySecurityMetadataSource" />
    </beans:bean>
    <!-- 实现了UserDetailsService的Bean -->
    <authentication-manager alias="myAuthenticationManager">
        <authentication-provider user-service-ref="myUserDetailServiceImpl" />
    </authentication-manager>
    <beans:bean id="myAccessDecisionManager" class="com.huaxin.security.MyAccessDecisionManager"></beans:bean>
	<beans:bean id="mySecurityMetadataSource" class="com.huaxin.security.MySecurityMetadataSource">
		<beans:constructor-arg name="resourcesDao" ref="resourcesDao"></beans:constructor-arg>
	</beans:bean>
	<beans:bean id="myUserDetailServiceImpl" class="com.huaxin.security.MyUserDetailServiceImpl">
		<beans:property name="usersDao" ref="usersDao"></beans:property>
	</beans:bean>
</beans:beans>



我们在第二个http标签下配置一个我们自定义的继承了org.springframework.security.access.intercept.AbstractSecurityInterceptor的Filter,并注入其
必须的3个组件authenticationManager、accessDecisionManager和securityMetadataSource。其作用上面已经注释了。

<custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> 这里的FILTER_SECURITY_INTERCEPTOR是Spring Security默认的Filter,
我们自定义的Filter必须在它之前,过滤客服请求。接下来看下我们最主要的myFilter吧。

3)myFilter
  (1) MySecurityFilter.java 过滤用户请求
     public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter {
	//与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,
	//其他的两个组件,已经在AbstractSecurityInterceptor定义
	private FilterInvocationSecurityMetadataSource securityMetadataSource;

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

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		invoke(fi);
	}
	
	private void invoke(FilterInvocation fi) throws IOException, ServletException {
		// object为FilterInvocation对象
                  //super.beforeInvocation(fi);源码
		//1.获取请求资源的权限
		//执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
		//2.是否拥有权限
		//this.accessDecisionManager.decide(authenticated, object, attributes);
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try {
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		} finally {
			super.afterInvocation(token, null);
		}
	}

	public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
		return securityMetadataSource;
	}

	public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
		this.securityMetadataSource = securityMetadataSource;
	}
	
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
	}
	
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Class<? extends Object> getSecureObjectClass() {
		//下面的MyAccessDecisionManager的supports方面必须放回true,否则会提醒类型错误
		return FilterInvocation.class;
	}
}


核心的InterceptorStatusToken token = super.beforeInvocation(fi);会调用我们定义的accessDecisionManager:decide(Object object)和securityMetadataSource
  :getAttributes(Object object)方法。

(2)MySecurityMetadataSource.java
  //1 加载资源与权限的对应关系
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
	//由spring调用
	public MySecurityMetadataSource(ResourcesDao resourcesDao) {
		this.resourcesDao = resourcesDao;
		loadResourceDefine();
	}

	private ResourcesDao resourcesDao;
	private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

	public ResourcesDao getResourcesDao() {
		return resourcesDao;
	}

	public void setResourcesDao(ResourcesDao resourcesDao) {
		this.resourcesDao = resourcesDao;
	}

	public Collection<ConfigAttribute> getAllConfigAttributes() {
		// TODO Auto-generated method stub
		return null;
	}

	public boolean supports(Class<?> clazz) {
		// TODO Auto-generated method stub
		return true;
	}
	//加载所有资源与权限的关系
	private void loadResourceDefine() {
		if(resourceMap == null) {
			resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
			List<Resources> resources = this.resourcesDao.findAll();
			for (Resources resource : resources) {
				Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
                                //以权限名封装为Spring的security Object
				ConfigAttribute configAttribute = new SecurityConfig(resource.getName());
				configAttributes.add(configAttribute);
				resourceMap.put(resource.getUrl(), configAttributes);
			}
		}
		
		Set<Entry<String, Collection<ConfigAttribute>>> resourceSet = resourceMap.entrySet();
		Iterator<Entry<String, Collection<ConfigAttribute>>> iterator = resourceSet.iterator();
		
	}
	//返回所请求资源所需要的权限
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		
		String requestUrl = ((FilterInvocation) object).getRequestUrl();
		System.out.println("requestUrl is " + requestUrl);
		if(resourceMap == null) {
			loadResourceDefine();
		}
		return resourceMap.get(requestUrl);
	}

}


这里的resourcesDao,熟悉Dao设计模式和Spring 注入的朋友应该看得明白。

(3)MyUserDetailServiceImpl.java

  public class MyUserDetailServiceImpl implements UserDetailsService {
	
	private UsersDao usersDao;
	public UsersDao getUsersDao() {
		return usersDao;
	}

	public void setUsersDao(UsersDao usersDao) {
		this.usersDao = usersDao;
	}
	
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		System.out.println("username is " + username);
		Users users = this.usersDao.findByName(username);
		if(users == null) {
			throw new UsernameNotFoundException(username);
		}
		Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(users);
		
		boolean enables = true;
		boolean accountNonExpired = true;
		boolean credentialsNonExpired = true;
		boolean accountNonLocked = true;
		
		User userdetail = new User(users.getAccount(), users.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
		return userdetail;
	}
	
	//取得用户的权限
	private Set<GrantedAuthority> obtionGrantedAuthorities(Users user) {
		Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
		Set<Roles> roles = user.getRoles();
		
		for(Roles role : roles) {
			Set<Resources> tempRes = role.getResources();
			for(Resources res : tempRes) {
				authSet.add(new GrantedAuthorityImpl(res.getName()));
s			}
		}
		return authSet;
	}
}


(4) MyAccessDecisionManager.java

  public class MyAccessDecisionManager implements AccessDecisionManager {
	
	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("needPermission is " + needPermission);
			//用户所拥有的权限authentication
			for(GrantedAuthority ga : authentication.getAuthorities()) {
				if(needPermission.equals(ga.getAuthority())) {
					return;
				}
			}
		}
		//没有权限
		throw new AccessDeniedException(" 没有权限访问! ");
	}

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

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


三、流程
1)容器启动(MySecurityMetadataSource:loadResourceDefine加载系统资源与权限列表)
2)用户发出请求
3)过滤器拦截(MySecurityFilter:doFilter)
4)取得请求资源所需权限(MySecurityMetadataSource:getAttributes)
5)匹配用户拥有权限和请求权限(MyAccessDecisionManager:decide),如果用户没有相应的权限,
     执行第6步,否则执行第7步。
6)登录
7)验证并授权(MyUserDetailServiceImpl:loadUserByUsername)
8)重复4,5

四、结束语
好了,终于写完了,回头看了一下,感觉不是怎么行。等我弄明白Spring Security它的原理之后,再回头修改下注释吧。大家觉得不妥的地方,可以留言,我会回复大家的。
分享到:
评论

相关推荐

    Spring Security3.1最新配置实例

    基于struts2 hibernate Spring Security3的权限配置。详情可以关注我的博客。

    Spring Security3.1 最新配置实例源码(包含jar包)

    代码来源于http://blog.csdn.net/k10509806/article/details/6369131这篇文章,可惜下载下来后的代码没有jar包,导致无法运行,故我在源代码的基础上增加了jar包并解决了部分bug,现将全部代码共享.......

    Spring Security3.1 最新配置实例 (转载)

    NULL 博文链接:https://grey2.iteye.com/blog/1714086

    Spring Security 3.1 配置实例,有URL 方法拦截,都存数据库 maven

    NULL 博文链接:https://chxiaowu.iteye.com/blog/1536141

    Spring攻略PDF版

     3.1 在Spring IoC容器里配置Bean   3.1.1 问题描述   3.1.2 解决方案   3.1.3 实现方法   3.2 实例化Spring IoC容器   3.2.1 问题描述   3.2.2 解决方案   3.2.3 实现方法   3.3...

    Spring攻略中文版PDF

     3.1 在Spring IoC容器里配置Bean   3.1.1 问题描述   3.1.2 解决方案   3.1.3 实现方法   3.2 实例化Spring IoC容器   3.2.1 问题描述   3.2.2 解决方案   3.2.3 实现方法   3.3...

    Spring攻略英文版(附带源码)

     3.1 在Spring IoC容器里配置Bean   3.1.1 问题描述   3.1.2 解决方案   3.1.3 实现方法   3.2 实例化Spring IoC容器   3.2.1 问题描述   3.2.2 解决方案   3.2.3 实现方法   3.3 解决...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part3

    3.1 Spring Aware 48 3.1.1 点睛 48 3.1.2 示例 49 3.2 多线程 51 3.2.1 点睛 51 3.2.2 示例 51 3.3 计划任务 54 3.3.1 点睛 54 3.3.2 示例 54 3.4 条件注解@Conditional 56 3.4.1 点睛 56 3.4.2 示例 57 3.5 组合...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part2

    3.1 Spring Aware 48 3.1.1 点睛 48 3.1.2 示例 49 3.2 多线程 51 3.2.1 点睛 51 3.2.2 示例 51 3.3 计划任务 54 3.3.1 点睛 54 3.3.2 示例 54 3.4 条件注解@Conditional 56 3.4.1 点睛 56 3.4.2 示例 57 3.5 组合...

    JavaEE开发的颠覆者SpringBoot实战[完整版].part1

    3.1 Spring Aware 48 3.1.1 点睛 48 3.1.2 示例 49 3.2 多线程 51 3.2.1 点睛 51 3.2.2 示例 51 3.3 计划任务 54 3.3.1 点睛 54 3.3.2 示例 54 3.4 条件注解@Conditional 56 3.4.1 点睛 56 3.4.2 示例 57 3.5 组合...

    Spring攻略(第二版 中文高清版).part1

    3.1 启用Spring的AspectJ注解支持 113 3.1.1 问题 113 3.1.2 解决方案 113 3.1.3 工作原理 113 3.2 用AspectJ注解声明aspect 115 3.2.1 问题 115 3.2.2 解决方案 115 3.2.3 工作原理 116 3.3 访问...

    Spring攻略(第二版 中文高清版).part2

    3.1 启用Spring的AspectJ注解支持 113 3.1.1 问题 113 3.1.2 解决方案 113 3.1.3 工作原理 113 3.2 用AspectJ注解声明aspect 115 3.2.1 问题 115 3.2.2 解决方案 115 3.2.3 工作原理 116 3.3 访问...

    DWR.xml配置文件说明书(含源码)

    在远程bean的默认构造函数不能进行进一步配置的情况下这种类型的creator比较有用.”Spring”类型允许你通过spring创建实例. 下面是关于creator的参考资料和配置参数说明. Creator Parameter User new class 类的全...

Global site tag (gtag.js) - Google Analytics