常見誤區以為需在 JSF 中編寫登錄頁面邏輯。正確模式:Acegi 自動提供登錄頁面、會話管理、記住我功能;JSF 專注於業務組件渲染。兩者通過 web.xml 統一部署,無需編寫任何 Java 安全代碼。
<web-app>
<!-- 1. 上下文參數 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<!-- 2. 監聽器:啟動 Spring 與 JSF -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<!-- 3. Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<!-- 4. Acegi Filter Chain Proxy -->
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
| 組件 | 角色 |
|---|---|
ContextLoaderListener | 啟動 Spring IOC 容器,載入 acegi-config.xml 定義的所有 Bean(過濾器、管理器、提供者) |
ConfigureListener | JSF 初始化監聽器,處理 ServletContext/Request/Session/Attribute 事件 |
FilterToBeanProxy | 橋接 Servlet Filter 與 Spring Bean,委派給 FilterChainProxy 執行完整過濾器鏈 |
/* 映射 | 所有請求經過 Acegi 過濾器鏈,JSF 頁面 (.faces) 自然受保護 |
ServletContext<listener>ContextLoaderListener.contextInitialized() → 解析 Acegi 配置,創建 WebApplicationContext,實例化所有安全 BeanConfigureListener.contextInitialized() → 校驗 JSF 托管 Bean 類是否存在FilterToBeanProxy,註冊為過濾器FacesServlet,開始監聽 *.faces 請求ServletRequest、ServletResponseConfigureListener.requestInitialized() → 準備 FacesContext(新會話則創建)HttpSession/* → 調用 FilterToBeanProxy.doFilter()FilterSecurityInterceptor)ServletContext 認證屬性ConfigureListener.attributeReplaced() → 同步 FacesContext(無 JSF Bean 變更則無操作)FacesServlet → 執行 JSF 生命周期 → 響應ExceptionTranslationFilter 配置 authenticationEntryPoint 指向登錄頁,無需 JSF 介入Authentication 存入 HTTP Session,JSF 可通過 ExternalContext.getSessionMap() 訪問defaultTargetUrl / filterProcessesUrl 機制)webappsjsf-1_1_01.zip) → JAR 放入 WEB-INF/libhttps://localhost:8080/JSFAcegiSample本文確立了 web.xml 為核心的聲明式整合模式:Spring ContextLoaderListener 載入安全上下文,Acegi FilterToBeanProxy 攔截所有請求,JSF ConfigureListener 管理組件生命周期。兩大框架通過 Servlet 標準接口(Listener/Filter/Servlet)協作,實現「安全與展示關注點完全分離」。下一部分將深入 Bean 級安全保護。