接觸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的集成固然更簡便些,後面會寫。