基于角色的权限控制
RBAC模型的基本思想是将访问许可权分配给一定的角色,用户通过饰演不同的角色获得角色所拥有的访问许可权。
- 用户(User):一个具有唯一标识符的用户,与权限相分离,只能通过所属的Role去关联权限,一个用户可以拥有多项角色;
- 角色(Role):一定数量的权限的集合,角色可以继承,一个角色对应多项权限;
- 权限(Resource):也可以看作是资源,它对应了应用系统中的一个功能;
|
|
通过以上五张表即可完成基于RBAC的权限控制。当然,上面提到的都是整个权限管理的基础数据,也就是需要配置的数据用户登录的时候,获取对应的权限。
- 查询用户所属的角色ID;
- 根据角色ID从sys_roles_resources表中获取该角色所能访问的节点列表;
- 从sys_resources中查询节点列表的相关信息;
- 对产生的节点列表信息进行处理,生成访问决策列表保存到SESSION中;
Spring security
Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。
简单原理
首先,权限管理离不开登陆验证的,登陆验证拦截器AuthenticationProcessingFilter;还有就是对访问的资源管吧,资源管理拦截器AbstractSecurityInterceptor;但拦截器里面的实现需要一些组件来实现,所以就有了AuthenticationManager、accessDecisionManager等组件来支撑。
流程解读:**用户登陆**,会被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容器了,具体的方法调用为:
到了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应用的配置方式:
这里配置了一个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是需要拦截每次请求的。
总结
在系统启动时候,启动Spring自动加载系统的权限资源表,加载进静态资源中;将spring security配置为从数据库中认证,登录成功后将用户所拥有的角色和资源存入session中,每次操作都当URI与session中的资源进行对比。
(如果涉及到数据权限,根据权限类别多一步操作,因为数据库要将权限进行分类,所以在mybatis上再扩展一层,从session中拿取所拥有数据级别来过滤数据。)
注释: web.xml加载过程 : context-param -> listener -> filter -> servlet -> spring而同类型节点之间的实际程序调用的时候的顺序是根据对应的 mapping 的顺序进行调用的。
- web 容器启动时初始化每个 filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法的。
- 【加载Spring】比如filter 需要用到 bean ,但加载顺序是: 先加载filter 后加载spring,则filter中初始化操作中的bean为null; 所以,如果过滤器中要使用到 bean,可以将spring 的加载 改成 Listener的方式 :
12345 <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>