Spring Boot Admin - Basic Auth - java

I'm setting up basic auth in my sb-admin and client, but the client can't register (401 unauthorized). Everything works without auth.
SB-Admin configuration:
application.properties
server.port=8080
spring.application.name=SB Admin
spring.boot.admin.ui.title=SB Admin
spring.security.user.name=admin
spring.security.user.password=admin
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'de.codecentric:spring-boot-admin-starter-server'
Client configuration:
application.properties
server.port=9000
management.endpoints.web.exposure.include=*
management.security.enabled=false
spring.boot.admin.client.enabled=true
spring.boot.admin.client.url=http://localhost:8080
spring.boot.admin.client.username=admin
spring.boot.admin.client.password=admin
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'de.codecentric:spring-boot-admin-starter-client'
Security Config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
private final AdminServerProperties adminServer;
public SecurityConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
this.adminServer = adminServerProperties;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeRequests((authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**"))
.permitAll().antMatchers(this.adminServer.path("/login")).permitAll().anyRequest().authenticated())
.formLogin((formLogin) -> formLogin.loginPage(this.adminServer.path("/login"))
.successHandler(successHandler).and())
.logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
.httpBasic(Customizer.withDefaults())
.csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
new AntPathRequestMatcher(this.adminServer.path("/instances"),
HttpMethod.POST.toString()),
new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
HttpMethod.DELETE.toString()),
new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))))
.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
}
}
Does anybody can help me please?

It's not enough just to add the spring security starter. You have to add a configuration class annotated with #EnableWebSecurity. Typically it'd be something like the following class, where you could configure things that are related to security of your app.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}

Make sure you are adding these lines in client properties file.
these credential will be submitted by admin server at the time of registration
spring.boot.admin.client.instance.metadata.user.name=client_username
spring.boot.admin.client.instance.metadata.user.password=client_password

#Marcos Vidolin All of your code snippets are correct, simply update configure method body in SecurityConfig with:
auth
.inMemoryAuthentication()
.withUser("admin")
.password("{noop}admin")
.roles("ADMIN");
It will fix the login error.

Related

Spring Boot Keycloak Adapter cannot authorize requests to context url "/"

I have a simple Spring Boot (2.4.5-SNAPSHOT) web project using keycloak-spring-boot-starter (12.0.4) adapter for Keycloak integration. I can secure all the endpoints except the context/base url of the application. The requests coming to this base url are not authenticated. Do I make a mistake in the configure method?
http://localhost:3000/greetings is secure, redirects to Keycloak login. But http://localhost:3000 is insecure.
HelloController.java
#RestController
public class HelloController {
#GetMapping("/greetings")
public ResponseEntity<String> getGreetings() {
return ResponseEntity.ok("Hello world!");
}
#GetMapping("/")
public ResponseEntity<String> getContextGreetings() {
return ResponseEntity.ok("Hello world context!");
}
}
KeycloakSecurityConfig.java
#Configuration
#EnableWebSecurity
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable()
.authorizeRequests().antMatchers("/**").authenticated();
//.authorizeRequests(authorize -> authorize.anyRequest().authenticated());
// Also tried the commented one, doesn't work either.
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Bean
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
application.properties
server.port=3000
keycloak.realm=myrealm
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=external
keycloak.resource=my-client
keycloak.credentials.secret=b5c3154c-012b-4ce2-af14-d58505a2a54d
keycloak.use-resource-role-mappings=true
build.gradle
...
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
implementation 'org.keycloak:keycloak-spring-boot-starter:12.0.4'
implementation 'org.keycloak.bom:keycloak-adapter-bom:12.0.4'
implementation 'com.fasterxml.jackson.core:jackson-core:2.12.2'
}
Found the reason thanks to the following SO answer.
In the configure method, super.configure(http); is being called. The parent configure method makes the logout url insecure which makes sense since it's the redirection page after the logout. In order overcome this issue, the logoutSuccessUrl needs to be set to another url.
See the last line of the method from KeycloakWebSecurityConfigurerAdapter:
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().requireCsrfProtectionMatcher(keycloakCsrfRequestMatcher())
.and()
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), LogoutFilter.class)
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
.addFilterAfter(keycloakAuthenticatedActionsRequestFilter(), KeycloakSecurityContextRequestFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/"); //permits the path
}

Spring Boot secure actuator endpoint with basic auth while securing other endpoints with Oath [duplicate]

I am trying to set up multiple WebsecurityConfigurerAdapter for my project where the spring boot actuator APIs are secured using basic auth and all other endpoints are authenticated using JWtAuthentication. I am just not able to make it work together, only the config with the lower order works. I am using Spring Boot 2.1.5.RELEASE
Security Config One with JWT Authenticator
#Order(1)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
"/docs/**",
"/csrf/**",
"/webjars/**",
"/**swagger**/**",
"/swagger-resources",
"/swagger-resources/**",
"/v2/api-docs"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.antMatchers("/abc/**", "/abc/pdf/**").hasAuthority("ABC")
.antMatchers("/ddd/**").hasAuthority("DDD")
.and()
.csrf().disable()
.oauth2ResourceServer().jwt().jwtAuthenticationConverter(new GrantedAuthoritiesExtractor());
}
}
The basic Auth config with username/password
#Order(2)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
/* #Bean
public UserDetailsService userDetailsService(final PasswordEncoder encoder) {
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
User
.withUsername("user1")
.password(encoder.encode("password"))
.roles("ADMIN")
.build()
);
return manager;
}
#Bean PasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").hasRole("ADMIN")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password").authorities("ADMIN");
}
}
I have been trying to make it work for many days but cannot make both of them work together. If i swap the order, only basic auth works and not the JWT Auth Manager.
I have gone through a lot of SOF Questions, like
[https://stackoverflow.com/questions/40743780/spring-boot-security-multiple-websecurityconfigureradapter][1]
[https://stackoverflow.com/questions/52606720/issue-with-having-multiple-websecurityconfigureradapter-in-spring-boot][1]
[https://github.com/spring-projects/spring-security/issues/5593][1]
[https://www.baeldung.com/spring-security-multiple-entry-points][1]
Nothing seems to be working, is this a known issue in Spring?
To use multiple WebsecurityConfigurerAdapter, you need restrict them to specific URL patterns using RequestMatcher.
In your case you can set a higher priority for ActuatorSecurityConfig and limit it only to actuator endpoints:
#Order(-1)
#Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/actuator/**")
.and()
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}

Spring Boot Admin 2.0.0: Client cannot Connect to Secured Server

Spring Boot Admin works fine without the spring-boot-starter-security dependency. As soon as I include it, no matter how I configure the security (by application.yml or WebSecurityConfigurerAdapter) everything seems beeing weird.
The result I´m aiming for is that (1) the Spring Boot Admin Server is secured via a login (HTTP Basic Auth for example) and (2) the Client can send data to the secured server.
Here's the configuration I expect to work:
Client: application.yml
spring:
boot:
admin:
client:
url:
- "http://localhost:8090"
instance:
metadata:
user.name: ${myApp.security.usernameAdmin}
user.password: ${myApp.security.passwordAdmin}
username: admin
password: adminPwd
Admin-Server: build.gradle
dependencies {
compile "de.codecentric:spring-boot-admin-starter-server:2.0.0"
compile "org.springframework.boot:spring-boot-starter-security"
}
1st try to make things work
Admin-Server: application.yml
server:
port: 8090
spring:
security:
user:
name: admin
password: adminPwd
logging:
level:
org.springframework.security: DEBUG
result: client can't connect
2nd try to make things work
Admin-Server: WebSecurityConfigurerAdapter
#Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
result: client can't connect
3rd try to make things work
Admin-Server: WebSecurityConfigurerAdapter
#Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private static final String ADMIN_ROLE = "ADMIN";
private static final String ADMIN_PASSWORD = "adminPwd";
private static final String ADMIN_USER_NAME = "admin";
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
adminContextPath = adminServerProperties.getContextPath();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
http.authorizeRequests()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().hasRole(ADMIN_ROLE)
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf().disable();
// #formatter:on
}
#Override
#Autowired
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(ADMIN_USER_NAME)
.password(ADMIN_PASSWORD)
.roles(ADMIN_ROLE);
;
}
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
result: client cannot connect...

Redirect using spring ldap login page causes extra url path which is incorrect

If I am using spring security to connect to ldap when I connect lets say to the url: www.serverAdress/myapp/
I should be redirected to
www.serverAdress/myapp/login
but instead I am redirected to
www.serverAddress/myappmyapp/login
I am not sure how the extra myapp is added it is also obtained if a redirect is used within the return of the views.
Edit added the configuration
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider("domain", "ldap://url", "rootDN custom");
authenticationProvider.setSearchFilter("customfilter");
authenticationProvider.setConvertSubErrorCodesToExceptions(true);
authenticationProvider.setUseAuthenticationRequestCredentials(true);
return authenticationProvider;
}
}

Spring Security java.lang.StackOverflowError exception after all providers

Environment:
Spring 4.1.6
Spring Security 4.0.1
I have 2 authentication providers - one that hits ActiveDirectory, and then one that hits a custom database provider I've created. Logging in as a user that is in either of those environments works perfectly. The user is authenticated and the app continues.
However, when an invalid user is entered and neither provider is able to authenticate, I get this exception on the page:
java.lang.StackOverflowError
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:393)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:394)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:394)
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:394)
Here is my WebSecurityConfigurerAdapter configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin().loginPage("/login").failureUrl("/login?error").defaultSuccessUrl("/overview").permitAll()
.and()
.logout().logoutSuccessUrl("/login?logout").permitAll()
.and()
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/favicon.ico").permitAll()
.antMatchers("/**").hasRole("AUTH");
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
.userDetailsService(userDetailsService());
authManagerBuilder
.authenticationProvider(databaseAuthenticationProvider())
.userDetailsService(userDetailsService());
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
UserDetailsContextMapper contextMapper = new MyUserDetailsContextMapper();
return contextMapper;
}
#Bean
public MyDatabaseAuthenticationProvider databaseAuthenticationProvider() {
return new MyDatabaseAuthenticationProvider();
}
There's really nothing special in the "MyDatabaseAuthenticationProvider" or "MyUserDetailsContextMapper" classes except for some custom logic for mapping and looking up users.
The app doesn't crash, but obviously not the page I want to show the user. :)
Any thoughts on how I can get rid of the StackOverflowError?
I had the same problem, this was the solution for me:
#Override
protected void configure(AuthenticationManagerBuilder
authManagerBuilder) throws Exception {
...
.userDetailsService(userDetailsService());
...
}
The problem where the brackets after userDetailsService - removed them and it works as expected.
From your code snippet I can't be sure where you get the userDetailsService from, for me I had it #Autowired.
I had the same problem and another solution worked in my case. The difference is, that I had simply username and password, database authentication.
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService();
}
The fix was to add the #Override annotation to the #Bean annotated method:
#Bean
#Override
UserDetailsService userDetailsService() {
return new UserDetailsService();
}

Categories