欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity授權(quán)機(jī)制的實現(xiàn)(AccessDecisionManager與投票決策)

 更新時間:2025年04月13日 10:16:21   作者:程序媛學(xué)姐  
本文主要介紹了SpringSecurity授權(quán)機(jī)制的實現(xiàn),其核心是AccessDecisionManager和投票系統(tǒng),下面就來介紹一下,感興趣的可以了解一下

引言

在企業(yè)級應(yīng)用開發(fā)中,安全控制不僅包括認(rèn)證(Authentication)——確認(rèn)用戶身份,還包括授權(quán)(Authorization)——確定用戶是否有權(quán)執(zhí)行特定操作。Spring Security提供了一套精心設(shè)計的授權(quán)機(jī)制,其核心是AccessDecisionManager和投票系統(tǒng)。與簡單的角色檢查相比,這種機(jī)制提供了更細(xì)粒度、更靈活的訪問控制能力。本文將深入探討Spring Security授權(quán)框架的內(nèi)部工作原理,重點分析AccessDecisionManager如何通過投票機(jī)制做出授權(quán)決策,以及如何根據(jù)業(yè)務(wù)需求進(jìn)行定制化配置。通過這些知識,開發(fā)者可以構(gòu)建既安全又靈活的訪問控制系統(tǒng)。

一、Spring Security授權(quán)架構(gòu)

Spring Security的授權(quán)架構(gòu)采用了責(zé)任鏈和投票模式相結(jié)合的設(shè)計,使授權(quán)決策過程模塊化且可擴(kuò)展。授權(quán)過程始于SecurityFilterChain中的FilterSecurityInterceptor,它攔截受保護(hù)資源的請求,收集安全元數(shù)據(jù)(如所需權(quán)限),然后委托給AccessDecisionManager進(jìn)行授權(quán)判斷。AccessDecisionManager通過組合多個AccessDecisionVoter實現(xiàn)復(fù)雜的授權(quán)策略,每個投票者根據(jù)自己的邏輯對授權(quán)請求投贊成、反對或棄權(quán)票。這種分層設(shè)計使得授權(quán)邏輯與業(yè)務(wù)代碼完全分離,便于維護(hù)和擴(kuò)展。

// FilterSecurityInterceptor 關(guān)鍵部分
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor {
    
    private final FilterInvocationSecurityMetadataSource securityMetadataSource;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        // 封裝HTTP請求
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        
        // 進(jìn)行安全攔截
        invoke(fi);
    }
    
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        // 檢查安全攔截器是否應(yīng)該被應(yīng)用
        if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)) {
            // 安全攔截器已應(yīng)用,繼續(xù)執(zhí)行過濾器鏈
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            return;
        }
        
        // 標(biāo)記攔截器已應(yīng)用
        fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
        
        // 執(zhí)行安全攔截
        InterceptorStatusToken token = super.beforeInvocation(fi);
        
        try {
            // 繼續(xù)執(zhí)行過濾器鏈
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            // 清理安全上下文
            super.finallyInvocation(token);
        }
        
        // 執(zhí)行后處理
        super.afterInvocation(token, null);
    }
    
    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }
    
    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }
}

二、AccessDecisionManager接口設(shè)計

AccessDecisionManager是Spring Security授權(quán)體系的核心接口,負(fù)責(zé)協(xié)調(diào)多個AccessDecisionVoter并做出最終授權(quán)決策。它定義了三個關(guān)鍵方法:decide()執(zhí)行授權(quán)判斷,supports(ConfigAttribute)檢查是否支持特定配置屬性,supports(Class)檢查是否支持特定安全對象類型。通過這些方法,AccessDecisionManager可以靈活處理不同類型的授權(quán)請求,如Web請求、方法調(diào)用或特定領(lǐng)域?qū)ο蟮脑L問。Spring Security提供了三種內(nèi)置實現(xiàn):AffirmativeBased(只要有一票贊成即通過)、ConsensusBased(多數(shù)票決定)和UnanimousBased(要求全票通過)。

// AccessDecisionManager接口定義
public interface AccessDecisionManager {
    /**
     * 對給定的安全對象做出訪問控制決策
     * @param authentication 當(dāng)前用戶的認(rèn)證信息
     * @param object 要訪問的安全對象
     * @param configAttributes 安全對象的安全配置屬性
     * @throws AccessDeniedException 如果拒絕訪問
     * @throws InsufficientAuthenticationException 如果認(rèn)證不足
     */
    void decide(Authentication authentication, Object object, 
            Collection<ConfigAttribute> configAttributes) 
            throws AccessDeniedException, InsufficientAuthenticationException;
    
    /**
     * 檢查此AccessDecisionManager是否支持指定的ConfigAttribute
     * @param attribute 要檢查的配置屬性
     * @return 如果支持該屬性返回true
     */
    boolean supports(ConfigAttribute attribute);
    
    /**
     * 檢查此AccessDecisionManager是否支持指定的安全對象類型
     * @param clazz 安全對象的類型
     * @return 如果支持該類型返回true
     */
    boolean supports(Class<?> clazz);
}

三、投票決策實現(xiàn)

Spring Security提供了三種不同的AccessDecisionManager實現(xiàn),每種實現(xiàn)代表不同的投票策略。AffirmativeBased采用"一票通過"策略,只要有一個投票者投贊成票就允許訪問,這是最寬松的策略。ConsensusBased基于"多數(shù)票"原則,根據(jù)贊成票與反對票的比較結(jié)果做出決策。UnanimousBased要求所有投票者都投贊成票(或棄權(quán))才允許訪問,是最嚴(yán)格的策略。這三種實現(xiàn)覆蓋了從寬松到嚴(yán)格的不同安全需求,開發(fā)者可以根據(jù)業(yè)務(wù)場景選擇合適的策略。

// AffirmativeBased實現(xiàn)關(guān)鍵代碼
public class AffirmativeBased extends AbstractAccessDecisionManager {
    
    public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
        super(decisionVoters);
    }
    
    @Override
    public void decide(Authentication authentication, Object object, 
            Collection<ConfigAttribute> attributes) 
            throws AccessDeniedException {
        int deny = 0;
        
        // 遍歷所有投票者
        for (AccessDecisionVoter voter : getDecisionVoters()) {
            // 獲取投票結(jié)果
            int result = voter.vote(authentication, object, attributes);
            
            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED: // 投票通過
                return; // 立即返回允許訪問
            case AccessDecisionVoter.ACCESS_DENIED: // 投票拒絕
                deny++; // 計數(shù)拒絕票
                break;
            default: // 棄權(quán)
                break;
            }
        }
        
        // 如果有拒絕票且沒有通過票,則拒絕訪問
        if (deny > 0) {
            throw new AccessDeniedException("訪問被拒絕,安全投票失敗");
        }
        
        // 所有投票者棄權(quán),根據(jù)配置決定(默認(rèn)允許)
        if (getAllowIfAllAbstainDecisions()) {
            return;
        }
        
        // 默認(rèn)拒絕訪問
        throw new AccessDeniedException("訪問被拒絕,沒有投票者同意");
    }
}

四、AccessDecisionVoter機(jī)制

AccessDecisionVoter是Spring Security授權(quán)機(jī)制中的投票者角色,負(fù)責(zé)對特定授權(quán)請求投票。每個投票者實現(xiàn)vote()方法,返回ACCESS_GRANTED(贊成)、ACCESS_DENIED(反對)或ACCESS_ABSTAIN(棄權(quán))。常用的投票者包括:RoleVoter(基于角色投票)、AuthenticatedVoter(基于認(rèn)證狀態(tài)投票)和WebExpressionVoter(基于SpEL表達(dá)式投票)。投票者的靈活性在于它可以基于任何條件做出決策,不僅限于用戶角色,還可以考慮時間、位置、資源屬性等因素。

// AccessDecisionVoter接口
public interface AccessDecisionVoter<S> {
    /**
     * 贊成訪問的常量
     */
    int ACCESS_GRANTED = 1;
    
    /**
     * 拒絕訪問的常量
     */
    int ACCESS_DENIED = -1;
    
    /**
     * 棄權(quán)的常量
     */
    int ACCESS_ABSTAIN = 0;
    
    /**
     * 對訪問請求進(jìn)行投票
     * @param authentication 當(dāng)前用戶的認(rèn)證信息
     * @param object 要訪問的安全對象
     * @param attributes 安全對象的配置屬性
     * @return 投票結(jié)果:ACCESS_GRANTED、ACCESS_DENIED或ACCESS_ABSTAIN
     */
    int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
    
    /**
     * 檢查此投票者是否支持指定的配置屬性
     * @param attribute 要檢查的配置屬性
     * @return 如果支持該屬性返回true
     */
    boolean supports(ConfigAttribute attribute);
    
    /**
     * 檢查此投票者是否支持指定的安全對象類型
     * @param clazz 安全對象的類型
     * @return 如果支持該類型返回true
     */
    boolean supports(Class<?> clazz);
}

// RoleVoter實現(xiàn)
public class RoleVoter implements AccessDecisionVoter<Object> {
    
    private String rolePrefix = "ROLE_";
    
    public int vote(Authentication authentication, Object object, 
            Collection<ConfigAttribute> attributes) {
        // 如果沒有屬性,棄權(quán)
        if (attributes.isEmpty()) {
            return ACCESS_ABSTAIN;
        }
        
        // 獲取用戶權(quán)限
        Collection<? extends GrantedAuthority> authorities = 
                authentication.getAuthorities();
        
        // 檢查每個配置屬性
        for (ConfigAttribute attribute : attributes) {
            if (this.supports(attribute)) {
                // 屬性值作為需要的角色
                String role = attribute.getAttribute();
                
                // 檢查用戶是否擁有該角色
                for (GrantedAuthority authority : authorities) {
                    if (role.equals(authority.getAuthority())) {
                        return ACCESS_GRANTED; // 用戶有所需角色,允許訪問
                    }
                }
                
                // 走到這里說明用戶沒有所需角色,返回拒絕
                return ACCESS_DENIED;
            }
        }
        
        // 沒有可支持的屬性,棄權(quán)
        return ACCESS_ABSTAIN;
    }
    
    @Override
    public boolean supports(ConfigAttribute attribute) {
        // 檢查屬性是否以角色前綴開頭
        return (attribute.getAttribute() != null) && 
                attribute.getAttribute().startsWith(rolePrefix);
    }
    
    @Override
    public boolean supports(Class<?> clazz) {
        return true; // 支持所有類型的安全對象
    }
}

五、自定義訪問控制規(guī)則

Spring Security的授權(quán)機(jī)制最大的優(yōu)勢在于其可擴(kuò)展性,開發(fā)者可以輕松實現(xiàn)自定義的訪問控制規(guī)則。通過創(chuàng)建自定義的AccessDecisionVoter,可以基于業(yè)務(wù)特定邏輯進(jìn)行授權(quán)決策,如限制特定時間段的訪問、根據(jù)用戶屬性控制權(quán)限或?qū)崿F(xiàn)數(shù)據(jù)行級安全。自定義投票者只需實現(xiàn)AccessDecisionVoter接口的三個方法,然后將其添加到AccessDecisionManager的投票者列表中即可。這種方式使復(fù)雜的授權(quán)需求變得易于實現(xiàn)且可維護(hù)。

// 自定義的工作時間投票者,只允許在工作時間訪問
public class BusinessHoursVoter implements AccessDecisionVoter<Object> {
    
    private final int startHour = 9;  // 工作開始時間
    private final int endHour = 17;   // 工作結(jié)束時間
    
    @Override
    public int vote(Authentication authentication, Object object, 
            Collection<ConfigAttribute> attributes) {
        // 檢查是否有工作時間限制的屬性
        boolean businessHoursRequired = attributes.stream()
                .anyMatch(a -> "BUSINESS_HOURS_ONLY".equals(a.getAttribute()));
        
        // 如果沒有時間限制,棄權(quán)
        if (!businessHoursRequired) {
            return ACCESS_ABSTAIN;
        }
        
        // 獲取當(dāng)前時間
        LocalTime now = LocalTime.now();
        int currentHour = now.getHour();
        
        // 檢查是否在工作時間內(nèi)
        if (currentHour >= startHour && currentHour < endHour) {
            return ACCESS_GRANTED;
        } else {
            return ACCESS_DENIED;
        }
    }
    
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return attribute != null && "BUSINESS_HOURS_ONLY".equals(attribute.getAttribute());
    }
    
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

// 自定義配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/admin/**").access("hasRole('ADMIN') and hasAuthority('BUSINESS_HOURS_ONLY')")
                .anyRequest().authenticated()
            .and()
                .formLogin()
                .loginPage("/login").permitAll();
        
        // 替換默認(rèn)的AccessDecisionManager
        http.authorizeRequests()
                .accessDecisionManager(accessDecisionManager());
    }
    
    @Bean
    public AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter<?>> voters = new ArrayList<>();
        voters.add(new WebExpressionVoter());
        voters.add(new RoleVoter());
        voters.add(new AuthenticatedVoter());
        voters.add(new BusinessHoursVoter());  // 添加自定義投票者
        
        // 使用"一票通過"策略
        return new AffirmativeBased(voters);
    }
}

六、方法級安全控制

除了Web請求的授權(quán)控制外,Spring Security還提供了方法級別的安全控制,使開發(fā)者能夠直接在業(yè)務(wù)方法上應(yīng)用授權(quán)規(guī)則。通過@PreAuthorize、@PostAuthorize等注解,可以使用SpEL表達(dá)式定義復(fù)雜的訪問條件。這些注解由MethodSecurityInterceptor處理,它同樣使用AccessDecisionManager進(jìn)行授權(quán)決策。方法級安全與Web安全共享相同的授權(quán)架構(gòu),但提供了更精細(xì)的控制粒度,特別適合業(yè)務(wù)邏輯層的權(quán)限管理。

// 啟用方法級安全
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        // 自定義方法級安全的AccessDecisionManager
        AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
        
        // 獲取現(xiàn)有的投票者并添加自定義投票者
        List<AccessDecisionVoter<?>> voters = new ArrayList<>(
                accessDecisionManager.getDecisionVoters());
        voters.add(new BusinessHoursVoter());
        
        // 創(chuàng)建新的AccessDecisionManager
        return new AffirmativeBased(voters);
    }
}

// 在服務(wù)類中使用方法級安全
@Service
public class UserService {
    
    @PreAuthorize("hasRole('ADMIN') and hasAuthority('BUSINESS_HOURS_ONLY')")
    public void deleteUser(Long userId) {
        // 刪除用戶的邏輯
    }
    
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public UserDetails viewUserProfile(Long userId) {
        // 查看用戶資料的邏輯
        return userProfile;
    }
    
    @PostAuthorize("returnObject.username == authentication.name or hasRole('ADMIN')")
    public UserDetails loadUserById(Long userId) {
        // 只允許查看自己的詳細(xì)信息或者管理員查看
        return userDetails;
    }
}

總結(jié)

Spring Security的授權(quán)機(jī)制以AccessDecisionManager和投票決策系統(tǒng)為核心,提供了一套靈活而強(qiáng)大的訪問控制框架。通過責(zé)任鏈模式和策略模式的結(jié)合,它實現(xiàn)了授權(quán)邏輯的模塊化和可擴(kuò)展性。AccessDecisionManager協(xié)調(diào)多個AccessDecisionVoter,根據(jù)不同的投票策略做出最終授權(quán)決策,支持從寬松到嚴(yán)格的各種安全需求。內(nèi)置的投票者如RoleVoter和WebExpressionVoter滿足了基本授權(quán)場景,而自定義投票者則使復(fù)雜的業(yè)務(wù)規(guī)則得以實現(xiàn)。方法級安全控制進(jìn)一步擴(kuò)展了授權(quán)能力,使開發(fā)者能夠在業(yè)務(wù)方法層面應(yīng)用精細(xì)的權(quán)限管理。理解并掌握這套授權(quán)機(jī)制,開發(fā)者可以構(gòu)建既安全又靈活的企業(yè)級應(yīng)用,有效平衡安全需求與用戶體驗。在安全威脅日益復(fù)雜的今天,Spring Security的授權(quán)框架為開發(fā)者提供了應(yīng)對挑戰(zhàn)的有力工具,使復(fù)雜的授權(quán)邏輯變得清晰可維護(hù),為應(yīng)用系統(tǒng)的安全基礎(chǔ)奠定了堅實基礎(chǔ)。

到此這篇關(guān)于SpringSecurity授權(quán)機(jī)制的實現(xiàn)(AccessDecisionManager與投票決策)的文章就介紹到這了,更多相關(guān)SpringSecurity授權(quán)機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring BeanFactory和FactoryBean區(qū)別解析

    Spring BeanFactory和FactoryBean區(qū)別解析

    這篇文章主要介紹了Spring BeanFactory和FactoryBean區(qū)別解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • SpringBoot加載多個配置文件實現(xiàn)dev、product多環(huán)境切換的方法

    SpringBoot加載多個配置文件實現(xiàn)dev、product多環(huán)境切換的方法

    這篇文章主要介紹了SpringBoot加載多個配置文件實現(xiàn)dev、product多環(huán)境切換,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • SpringBoot JPA出現(xiàn)錯誤:No identifier specified for en解決方案

    SpringBoot JPA出現(xiàn)錯誤:No identifier specified&nb

    這篇文章主要介紹了SpringBoot JPA出現(xiàn)錯誤:No identifier specified for en解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Spring MVC處理方法返回值過程解析

    Spring MVC處理方法返回值過程解析

    這篇文章主要介紹了Spring MVC處理方法返回值過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Java微信公眾號安全模式消息解密

    Java微信公眾號安全模式消息解密

    這篇文章主要為大家詳細(xì)介紹了Java微信公眾號安全模式消息解密,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Java根據(jù)模板實現(xiàn)excel導(dǎo)出標(biāo)準(zhǔn)化

    Java根據(jù)模板實現(xiàn)excel導(dǎo)出標(biāo)準(zhǔn)化

    這篇文章主要為大家詳細(xì)介紹了Java如何根據(jù)模板實現(xiàn)excel導(dǎo)出標(biāo)準(zhǔn)化,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,有需要的小伙伴可以參考下
    2024-03-03
  • 將字符串?dāng)?shù)字格式化為樣式1,000,000,000的方法

    將字符串?dāng)?shù)字格式化為樣式1,000,000,000的方法

    這篇文章主要介紹了將字符串?dāng)?shù)字格式化為樣式1,000,000,000的方法,有需要的朋友可以參考一下
    2014-01-01
  • JavaWeb中請求轉(zhuǎn)發(fā)和請求重定向的區(qū)別以及使用

    JavaWeb中請求轉(zhuǎn)發(fā)和請求重定向的區(qū)別以及使用

    今天帶大家學(xué)習(xí)JavaWeb的相關(guān)知識,文章圍繞著JavaWeb中請求轉(zhuǎn)發(fā)和請求重定向的區(qū)別以及使用展開,文中有非常詳細(xì)的介紹,需要的朋友可以參考下
    2021-06-06
  • jmeter基本使用小結(jié)

    jmeter基本使用小結(jié)

    jmeter是apache公司基于java開發(fā)的一款開源壓力測試工具,體積小,功能全,使用方便,是一個比較輕量級的測試工具,使用起來非常簡單。本文就簡單的介紹一下如何使用,感興趣的
    2021-11-11
  • SpringBoot中Aware接口使用及原理解析

    SpringBoot中Aware接口使用及原理解析

    在Spring中存在一個Aware接口,實現(xiàn)該接口可以讓我們的Bean獲取到Spring容器中特定的資源,但該接口只是個標(biāo)記接口,不存在任何方法,本文將給大家詳細(xì)介紹一下SpringBoot中Aware接口使用及原理,需要的朋友可以參考下
    2023-08-08

最新評論