首页 > 解决方案 > Java - Spring 安全性、Shibboleth (apache) 和 onelogin

问题描述

实际的 Spring Security 配置是这样的:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/uri1/**").hasAuthority(Permission.AUTHORITY1.toString())
        .antMatchers("/uri2/**").hasAuthority(Permission.AUTHORITY2.toString())
        .anyRequest().hasAuthority(Permission.AUTHORITY3.toString())
        .and().httpBasic()
        .realmName("App").and().csrf().disable();
        http.authorizeRequests();
        http.headers().frameOptions().sameOrigin().cacheControl().disable();
    }
    
    @Bean
    public Filter shallowEtagHeaderFilter() {
        return new ShallowEtagHeaderFilter();
    }
}

Web MVC 配置是这样的:

@Configuration
public class DefaultView extends WebMvcConfigurerAdapter{

    @Override
    public void addViewControllers( ViewControllerRegistry registry ) {
        registry.addViewController( "/" ).setViewName( "forward:myPage.html" );
        registry.setOrder( Ordered.HIGHEST_PRECEDENCE);
        super.addViewControllers( registry );
    }
}

我必须通过使用 onelogin 的身份验证替换 Spring Security 中完成的 httpBasic 身份验证(如果我了解我在 Internet 上找到的内容,则使用 SAML)。

通过研究,我发现一种可能是在 Apache 服务器上使用 Shibboleth,另一种是使用 Spring Security 中的插件来管理 SAML。

对于第一个解决方案(shibboleth),目标是直接在 Apache 服务器上管理 onelogin 身份验证(如果未连接,用户将被重定向到 onelogin 身份验证页面,如果已连接,则资源可访问)并在 SAML 响应中返回所需信息(如用户名和其他需要的数据)在请求的标头中(能够在 Spring 应用程序中拥有它们)。

使用此解决方案,是否可以在 Spring 安全性中保留 httpBasic 身份验证并在 Shibboleth 设置的每个请求的标头中包含“Basic XXXX”?或者,我是否要从 Spring Security 中删除 httpBasic 身份验证?

对于第二种方案(Spring Security 中管理 SAML 的插件),是否与第一种方案的结果相同,必须如何实现?

预先感谢您的回复。

标签: springapachespring-securityoneloginshibboleth-sp

解决方案


欢迎来到stackoverflow。

...并在请求的标头中在 SAML 响应中返回所需的信息(如用户名和其他需要的数据)(能够在 Spring 应用程序中拥有它们)

如果我理解正确,您已经在使用弹簧安全性。这意味着您的应用程序已经在您的控制器/服务层中使用 spring security 填充上下文进行身份验证和授权。如果您使用上述方法,其中 apache 在标头中填充身份验证用户信息,那么这不会自行填充 spring 安全上下文,除非您在链中添加preAuthFilter以提取此信息并适当地填充您的 spring 上下文。

使用此解决方案,是否可以在 Spring 安全性中保留 httpBasic 身份验证并在 Shibboleth 设置的每个请求的标头中包含“Basic XXXX”?或者,我是否要从 Spring Security 中删除 httpBasic 身份验证?

如果你能做到,那么我上面说的会有点轻松。话虽如此,据我所知,没有选项可以使用 shibboleth apache 模块推断基本身份验证标头。此外,我还建议谨慎使用这种方法,因为使用这种方法,您仍然必须使用虚拟密码对应用中的用户进行身份验证(因为您不会通过 SAML 获取用户的正确密码)此标头),这会打开您的应用程序以进行安全漏洞利用。我强烈反对这种方法。Shibboleth的文档中 已经包含了一些Spoof Checking 。

[编辑] 根据附加信息,以下是您可以采取哪些措施来实现 apache 的所有处理并仍然有效地使用 spring security

首先在您的应用程序中提供PreAuthenticatedAuthenticationToken的实现,您可以为此目的使用AbstractPreAuthenticatedProcessingFilter 。下面提供了实现的框架,这是我过去的一项工作的摘录,并且非常精简,只保留了与您的场景相关的基本元素。还要仔细看看AuthenticationManagerAuthentication文档并确保您完全了解使用什么以及用于什么目的。请仔细阅读所有这 4 个类的 javadocs 以了解合同,否则在 Spring Security 中正确使用它可能会令人困惑。我已经添加了必要的细节作为 TODO 和骨架打击中的注释,你必须在你的实现中填写自己。

public class ShibbolethAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
    private final String containsValidPrincipalHeader = "_valid_shibboleth_header_present";
    private final String shibbolethHeader = "_shibboleth_header";
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * This authentication manager's authenticate method MUST return a fully populated
     * org.springframework.security.core.Authentication object. You may very well use
     * either PreAuthenticatedAuthenticationToken OR UsernamePasswordAuthenticationToken
     * with any credentials set, most important is to correctly populate the Authorities
     * in the returned object so that hasAuthority checks works as expected.
     *
     * Another point, you can use authentication.getPrincipal() in the implementation
     * of authenticate method to access the same principal object as returned by
     * getPreAuthenticatedPrincipal method of this bean. So basically you pass the 
     * using Principal object from this bean to AuthenticationManager's authenticate
     * method which in turn return a fully populated spring's Authentication object
     * with fully populated Authorities.
     */
    @Autowired
    private ShibbolethAuthenticationManager authenticationManager;

    @Override
    public void afterPropertiesSet() {
        setAuthenticationManager(authenticationManager);
        super.afterPropertiesSet();
    }

    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        String authHeader = request.getHeader(shibbolethHeader);
        if (authHeader == null) {
            logger.trace("No {} header found, skipping Shibboleth Authentication", shibbolethHeader);
            return null;
        }
        // TODO - validate if all header and it's contents are what they should be
        ShibbolethAuthToken authToken = /* TODO - provide your own impl to supply java.security.Principal object here */;
        request.setAttribute(containsValidPrincipalHeader, Boolean.TRUE);
        return authToken;
    }

    /**
     * No password required thus Credentials will return null
     */
    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        if (Boolean.TRUE.equals(request.getAttribute(containsValidPrincipalHeader)))
            return System.currentTimeMillis(); // just returning non null value to satisfy spring security contract
        logger.trace("Returning null Credentials for non authenticated request");
        return null;
    }
}

使用以下注册器将其注册为您的应用程序中的 servlet 过滤器

@Configuration
public class ShibbolethFilterRegistrar {

    /*
     * We don't want to register Shibboleth Filter in spring global chain thus
     * added this explicit registration bean to disable just that.
     */
    @Bean
    public FilterRegistrationBean shibbolethFilterRegistrar(Shibboleth shibbolethAuthFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(shibbolethAuthFilter);
        registration.setEnabled(false);
        return registration;
    }

    @Bean
    public ShibbolethAuthFilter shibbolethAuthFilter() {
        return new ShibbolethAuthFilter();
    }
}

然后,将您的 WebSecurityConfig 更改为以下

@Override
protected void configure(HttpSecurity http) throws Exception {
    /* autowire shibbolethAuthFilter bean as well */
    http
    .addFilterBefore(shibbolethAuthFilter, AbstractPreAuthenticatedProcessingFilter.class);
    .authorizeRequests()
    .antMatchers("/uri1/**").hasAuthority(Permission.AUTHORITY1.toString())
    .antMatchers("/uri2/**").hasAuthority(Permission.AUTHORITY2.toString())
    .anyRequest().hasAuthority(Permission.AUTHORITY3.toString())
    .and()
    .realmName("App").and().csrf().disable();
    http.authorizeRequests();
    http.headers().frameOptions().sameOrigin().cacheControl().disable();
}

希望这些指针可以帮助您成功集成外部身份验证。

恕我直言,以下仍然有效 - 据我了解您的情况,如果我不得不这样做,我个人更喜欢为此目的使用 spring security 内置 SAML 身份验证,因为它在所有可能的情况下都提供了与 spring security 的非常平滑的集成框架内的上下文。此外,它还简化了我的部署场景,我还必须负责配置 apache,这通常会为 DevOps 团队带来额外的工作量。为了简单性和可扩展性,spring security 内置的 SAML SSO 支持将是我的首选,除非有一个约束迫使我这样做(根据提供的解释,我在当前的讨论上下文中无法看到)。网上有大量的教程和示例可以完成它。我知道这不是您所要求的,但我想与您分享我过去在 Spring 分布式应用程序中为类似的 SSO 解决方案所做的工作,并学习了我所拥有的知识。希望能帮助到你!!


推荐阅读