傳統硬編碼安全檢查耦合業務邏輯,複用性差。Spring AOP + Acegi Security (現 Spring Security 前身) 提供聲明式、可插拔的安全框架,配合 Java 5 註解極大簡化配置。
User: id, enable, user_name, user_pass, email_box
RoleInfo: id, role_name, role_title, descp
UserRole: user_id, user_name, role_id, role_name -- 多對多關聯
-- 種子數據
Role: ROLE_USER, ROLE_ADMIN
User1: ROLE_USER + ROLE_ADMIN
User2: ROLE_USER
User3: 無角色
acegi-security.xml<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="org.ymcn.security.AcegiUserDetailsService">
<property name="userDao" ref="userDao"/>
<property name="userRoleDao" ref="userRoleDao"/>
</bean>
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<list><ref bean="roleVoter"/></list>
</property>
<property name="allowIfAllAbstainDecisions" value="false"/>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value="ROLE_"/>
</bean>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,rememberMeFilter,exceptionFilter,securityInterceptor
</value>
</property>
</bean>
<bean id="securityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/admin/**=ROLE_ADMIN
/user/**=ROLE_USER
/cart/previeworder*=ROLE_USER
</value>
</property>
</bean>
關鍵過濾器:authenticationProcessingFilter (登錄)、rememberMeFilter (記住我)、exceptionFilter (異常轉向登錄/拒絕頁)。
<filter>
<filter-name>acegiFilterChain</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>acegiFilterChain</filter-name>
<url-pattern>*.j</url-pattern>
</filter-mapping>
<!-- 必須在 Struts2 Filter 前面 -->
<!-- 安全攔截器 -->
<bean id="serviceSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<bean class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
<property name="attributes">
<bean class="org.acegisecurity.annotation.SecurityAnnotationAttributes"/>
</property>
</bean>
</property>
</bean>
<!-- 自動代理目標 Service -->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list><value>transactionInterceptor</value><value>serviceSecurityInterceptor</value></list>
</property>
<property name="beanNames">
<list><value>userService</value><value>mailService</value></list>
</property>
</bean>
AcegiUserDetailsServicepublic class AcegiUserDetailsService implements UserDetailsService {
private UserDao userDao;
private UserRoleDao userRoleDao;
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
User user = userDao.getUserByName(userName);
if (user == null) throw new UsernameNotFoundException("User not found");
List<UserRole> roles = userRoleDao.getUserRoleByUserName(userName);
if (roles == null || roles.isEmpty()) throw new UsernameNotFoundException("No roles");
GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
int i = 0;
for (UserRole r : roles) {
authorities[i++] = new GrantedAuthorityImpl(r.getRoleName().toUpperCase());
}
return new org.acegisecurity.userdetails.User(
userName, user.getUserPass(), true, true, true, true, authorities);
}
// Setter 省略
}
核心職責:根據用戶名查詢用戶與角色,構建 UserDetails 返回。密碼驗證、會話管理均由框架自動處理。
public interface MailService {
@Secured({"ROLE_USER"})
void sendSimpleMail(Long userId);
@Secured({"ROLE_ADMIN"})
void sendAttachmentMail() throws Exception;
}
在介面或實現類方法上標註 @Secured,框架自動織入權限檢查。
acegi-security.xml、web.xml 過濾器、applicationContext.xml AOP 代理UserDetailsService 接入數據層@Secured 註解