mirror of https://github.com/apache/nifi.git
NIFI-10217 Refactored Registry Spring Security Configuration
Signed-off-by: Nathan Gough <thenatog@gmail.com> This closes #6196.
This commit is contained in:
parent
6b83bb6a6c
commit
a4ea92ea3b
|
@ -33,28 +33,24 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* NiFi Registry Web Api Spring security
|
||||
* Spring Security Filter Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
public class NiFiRegistrySecurityConfig {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NiFiRegistrySecurityConfig.class);
|
||||
|
||||
|
@ -67,28 +63,32 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
@Autowired
|
||||
private Authorizer authorizer;
|
||||
|
||||
private final AnonymousIdentityFilter anonymousAuthenticationFilter = new AnonymousIdentityFilter();
|
||||
|
||||
@Autowired
|
||||
private X509IdentityProvider x509IdentityProvider;
|
||||
private IdentityFilter x509AuthenticationFilter;
|
||||
private IdentityAuthenticationProvider x509AuthenticationProvider;
|
||||
|
||||
@Autowired
|
||||
private JwtIdentityProvider jwtIdentityProvider;
|
||||
private IdentityFilter jwtAuthenticationFilter;
|
||||
private IdentityAuthenticationProvider jwtAuthenticationProvider;
|
||||
|
||||
private ResourceAuthorizationFilter resourceAuthorizationFilter;
|
||||
|
||||
public NiFiRegistrySecurityConfig() {
|
||||
super(true); // disable defaults
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.addFilterBefore(x509AuthenticationFilter(), AnonymousAuthenticationFilter.class)
|
||||
.addFilterBefore(jwtAuthenticationFilter(), AnonymousAuthenticationFilter.class)
|
||||
// Add Resource Authorization after Spring Security but before Jersey Resources
|
||||
.addFilterAfter(resourceAuthorizationFilter(), FilterSecurityInterceptor.class)
|
||||
.anonymous().authenticationFilter(new AnonymousIdentityFilter()).and()
|
||||
.csrf().disable()
|
||||
.logout().disable()
|
||||
.rememberMe().disable()
|
||||
.requestCache().disable()
|
||||
.servletApi().disable()
|
||||
.securityContext().disable()
|
||||
.sessionManagement().disable()
|
||||
.headers()
|
||||
.xssProtection().and()
|
||||
.contentSecurityPolicy("frame-ancestors 'self'").and()
|
||||
.httpStrictTransportSecurity().maxAgeInSeconds(31540000).and()
|
||||
.frameOptions().sameOrigin().and()
|
||||
.authorizeRequests()
|
||||
.antMatchers(
|
||||
"/access/token",
|
||||
|
@ -101,116 +101,55 @@ public class NiFiRegistrySecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
.anyRequest().fullyAuthenticated()
|
||||
.and()
|
||||
.exceptionHandling()
|
||||
.authenticationEntryPoint(http401AuthenticationEntryPoint())
|
||||
.and()
|
||||
.sessionManagement()
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
||||
|
||||
// Apply security headers for registry API. Security headers for docs and UI are applied with Jetty filters in registry-core.
|
||||
http.headers().xssProtection();
|
||||
http.headers().contentSecurityPolicy("frame-ancestors 'self'");
|
||||
http.headers().httpStrictTransportSecurity().maxAgeInSeconds(31540000);
|
||||
http.headers().frameOptions().sameOrigin();
|
||||
|
||||
// x509
|
||||
http.addFilterBefore(x509AuthenticationFilter(), AnonymousAuthenticationFilter.class);
|
||||
|
||||
// jwt
|
||||
http.addFilterBefore(jwtAuthenticationFilter(), AnonymousAuthenticationFilter.class);
|
||||
|
||||
// otp
|
||||
// todo, if needed one-time password auth filter goes here
|
||||
|
||||
// add an anonymous authentication filter that will populate the authenticated,
|
||||
// anonymous user if no other user identity is detected earlier in the Spring filter chain
|
||||
http.anonymous().authenticationFilter(anonymousAuthenticationFilter);
|
||||
|
||||
// After Spring Security filter chain is complete (so authentication is done),
|
||||
// but before the Jersey application endpoints get the request,
|
||||
// insert the ResourceAuthorizationFilter to do its authorization checks
|
||||
http.addFilterAfter(resourceAuthorizationFilter(), FilterSecurityInterceptor.class);
|
||||
.authenticationEntryPoint(http401AuthenticationEntryPoint()).and()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth
|
||||
.authenticationProvider(x509AuthenticationProvider())
|
||||
.authenticationProvider(jwtAuthenticationProvider());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide Authentication Manager Bean to disable unnecessary UserDetailsServiceAutoConfiguration
|
||||
* @return Authentication Manager
|
||||
* @throws Exception Thrown when failing to initialize Authentication Manager
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
public AuthenticationManager authenticationManager() {
|
||||
return new ProviderManager(x509AuthenticationProvider(), jwtAuthenticationProvider());
|
||||
}
|
||||
|
||||
private IdentityFilter x509AuthenticationFilter() {
|
||||
if (x509AuthenticationFilter == null) {
|
||||
x509AuthenticationFilter = new IdentityFilter(x509IdentityProvider);
|
||||
}
|
||||
return x509AuthenticationFilter;
|
||||
return new IdentityFilter(x509IdentityProvider);
|
||||
}
|
||||
|
||||
private IdentityAuthenticationProvider x509AuthenticationProvider() {
|
||||
if (x509AuthenticationProvider == null) {
|
||||
x509AuthenticationProvider = new X509IdentityAuthenticationProvider(authorizer, x509IdentityProvider, identityMapper);
|
||||
}
|
||||
return x509AuthenticationProvider;
|
||||
return new X509IdentityAuthenticationProvider(authorizer, x509IdentityProvider, identityMapper);
|
||||
}
|
||||
|
||||
private IdentityFilter jwtAuthenticationFilter() {
|
||||
if (jwtAuthenticationFilter == null) {
|
||||
jwtAuthenticationFilter = new IdentityFilter(jwtIdentityProvider);
|
||||
}
|
||||
return jwtAuthenticationFilter;
|
||||
return new IdentityFilter(jwtIdentityProvider);
|
||||
}
|
||||
|
||||
private IdentityAuthenticationProvider jwtAuthenticationProvider() {
|
||||
if (jwtAuthenticationProvider == null) {
|
||||
jwtAuthenticationProvider = new IdentityAuthenticationProvider(authorizer, jwtIdentityProvider, identityMapper);
|
||||
}
|
||||
return jwtAuthenticationProvider;
|
||||
return new IdentityAuthenticationProvider(authorizer, jwtIdentityProvider, identityMapper);
|
||||
}
|
||||
|
||||
private ResourceAuthorizationFilter resourceAuthorizationFilter() {
|
||||
if (resourceAuthorizationFilter == null) {
|
||||
resourceAuthorizationFilter = ResourceAuthorizationFilter.builder()
|
||||
return ResourceAuthorizationFilter.builder()
|
||||
.setAuthorizationService(authorizationService)
|
||||
.addResourceType(ResourceType.Actuator)
|
||||
.addResourceType(ResourceType.Swagger)
|
||||
.build();
|
||||
}
|
||||
return resourceAuthorizationFilter;
|
||||
}
|
||||
|
||||
private AuthenticationEntryPoint http401AuthenticationEntryPoint() {
|
||||
// This gets used for both secured and unsecured configurations. It will be called by Spring Security if a request makes it through the filter chain without being authenticated.
|
||||
// For unsecured, this should never be reached because the custom AnonymousAuthenticationFilter should always populate a fully-authenticated anonymous user
|
||||
// For secured, this will cause attempt to access any API endpoint (except those explicitly ignored) without providing credentials to return a 401 Unauthorized challenge
|
||||
return new AuthenticationEntryPoint() {
|
||||
@Override
|
||||
public void commence(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
AuthenticationException authenticationException)
|
||||
throws IOException {
|
||||
return (request, response, authenticationException) -> {
|
||||
|
||||
// return a 401 response
|
||||
final int status = HttpServletResponse.SC_UNAUTHORIZED;
|
||||
logger.info("Client could not be authenticated due to: {} Returning 401 response.", authenticationException.toString());
|
||||
logger.debug("", authenticationException);
|
||||
// return a 401 response
|
||||
final int status = HttpServletResponse.SC_UNAUTHORIZED;
|
||||
logger.info("Client could not be authenticated due to: {} Returning 401 response.", authenticationException.toString());
|
||||
logger.debug("", authenticationException);
|
||||
|
||||
if (!response.isCommitted()) {
|
||||
response.setStatus(status);
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().println(String.format("%s Contact the system administrator.", authenticationException.getLocalizedMessage()));
|
||||
}
|
||||
if (!response.isCommitted()) {
|
||||
response.setStatus(status);
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().println(String.format("%s Contact the system administrator.", authenticationException.getLocalizedMessage()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue