【问题标题】:Is it possible to secure only one spring boot rest endpoint via certificate?是否可以通过证书仅保护一个弹簧引导休息端点?
【发布时间】:2019-08-19 10:30:49
【问题描述】:

关于架构的一些信息: - 我们在 Cloud Foundry 中运行(使用 https 路由) - 我们有一个网关(spring cloud Netflix zuul) - 我们的应用在内部由令牌保护

如果您需要其他信息,尽管问。

现在我们要通过证书保护我们网关 (api/v1/authorizations) 的一条路由。这样只有拥有此证书的客户端才能调用此端点。

这可能吗?

【问题讨论】:

    标签: spring spring-boot spring-security certificate cloud-foundry


    【解决方案1】:

    我将把你的问题分成两部分,因为它们是 Spring Security 的两个独立问题。

    是否可以只保护一个弹簧靴休息端点

    是的,您可以自定义您的 Spring Security 配置。可以打开所有端点,除了一个端点是安全的。也可以混合使用,所以有一些对所有人开放,一些由方法A(可能是密码)保护,另一些由方法B(可能是证书)保护。

    这是一个简单的示例,其中混合了开放 (/css/**) 和安全端点 (/user/**)。

    protected void configure(HttpSecurity http) throws Exception {
                http
                .authorizeRequests(authorizeRequests ->
                    authorizeRequests
                        .antMatchers("/css/**", "/index").permitAll()
                        .antMatchers("/user/**").hasRole("USER")
                )
                .formLogin(formLogin ->
                    formLogin
                        .loginPage("/login")
                        .failureUrl("/login-error")
                );
    }
    

    发件人:https://github.com/spring-projects/spring-security/blob/master/samples/boot/helloworld/src/main/java/org/springframework/security/samples/config/SecurityConfig.java#L34-L44

    通过证书?

    当然。 Spring Security 支持通过 x.509 证书进行身份验证。

    https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#x509

    这是一个使用 Spring Security 配置 x.509 身份验证的示例。

        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            http
                .x509()
                    .subjectPrincipalRegex("OU=(.*?)(?:,|$)")
                    .and()
                .authorizeRequests()
                    .mvcMatchers("/admin/**").hasRole("ADMIN")
                    .mvcMatchers("/noauth").permitAll()
                    .anyRequest().authenticated();
            // @formatter:on
        }
    

    发件人:https://github.com/nebhale/mtls-sample/blob/master/server/src/main/java/io/pivotal/mtlssample/server/ServerApplication.java#L96-L105

    前三行将身份验证配置为使用 x509 证书。其余四行将授权配置为要求管理员用户访问/admin/**,允许任何人访问/noauth,并要求任何经过身份验证的用户访问其他任何内容。

    要在 Cloud Foundry 上运行,您无需在应用中执行任何特殊操作,但您的平台运营商需要启用 mTLS 支持。您可以查看我在上面针对客户端和服务器测试提供的完整演示,其中包含在 Cloud Foundry 上运行的说明。

    https://github.com/nebhale/mtls-sample

    希望有帮助!

    【讨论】:

    • 谢谢。但我不能把它带到一条特定的路线上。如果我添加这个http.x509()...,那么我总是得到403 Forbidden
    • 如果您逐字复制上面的原始示例,这将是预期的结果。上面的例子只是展示了如何启用基于 x509 的授权。它设置的授权要求对每个请求进行身份验证。我已更新示例以允许未经身份验证的访问某个 URL,但您需要调整授权以适应您的应用程序的需求。
    【解决方案2】:

    在这种情况下,我们必须保留 server.ssl.client-auth=want,它代表需要客户端身份验证,但不是强制性的。 您可以为您想要的端点配置 x509 身份验证,即 api/v1/authorizations

    示例代码

    @Order(1)
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)  
    public class X509WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private ClientAuthenticationEntryPoint clientAuthenticationEntryPoint;
        
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("api/v1/authorizations")
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .x509()
                    .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                    .userDetailsService(userDetailsService())
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(clientAuthenticationEntryPoint)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .csrf()
                .disable();
        }
    
        @Bean
        public UserDetailsService userDetailsService() {
            return (UserDetailsService) username -> {
                if (username.equals("localhost")) {
                    return new User(username, "",
                            AuthorityUtils
                                    .commaSeparatedStringToAuthorityList("ROLE_USER"));
                } else {
                    throw new UsernameNotFoundException(String.format("User %s not found", username));
                }
            };
        }
    

    【讨论】:

      猜你喜欢
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 2013-01-13
      • 1970-01-01
      • 2018-08-20
      • 2020-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多