基于角色的权限控制

基于角色的权限控制

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>
我们开发业务系统的时候,基本上都会涉及到权限管理模块,要求不同的人看到不同的菜单,操作不同的按钮,看到不同的数据。很多初学者面对这样的需求不知道如何下手,特别是稍微复杂点的权限,更是找不到方向,为此我们夜鹰教程网特别推出了这套基于角色权限管理视频教程,通过给用户分配角色,给角色分配权限,来实现权限管理。这样一来,可以实现不同的人管理不同的菜单,操作不同的按钮,看到不同的数据。可以划分权限组,每个组的成员拥有相同的权限。也可以把同一个人分配到不同的权限组,具有多个权限组的权限,实现权限的组合。 本套教程通过c-s和b-s两种模式讲解,如果没有学过winform开发的学员可以通过本套教程补充下winform开发知识,以桌面程序的方式实现数据的CRUD操作,同时还可以学会一些常用控件的使用,特别是树形菜单的使用,以及递归的编程思想。 C-S部分目录 001 课程介绍 002 效果演示 003 新建项目 004 用户表 005 角色表 006 功能权限表 007 用户分配角色的表 008 角色拥有的菜单权限 009 登录界面设计 010 控件name 011 数据库操作类 012 登录操作 013 用户类 014 新建主窗体 015 主窗体布局 016 树形菜单 017 加载数据 018 递归调用 019 点击事件 020 密码修改 021 设备管理 022 角色列表 023 用户列表 024 新增用户 025 插入用户数据 026 显示用户数据 027 查询用户数据 028 用户数据分页 029 编辑用户数据 030 保存用户数据 031 删除用户数据 032 角色主数据 033 表单验证 034 编辑角色 035 角色数据分页 036 删除角色数据 037 设备列表 038 新增设备 039 设备分页 040 编辑设备 041 删除设备 042 点击菜单打开对应窗体 043 用户参数传递 044 通过构造函数传递参数 045 验证旧密码 046 通过菜单打开用户列表 047 菜单权限 048 权限主数据 049 给用户分配角色 050 显示待分配的菜单权限 051 角色用有菜单权限 052 取消菜单权限 053 保存菜单权限 054 清空旧的菜单权限 055 加载旧的菜单权限 056 功能权限配置 057 加载待分配的功能权限 058 加载已分配的功能权限 059 移动功能权限 060 保存功能权限 061 清空旧功能权限 062 分配数据权限 063 拼接已经分配的权限 064 移除权限 065 角色数据权限保存 066 给用户分配角色 067 待分配的角色 068 判断用户拥有的权限 069 获取功能权限 070 控制功能按钮是否可用 071 不能的用户看到不同的数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值