首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring Security 權限管理系統完整實戰:從配置到核心代碼深度解析

     編輯:Java 相關知識

架構概覽

基於 Spring Security 3.2 + Spring 4 构建企業級 RBAC 權限系統,核心組件:

  • FilterChainProxy:安全過濾器鏈入口
  • AuthenticationManager:認證管理器
  • AccessDecisionManager:授權決策器
  • FilterInvocationSecurityMetadataSource:資源-權限元數據源
  • UserDetailsService:用戶詳情加載器

關鍵配置文件

1. web.xml 核心過濾器

<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>

2. application-security.xml 核心配置

<http auto-config="true" use-expressions="true">
    <!-- 靜態資源放行 -->
    <http pattern="/js/**" security="none"/>
    <http pattern="/css/**" security="none"/>
    <http pattern="/images/**" security="none"/>
    
    <!-- 表單登錄 -->
    <form-login login-page="/login.jsp" 
                authentication-success-handler-ref="authSuccessHandler"
                authentication-failure-handler-ref="authFailureHandler"/>
    
    <!-- 記住我 -->
    <remember-me key="appKey" use-secure-cookie="true"/>
    
    <!-- 會話管理 -->
    <session-management>
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="false"/>
    </session-management>
    
    <!-- 自定義授權過濾器 -->
    <custom-filter ref="customFilterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>
</http>

<authentication-manager>
    <authentication-provider user-service-ref="customUserDetailsService">
        <password-encoder hash="md5">
            <salt-source user-property="username"/>
        </password-encoder>
    </authentication-provider>
</authentication-manager>

核心代碼實現

1. 動態資源權限元數據源

@Service
public class CustomFilterInvocationSecurityMetadataSource 
    implements FilterInvocationSecurityMetadataSource {
    
    @Autowired AuthorityService authorityService;
    private static Map<String, Collection<ConfigAttribute>> resourceMap;
    
    @PostConstruct
    public void loadResourceDefine() {
        List<Authority> authorities = authorityService.findAll();
        resourceMap = new HashMap<>();
        for (Authority auth : authorities) {
            String url = auth.getResourceUrl();
            ConfigAttribute ca = new SecurityConfig(auth.getCode()); // ROLE_*
            resourceMap.computeIfAbsent(url, k -> new ArrayList<>()).add(ca);
        }
    }
    
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) {
        String url = ((FilterInvocation)object).getRequestUrl();
        // Ant 路徑匹配,最長路徑優先
        return resourceMap.entrySet().stream()
            .filter(e -> url.startsWith(e.getKey()))
            .max(Comparator.comparingInt(e -> e.getKey().length()))
            .map(Map.Entry::getValue)
            .orElse(null); // null 表示放行
    }
}

2. 訪問決策器

@Service
public class CustomAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication auth, Object obj, Collection<ConfigAttribute> configAttrs) {
        if (configAttrs == null) return; // 無需權限
        
        for (ConfigAttribute ca : configAttrs) {
            String needRole = ((SecurityConfig)ca).getAttribute();
            for (GrantedAuthority ga : auth.getAuthorities()) {
                if (needRole.equals(ga.getAuthority())) return; // 權限匹配
            }
        }
        throw new AccessDeniedException("權限不足: " + configAttrs);
    }
}

3. 自定義安全攔截器

@Service
public class CustomFilterSecurityInterceptor 
    extends AbstractSecurityInterceptor implements Filter {
    
    @Resource CustomFilterInvocationSecurityMetadataSource securityMetadataSource;
    @Resource CustomAccessDecisionManager accessDecisionManager;
    @Resource AuthenticationManager authenticationManager;
    
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        // 未登錄重定向
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            ((HttpServletResponse)res).sendRedirect(request.getContextPath() + "/login.jsp");
            return;
        }
        
        FilterInvocation fi = new FilterInvocation(request, res, chain);
        InterceptorStatusToken token = beforeInvocation(fi);
        try {
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            afterInvocation(token, null);
        }
    }
}

4. 用戶詳情服務

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired UserDao userDao;
    @Autowired AuthorityDao authorityDao;
    
    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userDao.findByName(username);
        if (user == null) throw new UsernameNotFoundException("用戶不存在");
        
        List<GrantedAuthority> authorities = new ArrayList<>();
        if (user.getUserType() == UserType.SYS_ADMIN) {
            authorityDao.findAll().forEach(a -> 
                authorities.add(new SimpleGrantedAuthority(a.getCode())));
        } else {
            user.getAuthoritySet().forEach(a -> 
                authorities.add(new SimpleGrantedAuthority(a.getCode())));
        }
        
        return new UserDetail(
            user.getName(), user.getPassword(), 
            user.getEnabled(), true, true, 
            user.getUserState() == UserState.ENABLE,
            authorities
        );
    }
}

5. 實體設計要點

  • User:用戶名、密碼、狀態、類型、關聯 UserGroup、Authority、Crop
  • Authority:代碼 ROLE_*、資源 URL、父權限、菜單關聯、樹形結構
  • UserGroup:層級結構、團隊管理員、用戶列表
  • UserDetail:繼承 Spring Security User,擴展真實姓名、用戶組 ID 等業務字段

登錄/登出擴展點

// 登錄成功處理器
public class CustomAuthSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(...) {
        // 記錄登錄日誌、加載用戶作物權限到 Session
        super.onAuthenticationSuccess(...);
    }
}

// 登出成功處理器
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(...) {
        // 記錄登出日誌
        super.onLogoutSuccess(...);
    }
}

部署注意事項

  1. 密碼編碼:生產環境務必使用 BCrypt (BCryptPasswordEncoder) 替代 MD5
  2. CSRF 保護:Spring Security 4+ 默認開啟,表單需包含 _csrf 隱藏域
  3. 會話固定攻擊防護:session-management session-fixation-protection="migrateSession"
  4. 權限緩存:資源映射表建議引入 Redis/Ehcache,減少數據庫查詢

總結

本文展示了基於 Spring Security 構建動態 RBAC 系統的完整鏈路:配置 → 元數據加載 → 認證授權核心組件 → 業務實體設計。關鍵在於 FilterInvocationSecurityMetadataSource 動態加載資源權限映射,配合自定義 AccessDecisionManager 實現細粒度 URL 級權限控制,滿足企業級應用複雜授權需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved