Dynamic Spring Security с использованием SQL-запросов - PullRequest
3 голосов
/ 05 декабря 2011

Здравствуйте. Я хочу создать шаблон перехвата URL и получить динамический доступ с помощью SQL-запроса в весенней безопасности.

Обычно мы используем этот тип нотации в XML, и я хочу взять эти значения (/ add-role и ROLE_ADMIN) из базы данных.

<intercept-url pattern="/add-role*" access="ROLE_ADMIN" />

Возможно ли сделать это динамически?

Ответы [ 3 ]

11 голосов
/ 05 декабря 2011

Отказ

Как упоминает Spring Security FAQ , первое, что вы должны сделать, это спросить, должен ли я действительно это сделать? Безопасность сложна, и конфигурация должна быть тщательно протестирована. Разрешение динамического изменения конфигурации только еще больше усложняет ситуацию, делая приложение гораздо более уязвимым. Если вы действительно хотите это сделать, в разделе «Часто задаваемые вопросы» описан основной метод для достижения этой цели. Я расширил ответ на часто задаваемые вопросы ниже.

Реализация пользовательского FilterInvocationSecurityMetadataSource

Для динамического получения сопоставлений URL безопасности вы можете реализовать свой собственный FilterInvocationSecurityMetadataSource. Пример реализации приведен ниже.

ПРИМЕЧАНИЕ: Имейте в виду, что getAttributes будет вызываться для каждого запроса, который перехватывает Spring Security, поэтому вам, скорее всего, понадобится какое-то кэширование.

public class JdbcFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) object;

        String url = fi.getRequestUrl();
        HttpServletRequest request = fi.getHttpRequest();

        // Instead of hard coding the roles lookup the roles from the database using the url and/or HttpServletRequest
        // Do not forget to add caching of the lookup   
        String[] roles = new String[] { "ROLE_ADMIN", "ROLE_USER" };
        return SecurityConfig.createList(roles);
    }

    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

Создание BeanPostProcessor

Вы не можете использовать пространство имен для подключения, поэтому, воспользовавшись подсказкой из FAQ , вы можете использовать BeanPostProcessor, который может выглядеть следующим образом:

public class FilterInvocationSecurityMetadataSourcePostProcessor implements BeanPostProcessor, InitializingBean {
    private FilterInvocationSecurityMetadataSource securityMetadataSource;

    public Object postProcessAfterInitialization(Object bean, String name) {
        if (bean instanceof FilterSecurityInterceptor) {
            ((FilterSecurityInterceptor)bean).setSecurityMetadataSource(securityMetadataSource);
        }
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String name) {
        return bean;
    }

    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
        this.securityMetadataSource = securityMetadataSource;
    }

    public void afterPropertiesSet() throws Exception {
        Assert.notNull(securityMetadataSource,"securityMetadataSource cannot be null");
    }
}

Конфигурация XML

Затем, предполагая, что оба приведенных выше bean-компонента находятся в образце пакета, вы добавили бы следующую конфигурацию

<bean class="sample.FilterInvocationSecurityMetadataSourcePostProcessor">
    <property name="securityMetadataSource">
        <bean class="sample.JdbcFilterInvocationSecurityMetadataSource"/>
    </property>
</bean>

Возможные проблемы

Если в итоге вы получите ClassCastException, вы, скорее всего, столкнетесь с SEC-1957 , который был исправлен в Spring Security 3.1.1+. Попробуйте обновить его до последней версии, чтобы устранить эту проблему.

1 голос
/ 05 декабря 2011

Вы действительно не можете получить эти значения из базы данных, но вы можете написать собственный код под названием DecisionManager, который оценивает, разрешено ли выполнение ресурса. С помощью этого кода вы можете даже читать данные из базы данных.

<bean id="MyDecisionManagerBean" class="org.springframework.security.vote.UnanimousBased">
    <property name="decisionVoters">
        <list>
            <!-- <bean class="org.springframework.security.vote.RoleVoter"/>  -->
            <bean class="org.springframework.security.vote.RoleHierarchyVoter" >
                 <constructor-arg>
                  <bean class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl" factory-bean="roleHierarchyImplFactory" factory-method="createRoleHierarchyImpl"/>
                </constructor-arg>  
            </bean>
            <bean class="com.mycompany.RoleDenyVoter"/>
            <bean class="com.mycompany.RoleAllowVoter"/>
        </list>
    </property>
</bean> 

Ваш класс будет таким:

public class RoleDenyVoter implements AccessDecisionVoter {

    public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
         //read from the DB and decide if access is granted

процесс задокументирован здесь:

http://static.springsource.org/spring-security/site/docs/3.0.x/reference/authz-arch.html#authz-voting-based

0 голосов
/ 17 июля 2019

Я создал эту запись для целей обновления

Реализация пользовательского FilterInvocationSecurityMetadataSource

Этот класс получает только URL-адрес в каждом запросе и ищет их разрешения из базы данных или третьегосторонние приложения

public class CommonFilterSecurityMetaDataSource implements FilterInvocationSecurityMetadataSource {

private final Map<String, UrlRequestModel> permissions;

@Autowired
private UrlRequestDao urlRequestDao;

public CommonFilterSecurityMetaDataSource() {
    permissions = new Hashtable<>();
}

public List<ConfigAttribute> getAttributes(Object object) {
    final FilterInvocation fi = (FilterInvocation) object;
    final String url = fi.getRequestUrl();
    final String httpMethod = fi.getRequest().getMethod();
    final String key = String.format("%s %s", httpMethod, url);
    final UrlRequestModel urlRequestModel;

    List<ConfigAttribute> attributes = null;

    // Lookup your database (or other source) using this information and populate the
    // list of attributes
    if(permissions.containsKey(key)) {
        urlRequestModel= permissions.get(key);
    } else {
        urlRequestModel= catRequestDao.findByUrl(url);
        if(catRequestMapModel != null) {
            permissions.put(key, urlRequestModel);
        }
    }


    if (catRequestMapModel != null) {
        List<RoleModel> roles = ulrRequestModel.getRoleList();
        if(!roles.isEmpty()) {
            attributes = new ArrayList<>(roles.size());
            for (RoleModel role : roles) {
                attributes.add(new SecurityConfig(role.getDescription()));
            }
        }
    }



    return attributes;
}

public Collection<ConfigAttribute> getAllConfigAttributes() {
    return null;
}

public boolean supports(Class<?> clazz) {
    return FilterInvocation.class.isAssignableFrom(clazz);
}
}

Конфигурация Java

Для конфигурации Java добавьте только этот класс в ваш класс, который выходит из WebSecurityConfigurerAdapter

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.headers().frameOptions().disable();
    http.authorizeRequests().
            antMatchers( "/javax.faces.resource/**").permitAll().
            and()
            .exceptionHandling().accessDeniedPage("/accessDenied.jsf").
            and().formLogin().
            loginPage("/login.jsf").
            loginProcessingUrl("/loginAction").
            usernameParameter("app_username").
            passwordParameter("app_password").
            defaultSuccessUrl("/secure/index.jsf").
            and().logout().
            logoutUrl("/appLogout").
            logoutSuccessUrl("/login.jsf").logoutRequestMatcher(new AntPathRequestMatcher("/appLogout")).
            and().addFilterAfter(filterSecurityInterceptor(), FilterSecurityInterceptor.class);
    http.csrf().disable();

}
@Bean
public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {
    FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
    filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource());
    filterSecurityInterceptor.setAuthenticationManager(authenticationManager());
    filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
    filterSecurityInterceptor.setPublishAuthorizationSuccess(true);
    return filterSecurityInterceptor;
}


@Bean
public AccessDecisionManager accessDecisionManager() {
    AuthenticatedVoter authenticatedVoter = new AuthenticatedVoter();
    RoleVoter roleVoter = new RoleVoter();
    List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();
    voters.add(authenticatedVoter);
    voters.add(roleVoter);
    return new AffirmativeBased(voters);
}

@Bean
public FilterInvocationSecurityMetadataSource securityMetadataSource() {
    return new CommonFilterSecurityMetaDataSource();
}

Я проверил этоиспользуя Spring security 5.0.8

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...