亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot+springsecurity怎么實現動態url細粒度權限認證

發布時間:2021-06-22 13:05:09 來源:億速云 閱讀:417 作者:小新 欄目:開發技術

這篇文章主要介紹springboot+springsecurity怎么實現動態url細粒度權限認證,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

1、MyFilterInvocationSecurityMetadataSource 類判斷該訪問路徑是否被保護

@Component
//用于設置受保護資源的權限信息的數據源
public class MyFilterInvocationSecurityMetadataSource implements
        FilterInvocationSecurityMetadataSource {
    @Bean
    public AntPathMatcher getAntPathMatcher(){
        return new AntPathMatcher();
    }    
    @Autowired  
	//獲取數據庫中的保存的url  Url表只儲存受保護的資源,不在表里的資源說明不受保護,任何人都可以訪問
    private RightsMapper rightsMapper; 
    
    @Autowired
    private AntPathMatcher antPathMatcher;
    @Override
    /*
     * @param 被調用的保護資源
     * @return 返回能夠訪問該保護資源的角色集合,如果沒有,則應返回空集合。
     */
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) object;
        //獲取用戶請求的Url
        String url = fi.getRequestUrl();
        //先到數據庫獲取受權限控制的Url
        List<Rights> us = rightsMapper.queryAll();
        //用于儲存用戶請求的Url能夠訪問的角色
        Collection<ConfigAttribute> rs=new ArrayList<ConfigAttribute>();
        for(Rights u:us){
            if (u.getUrl() != null) {
                //逐一判斷用戶請求的Url是否和數據庫中受權限控制的Url有匹配的
                if (antPathMatcher.match(u.getUrl(), url)) {
                    //如果有則將可以訪問該Url的角色儲存到Collection<ConfigAttribute>
                    rs.add(rightsMapper.queryById(u.getId()));
                }
            }
        }
        if(rs.size()>0) {
            return rs;
        }
        //沒有匹配到,就說明此資源沒有被控制,所有人都可以訪問,返回null即可,返回null則不會進入之后的decide方法
        return null;
    }
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        // TODO 自動生成的方法存根
        return null;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        // TODO 自動生成的方法存根
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

rights表中的部分內容:

表結構

springboot+springsecurity怎么實現動態url細粒度權限認證

內容:

springboot+springsecurity怎么實現動態url細粒度權限認證

2、MyAccessDecisionManager 類判斷該用戶是否有權限訪問

@Component
//用于設置判斷當前用戶是否可以訪問被保護資源的邏輯
public class MyAccessDecisionManager implements AccessDecisionManager {
    @Override
    /*
     * @param 請求該保護資源的用戶對象
     * @param 被調用的保護資源
     * @param 有權限調用該資源的集合
     */
    public void decide(Authentication authentication, Object object,
                       Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        //遍歷configAttributes,查看當前用戶是否有對應的權限訪問該保護資源
        while (ite.hasNext()) {
            ConfigAttribute ca = ite.next();
            String needRole = ca.getAttribute();
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (ga.getAuthority().equals(needRole)) {
                    // 匹配到有對應角色,則允許通過
                    return;
                }
            }
        }
        // 該url有配置權限,但是當前登錄用戶沒有匹配到對應權限,則禁止訪問
        throw new AccessDeniedException("not allow");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

3、在SecurityConfig 類中配置說明

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MyUserDetailsService myUserDetailsService;
    @Autowired
    private SendSmsSecurityConfig sendSmsSecurityConfig;
    @Autowired
    private MyAccessDecisionManager myAccessDecisionManager;
    @Autowired
    private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;
    //加密機制
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance(); // 不加密
    }
    //認證
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()//對請求授權
                .antMatchers("/**").permitAll()
                .anyRequest()//任何請求
                .authenticated()//登錄后訪問
                .withObjectPostProcessor(
                        new ObjectPostProcessor<FilterSecurityInterceptor>() {
                            @Override
                            public <O extends FilterSecurityInterceptor> O postProcess(
                                    O fsi) {
                                fsi.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);
                                fsi.setAccessDecisionManager(myAccessDecisionManager);
                                return fsi;
                            }
                        })
                .and().csrf().disable();
    }
}

配置如下代碼:

springboot+springsecurity怎么實現動態url細粒度權限認證

至此完成所有配置!!!

SpringSecurity解決公共接口自定義權限驗證失效問題,和源碼分析

背景:

自定義權限認證,一部分接口必須要有相應的角色權限,一部分接口面向所有訪問者,一部分接口任何人都不能訪問。但是在使用 SpringSecurity的過程中發現,框架會將沒有指定角色列表的URL資源直接放行,不做攔截。

用戶登錄認證成功后,攜帶Token訪問URL資源,spring security 根據Token(請求頭Authorization中)來分辨不同用戶。

用戶權限數據源是一個Map:以 URL資源為Key,以有權訪問的Key的角色列表為Value。

使用時發現當一個接口有Key,但是Value為空或null時,spring security 框架自動放行,導致了權限失效問題。

解決方法有兩種:

第一種方法:

默認rejectPublicInvocations為false。

對需要控制權限的URL資源添加標志,以防止roleList為空,跳過了權限驗證.

公共權限設置為null,不進行權限驗證

第二種方法:

配置rejectPublicInvocations為true

此后roleList為空,或者沒有找到URL資源時,都為拒絕訪問

需要控制權限的URL資源,即使對應角色為空,也會進行權限驗證

公共權限設置為所有角色和匿名角色,不進行權限驗證

package org.springframework.security.access.intercept;
/**
 * 對安全對象(訪問請求+用戶主體)攔截的抽象類源碼
 */
public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
	// ... 其他方法省略
	
	protected InterceptorStatusToken beforeInvocation(Object object) {
		Assert.notNull(object, "Object was null");
		final boolean debug = logger.isDebugEnabled();
		if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
			throw new IllegalArgumentException(
					"Security invocation attempted for object "
							+ object.getClass().getName()
							+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
							+ getSecureObjectClass());
		}
		// 從權限數據源獲取了當前 <URL資源> 對應的 <角色列表>
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
				
		// 框架在此處判斷URL資源對應的角色列表是否為空
		if (attributes == null || attributes.isEmpty()) {
			// rejectPublicInvocations默認為false 
			// 可以配置為true,即角色列表為空的時候不進行放行
			if (rejectPublicInvocations) {
				throw new IllegalArgumentException(
						"Secure object invocation "
								+ object
								+ " was denied as public invocations are not allowed via this interceptor. "
								+ "This indicates a configuration error because the "
								+ "rejectPublicInvocations property is set to 'true'");
			}
			if (debug) {
				logger.debug("Public object - authentication not attempted");
			}
			publishEvent(new PublicInvocationEvent(object));
			return null; // no further work post-invocation
		}
		if (debug) {
			logger.debug("Secure object: " + object + "; Attributes: " + attributes);
		}
		
		// 如果當前用戶權限對象為null
		if (SecurityContextHolder.getContext().getAuthentication() == null) {
			credentialsNotFound(messages.getMessage(
					"AbstractSecurityInterceptor.authenticationNotFound",
					"An Authentication object was not found in the SecurityContext"),
					object, attributes);
		}
		Authentication authenticated = authenticateIfRequired();
		// Attempt authorization,此處調用accessDecisionManager 進行鑒權
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));
			throw accessDeniedException;
		}
		if (debug) {
			logger.debug("Authorization successful");
		}
		if (publishAuthorizationSuccess) {
			publishEvent(new AuthorizedEvent(object, attributes, authenticated));
		}
		// Attempt to run as a different user,這里可以另外配置或修改用戶的權限對象,特殊場景使用
		Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,
				attributes);
		if (runAs == null) {
			if (debug) {
				logger.debug("RunAsManager did not change Authentication object");
			}
			// no further work post-invocation
			return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,
					attributes, object);
		}
		else {
			if (debug) {
				logger.debug("Switching to RunAs Authentication: " + runAs);
			}
			SecurityContext origCtx = SecurityContextHolder.getContext();
			SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
			SecurityContextHolder.getContext().setAuthentication(runAs);
			// need to revert to token.Authenticated post-invocation
			return new InterceptorStatusToken(origCtx, true, attributes, object);
		}
	}
	// ... 其他方法略
}

以上是“springboot+springsecurity怎么實現動態url細粒度權限認證”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

崇文区| 茌平县| 同仁县| 江西省| 南部县| 凉山| 台中市| 商南县| 苗栗市| 高要市| 铜鼓县| 华亭县| 肥西县| 东乌| 赣州市| 巧家县| 泗阳县| 贵定县| 泰兴市| 民和| 长宁县| 安塞县| 临猗县| 北安市| 溧阳市| 旌德县| 清徐县| 巴彦淖尔市| 卢湾区| 元江| 剑阁县| 肥城市| 上饶市| 榆社县| 迭部县| 大英县| 齐齐哈尔市| 山东| 简阳市| 平泉县| 崇左市|