美文网首页Spring Security
spring security动态配置url权限

spring security动态配置url权限

作者: go4it | 来源:发表于2017-08-15 15:09 被阅读1084次

    对于使用spring security来说,存在一种需求,就是动态去配置url的权限,即在运行时去配置url对应的访问角色。这里简单介绍一下。

    Standard Filter Aliases and Ordering

    首先需要了解spring security内置的各种filter:

    Alias Filter Class Namespace Element or Attribute
    CHANNEL_FILTER ChannelProcessingFilter http/intercept-url@requires-channel
    SECURITY_CONTEXT_FILTER SecurityContextPersistenceFilter http
    CONCURRENT_SESSION_FILTER ConcurrentSessionFilter session-management/concurrency-control
    HEADERS_FILTER HeaderWriterFilter http/headers
    CSRF_FILTER CsrfFilter http/csrf
    LOGOUT_FILTER LogoutFilter http/logout
    X509_FILTER X509AuthenticationFilter http/x509
    PRE_AUTH_FILTER AbstractPreAuthenticatedProcessingFilter Subclasses N/A
    CAS_FILTER CasAuthenticationFilter N/A
    FORM_LOGIN_FILTER UsernamePasswordAuthenticationFilter http/form-login
    BASIC_AUTH_FILTER BasicAuthenticationFilter http/http-basic
    SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter http/@servlet-api-provision
    JAAS_API_SUPPORT_FILTER JaasApiIntegrationFilter http/@jaas-api-provision
    REMEMBER_ME_FILTER RememberMeAuthenticationFilter http/remember-me
    ANONYMOUS_FILTER AnonymousAuthenticationFilter http/anonymous
    SESSION_MANAGEMENT_FILTER SessionManagementFilter session-management
    EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter http
    FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor http
    SWITCH_USER_FILTER SwitchUserFilter N/A

    这里我们要操作的是FilterSecurityInterceptor这个interceptor,使用withObjectPostProcessor来设置

    FilterSecurityInterceptor

    这个filter有几个要素,如下:

    • SecurityMetadataSource
    • AccessDecisionManager
    • AuthenticationManager

    可以根据情况自己去重新设置,这里我们重写一下SecurityMetadataSource用来动态获取url权限配置,还有AccessDecisionManager来进行权限判断。

    MyAccessDecisionManager

    public class MyAccessDecisionManager implements org.springframework.security.access.AccessDecisionManager {
    
        @Override
        public void decide(Authentication authentication, Object object,
                           Collection<ConfigAttribute> configAttributes)
                throws AccessDeniedException, InsufficientAuthenticationException {
            //这段代码其实不需要,因为spring-security-core-4.1.4.RELEASE-sources.jar!/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java第215行判断提前返回了,不会进入decide方法
            if (CollectionUtils.isEmpty(configAttributes)) {
                throw new AccessDeniedException("not allow");
            }
            Iterator<ConfigAttribute> ite = configAttributes.iterator();
            while (ite.hasNext()) {
                ConfigAttribute ca = ite.next();
                String needRole = ((org.springframework.security.access.SecurityConfig) 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;
        }
    }
    

    这里遍历判断该url所需的角色看用户是否具备,有具备则返回,都不具备则抛出AccessDeniedException异常

    MyFilterInvocationSecurityMetadataSource

    public class MyFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource {
    
        private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    
        private final Map<String,String> urlRoleMap = new HashMap<String,String>(){{
            put("/open/**","ROLE_ANONYMOUS");
            put("/health","ROLE_ANONYMOUS");
            put("/restart","ROLE_ADMIN");
            put("/demo","ROLE_USER");
        }};
    
        @Override
        public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
            FilterInvocation fi = (FilterInvocation) object;
            String url = fi.getRequestUrl();
    //        String httpMethod = fi.getRequest().getMethod();
            for(Map.Entry<String,String> entry:urlRoleMap.entrySet()){
                if(antPathMatcher.match(entry.getKey(),url)){
                    return SecurityConfig.createList(entry.getValue());
                }
            }
            //没有匹配到,默认是要登录才能访问
            return SecurityConfig.createList("ROLE_USER");
    //        return null;
        }
    
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return FilterInvocation.class.isAssignableFrom(clazz);
        }
    }
    

    这里以内存的map来展示一下,实际应用可以从分布式配置中心或者数据库中读取,另外循环遍历这个可能消耗性能,必要时得优化一下。

    SecurityConfig

    最后需要综合配置一下,如下

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                        public <O extends FilterSecurityInterceptor> O postProcess(
                                O fsi) {
                            fsi.setSecurityMetadataSource(mySecurityMetadataSource());
                            fsi.setAccessDecisionManager(myAccessDecisionManager());
                            return fsi;
                        }
                    });
        }
    
        @Bean
        public FilterInvocationSecurityMetadataSource mySecurityMetadataSource() {
            MyFilterInvocationSecurityMetadataSource securityMetadataSource = new MyFilterInvocationSecurityMetadataSource();
            return securityMetadataSource;
        }
    
        @Bean
        public AccessDecisionManager myAccessDecisionManager() {
            return new MyAccessDecisionManager();
        }
    }
    

    doc

    相关文章

      网友评论

        本文标题:spring security动态配置url权限

        本文链接:https://www.haomeiwen.com/subject/pifvrxtx.html