多个身份验证提供程序-Spring Boot应用程序中的LDAP JWT Oauth2和基本身份验证

时间:2020-04-07 10:32:31

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

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

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

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

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

}

谢谢。

1 个答案:

答案 0 :(得分:0)

所以我自己遇到了这个问题(OAuth + Basic Auth),最终选择了一条不同的路径,该路径对于Basic Auth而言涉及更多,但最终还是有效。

首先,您需要实现自己的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;
    }

}

最后,我们希望将其与我们自己的Configuration类连接在一起

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

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