本文記錄一次完整的技術棧整合過程:Spring MVC(控制層)+ Httl(模板引擎)+ Shiro(安全框架)+ Maven(依賴管理)。涵蓋 pom.xml 依賴、web.xml 過濾器鏈、Httl 屬性配置、Shiro 權限配置等核心文件。
<dependencies>
<!-- JUnit -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
<!-- Spring MVC -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.0.5.RELEASE</version></dependency>
<!-- Httl 模板引擎 -->
<dependency><groupId>com.github.httl</groupId><artifactId>httl-springmvc</artifactId><version>1.0.10</version></dependency>
<dependency><groupId>com.github.httl</groupId><artifactId>httl</artifactId><version>1.0.11</version></dependency>
<!-- JRE 環境下 Httl 必須依賴 Javassist 編譯器 -->
<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.18.2-GA</version></dependency>
<dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.3</version></dependency>
<!-- Shiro 安全框架 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.2.2</version></dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>1.2.2</version></dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.2.2</version></dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.2.2</version></dependency>
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-quartz</artifactId><version>1.2.2</version></dependency>
<!-- Servlet API -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency>
<!-- Spring AOP (Shiro 注解支持) -->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.5</version></dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.5</version></dependency>
</dependencies>
關鍵說明:JRE 環境(無 JDK tools.jar)下 Httl 必須顯式依賴 javassist,並在 httl.properties 指定 compiler=httl.spi.compilers.JavassistCompiler,否則啟動拋 ClassNotFoundException。
<web-app version="3.0" ...>
<!-- Spring 容器啟動:加載 Shiro 配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-shiro.xml</param-value>
</context-param>
<!-- Shiro 過濾器:必須放在最前面,攔截所有請求 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
<!-- Httl 屬性文件加載 -->
<context-param><param-name>httl.properties</param-name><param-value>classpath:httl.properties</param-value></context-param>
<!-- 編碼過濾器 -->
<filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
<init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
<!-- Spring MVC DispatcherServlet -->
<servlet><servlet-name>springMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>springMVC</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
<!-- ContextLoaderListener 初始化 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>
順序關鍵:shiroFilter 映射必須在 encodingFilter 之前,確保權限校驗優先於編碼處理。
# 必須指定編譯器(JRE 環境下)
compiler=httl.spi.compilers.JavassistCompiler
# 模板加載路徑
template.directories=classpath:/templates
# 緩存設置
cache.enable=true
cache.size=1000
# 編碼
input.encoding=UTF-8
output.encoding=UTF-8
# 導入常用工具類
import=java.util.*,com.example.util.*
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
<property name="cacheManager" ref="ehCacheManager"/>
</bean>
<bean id="myRealm" class="com.example.shiro.MyShiroRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="2"/>
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.httl"/>
<property name="successUrl" value="/index.httl"/>
<property name="unauthorizedUrl" value="/403.httl"/>
<property name="filterChainDefinitions">
/login.httl = anon
/static/** = anon
/** = authc
</property>
</bean>
<!-- EhCache 配置 -->
<bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<!-- 支持 @RequiresPermissions 等註解 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.httl.HttlViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".httl"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
<mvc:resources mapping="/static/**" location="/static/"/><context:component-scan base-package="com.example.controller"/>https://localhost:8080/ 自動跳轉 /login.httl。/index.httl,頁面渲染無誤說明 Httl 生效。/403.httl,說明 Shiro 攔截生效。ClassNotFoundException: JavassistCompiler,說明 Httl 編譯器配置正確。| 現象 | 原因 | 解決 |
|---|---|---|
啟動報 ClassNotFound: JavassistCompiler | JRE 環境缺少編譯器 | 加 javassist 依賴 + httl.properties 指定 compiler |
| Shiro 註解不生效 | 缺少 AOP 依賴或 Advisor 未註冊 | 加 aspectjrt/weaver + 配置 AuthorizationAttributeSourceAdvisor |
| 模板找不到 | 前綴/後綴配置錯誤 | 檢查 HttlViewResolver prefix/suffix 與模板實際路徑 |
| 編碼亂碼 | CharacterEncodingFilter 順序靠後 | 確保 encodingFilter 在 shiroFilter 之後(但 url-pattern 相同靠 filter-mapping 順序) |