接触Spring Security是由于在面试以前,面试官要我用Spring Boot+Spring Security实现用户登陆校验的功能。在此以前接触过一些Spring Boot,对Spring Security则彻底没有了解,只知道它是一个权限管理的框架。也好,借此机会写些文章记录下了解和使用Spring Security的过程。java
Spring Security是一套验证受权框架。Spring Security最重要的两个功能也就是验证(authentication)和受权(authorization)。搭建网站过程当中如下需求十分常见:web
不用Spring Security框架,常见的作法是先利用Filter拦截须要权限的页面,经过Session判断用户是否登陆且拥有对应权限,若已登陆且有权限就进行过滤,未登陆或者没有权限则拦截、跳转登陆页面,提示相关信息。咱们知道要访问某个页面或者进行某个操做时,须要知足已登陆和有权限两个条件,这就是Spring Security框架核心的两个功能:验证和受权。面试
验证(authentication):指的是创建系统使用者信息( principal )的过程,使用者能够是一个设备、用户等;spring
受权(authorization):指的是判断某个 principal 在咱们的应用是否容许执行某个操做。在进行受权判断以前,要求所要使用到的规则必须在验证过程当中已经创建好。数据库
一个标准的验证场景:express
接下来讲说,Spring Security是如何实现这些流程的,主要是流程,不怎么涉及源码,源码太多看了我也晕啊! 安全
首先是容器启动时经过SecurityMetadataSource (自定义拦截器中的属性)中的loadResourceDefine方法加载系统资源与权限列表resourceMap(一个Map结构,资源[url]为key,权限[auth]为value )。服务器
其次是WEB服务器启动,加载security内置的拦截器链:app
SecurityContextPersistenceFilter框架
WebAsyncManagerIntegrationFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
DefaultLoginPageGeneratingFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
固然,咱们须要自定义一个拦截器配置具体的要拦截信息,并指定它在拦截器链中生效的位置。这就是Spring Security中四个重要的类:
AbstractSecurityInterceptor :继承此类定义一个拦截器,这个拦截器会加载在FILTER_SECURITY_INTERCEPTOR以前。
AuthenticationManager:读取登陆用户信息、权限。
SecurityMetadataSource :加载资源与权限的所有对应关系的,并提供一个经过资源获取全部权限的方法。
AccessDecisionManager:受权器,经过登陆用户的权限信息、资源、获取资源所需的权限来根据不一样的受权策略来判断用户是否有权限访问资源。
在网上找的一个自定义拦截器的例子:
/**
* 该拦截器用以添加资源拦截
*
* 添加一个拦截器,配置在 FILTER_SECURITY_INTERCEPTOR以前
* 继承原本最后的 AbstractSecurityInterceptor以实现 登陆过程
*
*
* 过滤器依赖于 SecurityContext 和 Authentication 对象,来进行辨别用户的 GrantedAuthority。
* 因此,咱们要将这个过滤器的位置放在 FilterSecurityInterceptor 以前
*
*/
public class AppFilterSecurityInterceptor extends AbstractSecurityInterceptor
implements Filter { private FilterInvocationSecurityMetadataSource securityMetadataSource; //拦截器入口 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } /** * fi里面有一个被拦截的url * 调用MyInvocationSecurityMetadataSource的getAttributes(Object object) * 这个方法获取fi对应的全部权限 * 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够 */ public void invoke(FilterInvocation fi) throws IOException, ServletException { InterceptorStatusToken token = super.beforeInvocation(fi); try { // 执行下一个拦截器 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } //实现接口的方法 public Class<? extends Object> getSecureObjectClass() { return FilterInvocation.class; } public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } public void destroy() { } public void init(FilterConfig arg0) throws ServletException { } //用以注入securityMetadataSource public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return this.securityMetadataSource; } public void setSecurityMetadataSource( FilterInvocationSecurityMetadataSource newSource) { this.securityMetadataSource = newSource;
}
定义好拦截器以后,在xml文件中进行配置:
1.配置spring security的配置文件securityConfig.xml
<!--忽略http的其余配置,此处只用于说明如何配置Filter->
<http auto-config="true" use-expressions="true" >
<custom-filter ref="appFilter" before="FILTER_SECURITY_INTERCEPTOR" />
</http>
<!--一个自定义的filter,必须包含 authenticationManager,accessDecisionManager,securityMetadataSource三个属性-->
<beans:bean id="appFilter"
class="security.filter.AppFilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="appAuthenticationManager" />
<beans:property name="accessDecisionManager" ref="appAccessDecisionManagerBean" />
<beans:property name="securityMetadataSource" ref="appSecurityMetadataSource" />
</beans:bean>
<!--在java文件中对authenticationManager,accessDecisionManager,securityMetadataSource接口进行扩展,在xml文件中进行配置-->
<!--资源源数据定义,将全部的资源和权限对应关系创建起来,即定义某一资源能够被哪些角色访问-->
<beans:bean id="appSecurityMetadataSource" class="security.filter.properties.AppInvocationSecurityMetadataSource" >
<beans:constructor-arg name="resourcesDao" ref="securityResourcesDao"/>
</beans:bean>
<!--访问决策器,决定某个用户具备的角色,是否有足够的权限去访问某个资源 -->
<beans:bean id="appAccessDecisionManagerBean" class="security.filter.properties.AppAccessDecisionManager">
</beans:bean>
<!--在这个类中,你就能够从数据库中读入用户的密码,角色信息,是否锁定,帐号是否过时等 -->
<beans:bean id="appUserDetailService" class="security.service.impl.AppUserDetailService" />
2.web.xml文件中的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!--加载Spring XML配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:securityConfig.xml </param-value> </context-param> <!-- Spring Secutiry3.1的过滤器链配置 --> <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> <!-- Spring 容器启动监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--系统欢迎页面 --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
这是Spring Security与Spring的集成,与Spring Boot的集成固然更简便些,后面会写。