最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot+springsecurity如何實(shí)現(xiàn)動(dòng)態(tài)url細(xì)粒度權(quán)限認(rèn)證

 更新時(shí)間:2021年06月22日 11:55:25   作者:佛說(shuō)  
這篇文章主要介紹了springboot+springsecurity如何實(shí)現(xiàn)動(dòng)態(tài)url細(xì)粒度權(quán)限認(rèn)證的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

謹(jǐn)記:Url表只儲(chǔ)存受保護(hù)的資源,不在表里的資源說(shuō)明不受保護(hù),任何人都可以訪問(wèn)

1、MyFilterInvocationSecurityMetadataSource 類判斷該訪問(wèn)路徑是否被保護(hù)

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

rights表中的部分內(nèi)容:

表結(jié)構(gòu)

在這里插入圖片描述

內(nèi)容:

在這里插入圖片描述

2、MyAccessDecisionManager 類判斷該用戶是否有權(quán)限訪問(wèn)

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

3、在SecurityConfig 類中配置說(shuō)明

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

配置如下代碼:

在這里插入圖片描述

至此完成所有配置?。?!

SpringSecurity解決公共接口自定義權(quán)限驗(yàn)證失效問(wèn)題,和源碼分析

背景:

自定義權(quán)限認(rèn)證,一部分接口必須要有相應(yīng)的角色權(quán)限,一部分接口面向所有訪問(wèn)者,一部分接口任何人都不能訪問(wèn)。但是在使用 SpringSecurity的過(guò)程中發(fā)現(xiàn),框架會(huì)將沒(méi)有指定角色列表的URL資源直接放行,不做攔截。

用戶登錄認(rèn)證成功后,攜帶Token訪問(wèn)URL資源,spring security 根據(jù)Token(請(qǐng)求頭Authorization中)來(lái)分辨不同用戶。

用戶權(quán)限數(shù)據(jù)源是一個(gè)Map:以 URL資源為Key,以有權(quán)訪問(wèn)的Key的角色列表為Value。

使用時(shí)發(fā)現(xiàn)當(dāng)一個(gè)接口有Key,但是Value為空或null時(shí),spring security 框架自動(dòng)放行,導(dǎo)致了權(quán)限失效問(wèn)題。

解決方法有兩種:

第一種方法:

默認(rèn)rejectPublicInvocations為false。

對(duì)需要控制權(quán)限的URL資源添加標(biāo)志,以防止roleList為空,跳過(guò)了權(quán)限驗(yàn)證.

公共權(quán)限設(shè)置為null,不進(jìn)行權(quán)限驗(yàn)證

第二種方法:

配置rejectPublicInvocations為true

此后roleList為空,或者沒(méi)有找到URL資源時(shí),都為拒絕訪問(wèn)

需要控制權(quán)限的URL資源,即使對(duì)應(yīng)角色為空,也會(huì)進(jìn)行權(quán)限驗(yàn)證

公共權(quán)限設(shè)置為所有角色和匿名角色,不進(jìn)行權(quán)限驗(yàn)證

package org.springframework.security.access.intercept;
/**
 * 對(duì)安全對(duì)象(訪問(wèn)請(qǐng)求+用戶主體)攔截的抽象類源碼
 */
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());
		}
		// 從權(quán)限數(shù)據(jù)源獲取了當(dāng)前 <URL資源> 對(duì)應(yīng)的 <角色列表>
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
				
		// 框架在此處判斷URL資源對(duì)應(yīng)的角色列表是否為空
		if (attributes == null || attributes.isEmpty()) {
			// rejectPublicInvocations默認(rèn)為false 
			// 可以配置為true,即角色列表為空的時(shí)候不進(jìn)行放行
			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);
		}
		
		// 如果當(dāng)前用戶權(quán)限對(duì)象為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,此處調(diào)用accessDecisionManager 進(jìn)行鑒權(quán)
		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,這里可以另外配置或修改用戶的權(quán)限對(duì)象,特殊場(chǎng)景使用
		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);
		}
	}
	// ... 其他方法略
}

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

遂宁市| 湾仔区| 枣庄市| 津市市| 江城| 喀喇沁旗| 绥滨县| 丹东市| 乌鲁木齐县| 隆化县| 门源| 岐山县| 内江市| 九江市| 伊春市| 佛教| 彩票| 盐源县| 常山县| 迭部县| 娄底市| 上思县| 武鸣县| 成都市| 盘山县| 冕宁县| 绥中县| 海口市| 和林格尔县| 鄂托克前旗| 兴化市| 开阳县| 伊宁市| 东丽区| 富锦市| 儋州市| 黄陵县| 阳谷县| 故城县| 饶阳县| 富阳市|