基于角色的权限控制

来源:互联网 发布:传奇霸业移动网络 编辑:程序博客网 时间:2024/06/01 08:31

基于角色的权限控制

RBAC模型的基本思想是将访问许可权分配给一定的角色,用户通过饰演不同的角色获得角色所拥有的访问许可权。

  1. 用户(User):一个具有唯一标识符的用户,与权限相分离,只能通过所属的Role去关联权限,一个用户可以拥有多项角色;
  2. 角色(Role):一定数量的权限的集合,角色可以继承,一个角色对应多项权限;
  3. 权限(Resource):也可以看作是资源,它对应了应用系统中的一个功能;
  1. # 用户表:
  2. CREATE TABLE `sys_users` (
  3. `user_id` int(11) NOT NULL AUTO_INCREMENT,
  4. `employee_id` int(11) DEFAULT NULL,
  5. `user_name` varchar(45) DEFAULT NULL,
  6. `password` varchar(45) DEFAULT NULL,
  7. `create_time` datetime NOT NULL,
  8. `status` varchar(20) DEFAULT '1',
  9. `last_login_time` datetime DEFAULT NULL,
  10. `last_login_ip` varchar(100) DEFAULT NULL,
  11. `type` varchar(20) DEFAULT NULL,
  12. PRIMARY KEY (`user_id`)
  13. ) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8;
  14. # 角色表
  15. CREATE TABLE `sys_roles` (
  16. `role_id` int(11) NOT NULL AUTO_INCREMENT,
  17. `name` varchar(45) DEFAULT NULL,
  18. `description` varchar(100) DEFAULT NULL,
  19. `status` varchar(20) DEFAULT NULL,
  20. PRIMARY KEY (`role_id`)
  21. ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
  22. # 用户-角色关系
  23. CREATE TABLE `sys_users_roles` (
  24. `ur_id` int(11) NOT NULL AUTO_INCREMENT,
  25. `user_id` int(11) DEFAULT NULL,
  26. `role_id` int(11) DEFAULT NULL,
  27. `status` varchar(20) DEFAULT NULL,
  28. PRIMARY KEY (`ur_id`)
  29. ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
  30. # 资源表
  31. CREATE TABLE `sys_resources` (
  32. `resource_id` int(11) NOT NULL,
  33. `text` varchar(45) DEFAULT NULL
  34. `type` varchar(45) DEFAULT NULL ,
  35. `priority` int(11) DEFAULT NULL
  36. `url` varchar(100) DEFAULT NULL
  37. `parent_id` int(11) DEFAULT NULL ,
  38. `remark` varchar(45) DEFAULT NULL ,
  39. `status` varchar(20) DEFAULT NULL ,
  40. `icon` varchar(100) DEFAULT NULL,
  41. PRIMARY KEY (`resource_id`)
  42. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  43. # 角色-资源-映射表
  44. CREATE TABLE `sys_roles_resources` (
  45. `rr_id` int(11) NOT NULL AUTO_INCREMENT,
  46. `role_id` int(11) DEFAULT NULL ,
  47. `resources_id` int(11) DEFAULT NULL
  48. `organization_id` int(11) DEFAULT NULL,
  49. `status` varchar(20) DEFAULT NULL,
  50. PRIMARY KEY (`rr_id`)
  51. ) ENGINE=InnoDB AUTO_INCREMENT=366 DEFAULT CHARSET=utf8;

通过以上五张表即可完成基于RBAC的权限控制。当然,上面提到的都是整个权限管理的基础数据,也就是需要配置的数据用户登录的时候,获取对应的权限。

  1. 查询用户所属的角色ID;
  2. 根据角色ID从sys_roles_resources表中获取该角色所能访问的节点列表;
  3. 从sys_resources中查询节点列表的相关信息;
  4. 对产生的节点列表信息进行处理,生成访问决策列表保存到SESSION中;

Spring security

Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。

简单原理

  1. 首先,权限管理离不开登陆验证的,登陆验证拦截器AuthenticationProcessingFilter;还有就是对访问的资源管吧,资源管理拦截器AbstractSecurityInterceptor;但拦截器里面的实现需要一些组件来实现,所以就有了AuthenticationManageraccessDecisionManager等组件来支撑。

  1. 流程解读:用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。

Spring-Security的启动加载细节

从加载并解析xml配置文件开始的,spring通过注册自己的ServletContextListener:ContextLoaderListener,来监听ServletContext,一旦ServletContext建立完成,spring就开始加载并解析配置文件,然后初始化ioc容器了,具体的方法调用为:

  1. org.springframework.web.context.ContextLoaderListener#contextInitialized
  2. ->org.springframework.web.context.ContextLoader#initWebApplicationContext
  3. ->org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext
  4. ->org.springframework.context.support.AbstractApplicationContext#refresh

到了refresh方法之后,开始进行一系列实质性的动作了,本文关心的两个重要的动作见下图注释。这里有一点需要明确的是spring的bean解析和创建bean是两个独立的过程,在解析时生成的一种叫beandefinition的对象(存放于beanFactory的beanDefinitionMap里)代表一个将要创建的bean实例的诸多信息(如bean的class类名,构造参数,是singleton还是prototype等等)用于指导bean的创建。创建出来的bean实例存放于beanFactory的xxxxBeanMap、xxxxSingletonObjects等集合字段中。 
加载spring security的配置文件 –> 实例化bean

Spring-Security的切入点

spring security的整个工作模式是通过Servlet中的Filter机制,创建一个由多种Filter和Interceptor组成的FilterChain来实现的,以下是标准的spring-security嵌入web应用的配置方式:

  1. <filter>
  2. <filter-name>springSecurityFilter</filter-name>
  3. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  4. <init-param>
  5. <param-name>targetBeanName</param-name>
  6. <param-value>springSecurityFilterChain</param-value>
  7. </init-param>
  8. </filter>
  9. <filter-mapping>
  10. <filter-name>springSecurityFilter</filter-name>
  11. <url-pattern>/*</url-pattern>
  12. </filter-mapping>

这里配置了一个servlet的filter,这个filter本身并不处理具体的请求,它其实是一个filter chain,它内部包含了一个由多个spring security提供的filter的list,它负责把请求委派给list中的每一个filter进行处理。这个springSecurityFilterChain的类型是:DefaultSecurityFilterChain,它和它包含的大部分filter都是spring security包提供的类,如前文所述,这些filter实例都是spring的inner bean,是由spring隐式地初始化并置于容器中管理的。 
1、UsernamePasswordAuthenticationFilter:该filter用于用户初次登录时验证用户身份(authentication)。该filter只在初次认证时存在,一旦认证通过将会从 filter chain中移除。 
2、FilterSecurityInterceptor:当用户登入成功之后,每次发送请求都会使用该filter检查用户是否已经通过了认证。如果通过了认证,就放行,否则转向登录页面。 
两个filter的差别在于: 第一个负责初次登入时的用户检查,这个检查需要根据用户提供的用户名和密码去数据库核对,若存在,将相关信息封装在一个Authentication对象中。这个filter可以说是处理初次登录时的authentication工作。而第二个filter则不需要像每个filter每次都去查询数据库,它只需要从 security context中查看当前请求用户对应的Authentication 对象是否已经存在就可以了,这个filter处理的是登入成功之后的authentication工作。这个filter是需要拦截每次请求的。

  1. <http use-expressions='true' entry-point-ref="myAuthenticationEntryPoint">
  2. <custom-filter ref="myAuthorizationFilter" before="FILTER_SECURITY_INTERCEPTOR" />
  3. <logout logout-url="/j_spring_security_logout"
  4. invalidate-session="true" logout-success-url="/checkin.do" />
  5. <custom-filter ref="myAuthenticationFilter" position="FORM_LOGIN_FILTER" />
  6. <access-denied-handler ref="accessDeniedHandler" />
  7. </http>
  8. <beans:bean id="myAuthenticationEntryPoint"
  9. class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
  10. <beans:property name="loginFormUrl" value="/login.html"></beans:property>
  11. </beans:bean>
  12. <!-- 自定义权限不足处理程序 -->
  13. <beans:bean id="accessDeniedHandler"
  14. class="com.mz3co.bspheis.sysadmin.security.MyAccessDeniedHandler">
  15. <beans:property name="errorPage" value="/sysadmin/error.jsp?errorCode=1"></beans:property>
  16. </beans:bean>
  17. <!--认证-->
  18. <beans:bean id="myAuthenticationFilter"
  19. class="com.mz3co.bspheis.sysadmin.security.MyAuthenticationFilter">
  20. <beans:property name="authenticationManager" ref="myAuthenticationManager" />
  21. <beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
  22. <beans:property name="authenticationSuccessHandler">
  23. <beans:bean
  24. class="com.mz3co.bspheis.sysadmin.security.MySimpleUrlAuthenticationSuccessHandler">
  25. </beans:bean>
  26. </beans:property>
  27. <beans:property name="authenticationFailureHandler">
  28. <beans:bean
  29. class="com.mz3co.bspheis.sysadmin.security.MySimpleUrlAuthenticationFailureHandler">
  30. <beans:property name="defaultFailureUrl" value="/login.html" />
  31. </beans:bean>
  32. </beans:property>
  33. </beans:bean>
  34. <!--授权-->
  35. <beans:bean id="myAuthorizationFilter"
  36. class="com.mz3co.bspheis.sysadmin.security.MyAuthorizationFilter">
  37. <beans:property name="authenticationManager" ref="myAuthenticationManager" />
  38. <beans:property name="accessDecisionManager"> <!--实现访问控制决策-->
  39. <beans:bean
  40. class="com.mz3co.bspheis.sysadmin.security.MyAccessDecisionManager" />
  41. </beans:property>
  42. <beans:property name="securityMetadataSource">
  43. <beans:bean
  44. class="com.mz3co.bspheis.sysadmin.security.MySecurityMetadataSource">
  45. </beans:bean>
  46. </beans:property>
  47. </beans:bean>
  48. <authentication-manager alias="myAuthenticationManager">
  49. <authentication-provider user-service-ref="myUserDetailService">
  50. </authentication-provider>
  51. </authentication-manager>
  52. <!--数据库获取用户信息-->
  53. <beans:bean id="myUserDetailService"
  54. class="com.mz3co.bspheis.sysadmin.security.MyUserDetailService">
  55. </beans:bean>

总结

在系统启动时候,启动Spring自动加载系统的权限资源表,加载进静态资源中;将spring security配置为从数据库中认证,登录成功后将用户所拥有的角色和资源存入session中,每次操作都当URI与session中的资源进行对比。 
(如果涉及到数据权限,根据权限类别多一步操作,因为数据库要将权限进行分类,所以在mybatis上再扩展一层,从session中拿取所拥有数据级别来过滤数据。)

注释: web.xml加载过程 : context-param -> listener -> filter -> servlet -> spring而同类型节点之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。 
1. web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。 
2. 【加载Spring】比如filter 需要用到 bean ,但加载顺序是: 先加载filter 后加载spring,则filter中初始化操作中的bean为null; 所以,如果过滤器中要使用到 bean,可以将spring 的加载 改成 Listener的方式 :

  1. <listener>
  2. <listener-class>
  3. org.springframework.web.context.ContextLoaderListener
  4. </listener-class>
  5. </listener>
0 0