【问题标题】:Multiple Authentication providers - LDAP JWT Oauth2 and Basic Authentication in Spring Boot Application多个身份验证提供程序 - Spring Boot 应用程序中的 LDAP JWT Oauth2 和基本身份验证
【发布时间】:2020-07-19 12:36:12
【问题描述】:

我有一个 Spring Boot 2.0 应用程序,用作我的 Angular 应用程序的后端(提供休息服务)。

我们正在为登录进行 LDAP 身份验证。它工作得很好。我们已经使用 spring-oauth-2 和 spring-ldap 为其实现了自定义代码。

现在,我们已经集成了执行器。我希望我的执行器端点可以通过基本身份验证在浏览器中访问。

为此,我使用 @Order(1) 添加了 WebSecurityConfigurerAdapter 的实现。它在浏览器中运行良好。但是,当我从我的 angualar 应用程序调用登录 url 时,它给了我 401/oauth/token url 未经授权的错误,因此我无法从 ui 应用程序登录。

如果您能帮助解决此错误,我们将不胜感激。

类:

    @Configuration
    @Order(1)
    public class FormLoginWebSecurityConfigurerAdapter extends 
          WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser("actuator") 
     .password(passwordEncoder().encode("actuator")).roles("ACTUATOR_ADMIN");
  }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
    http.antMatcher("/actuator/**").authorizeRequests().anyRequest().hasRole("ACTUATOR_ADMIN").and()
        .httpBasic();

  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

}

谢谢。

【问题讨论】:

    标签: spring-boot spring-security-oauth2 spring-boot-actuator jwt-auth


    【解决方案1】:

    所以我自己遇到了这个问题(OAuth + 基本身份验证),最终选择了一条不同的路径,该路径对基本身份验证部分涉及更多,但最终它奏效了。

    首先你需要实现你自己的Authentication对象:

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    
    import java.util.Collection;
    import java.util.Collections;
    
    public class ActuatorAuthentication implements Authentication {
    
        private static final Object DUMMY = new Object();
        private static final String NAME = "Actuator Authenticated";
        private static final boolean AUTHENTICATED = true;
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return Collections.emptyList();
        }
    
        @Override
        public Object getCredentials() {
            return DUMMY;
        }
    
        @Override
        public Object getDetails() {
            return DUMMY;
        }
    
        @Override
        public Object getPrincipal() {
            return DUMMY;
        }
    
        @Override
        public boolean isAuthenticated() {
            return authenticated;
        }
    
        @Override
        public void setAuthenticated(boolean b) throws IllegalArgumentException {
            throw new UnsupportedOperationException("Operation setAuthenticated not supported.");
        }
    
        @Override
        public String getName() {
            return NAME;
        }
    }
    

    我们将使用它与AutoConfiguredHealthEndpointGroup.isAuthorized() 方法一起使用,以便在我们配置以下内容时切换我们想要的行为:

    management:
      endpoint:
        health:
          show-details: when_authorized
    

    因此,我们将使用它来实现我们自己的OncePerRequestFilter,它将对除/health/health/liveness/health/readiness 之外的所有执行器端点执行实际的基本身份验证。

    如果未提供基本身份验证,我们还将确保仅 /health 将返回基本 UPDOWN

    这个代码最终是:

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.util.AntPathMatcher;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    
    public class ActuatorSecurityFilter extends OncePerRequestFilter {
    
        private static final String[] EXCLUDED_ENDPOINTS = {
                "/actuator/health/liveness",
                "/actuator/health/readiness",
                "/actuator/info"
        };
    
        private AntPathMatcher pathMatcher = new AntPathMatcher();
    
        private final String username;
        private final String password;
        
        public ActuatorSecurityFilter(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
            boolean authorized = false;
    
            String authorization = request.getHeader("Authorization");
            if (authorization != null) {
                // Authorization: Basic base64credentials
                String base64Credentials = authorization.substring("Basic".length()).trim();
                byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
                String credentials = new String(credDecoded, StandardCharsets.UTF_8);
                // credentials = username:password
                final String[] values = credentials.split(":", 2);
    
                if (values.length == 2 && username.equals(values[0]) && password.equals(values[1])) {
                    authorized = true;
                }
            }
    
            // Handle specific non-auth vs. auth for actuator health
            if (pathMatcher.match("/actuator/health", request.getRequestURI())) {
                if (authorized) {
                    // Utilize Dummy Authentication to show full details
                    Authentication authentication = new ActuatorAuthentication();
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                } else {
                    // Ensure no authentication is provided to show basic details if auth is incorrect
                    SecurityContextHolder.getContext().setAuthentication(null);
                }
            } else if (!authorized) {
                response.sendError(401);
                return;
            }
    
            chain.doFilter(request, response);
        }
    
        @Override
        protected boolean shouldNotFilter(HttpServletRequest request) {
            for (String endpoint : EXCLUDED_ENDPOINTS) {
                if (pathMatcher.match(endpoint, request.getRequestURI())) {
                    return true;
                }
            }
            return false;
        }
    
    }
    

    最后我们想把它和我们自己的配置类连接起来:

    import com.cantire.sps.application.filter.ActuatorSecurityFilter;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ActuatorSecurityConfiguration {
    
        @Value("${actuator.username}")
        private String username;
    
        @Value("${actuator.password}")
        private String password;
    
        @Bean
        FilterRegistrationBean<ActuatorSecurityFilter> securityFilterRegistration() {
            FilterRegistrationBean<ActuatorSecurityFilter> registration = new FilterRegistrationBean<>();
            registration.setFilter(new ActuatorSecurityFilter(username, password));
            registration.addUrlPatterns("/actuator/*");
            return registration;
        }
    }
    

    然后我们只需将其添加到您的应用程序属性中:

    actuator:
      username: monitor
      password: password
    

    瞧,无论您实施了什么 WebSecurityConfigurerAdapter,您都可以为您的 Actuator 端点使用基本身份验证。

    【讨论】:

    • 谢谢@welsh,我去看看。
    猜你喜欢
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 2017-11-28
    • 2021-02-28
    • 2012-02-18
    • 2015-04-10
    • 2019-10-15
    • 1970-01-01
    相关资源
    最近更新 更多