使用SQL查询的动态Spring安全性

时间:2011-12-05 06:35:25

标签: spring spring-mvc spring-security

你好我想制作一个拦截url模式并通过在spring security中使用sql查询动态访问。

通常我们在XML中使用这种表示法,我想从数据库中获取这些值(/ add-role和ROLE_ADMIN)。

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

是否可以动态执行此操作?

3 个答案:

答案 0 :(得分:11)

声明

正如Spring Security FAQ所提到的,你要做的第一件事就是问我应该这样做吗?安全性很复杂,应该对配置进行广泛测试。允许配置动态更改只会使使应用程序更容易受到攻击的事情变得更加复杂。如果你真的想这样做,FAQ概述了实现这一目标的基本方法。我已经扩展了下面的常见问题解答答案。

实施自定义FilterInvocationSecurityMetadataSource

要动态获取安全URL映射,您可以实现自己的FilterInvocationSecurityMetadataSource。下面给出了一个示例实现。

注意:请记住,对于Spring Security拦截的每个请求,都会调用getAttributes,因此您很可能需要某种缓存。

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

你不能使用命名空间来连接它,所以拿另一个tip from the 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 :(得分:1)

您无法从数据库中获取这些值,但您可以编写一个名为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

答案 2 :(得分:0)

我已创建此条目用于更新目的

实施自定义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对其进行了测试