Recently I have integrated Spring Boot Admin in my application. Everything fine, until I've stared adding security (nothing complicated, just Basic Auth). When I try to login in Spring Boot Admin UI, it redirects me back to the login page with "Login required to access the resource (Error: 401).", or to "variables.css". I am using Spring Boot 3.0.0 with Spring Boot Admin version 3.0.0-M6.
I have to mention that everything works alright if I disable spring security.
Security Config class looks like this:
#Configuration(proxyBeanMethods = false)
#EnableWebSecurity
#AllArgsConstructor
public class AdminSecurityConfig {
private final AdminServerProperties adminServerProperties;
private final SecurityProperties securityProperties;
private final AuthenticationConfiguration authenticationConfiguration;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServerProperties.path("/"));
http
.authorizeHttpRequests(
(authorizeRequests) ->
authorizeRequests.requestMatchers(this.adminServerProperties.path("/assets/**")).permitAll()
.requestMatchers(this.adminServerProperties.path("/*.css")).permitAll()
.requestMatchers(this.adminServerProperties.path("/actuator/info")).permitAll()
.requestMatchers(this.adminServerProperties.path("/actuator/health")).permitAll()
.requestMatchers(this.adminServerProperties.path("/login")).permitAll()
.anyRequest().authenticated()
).formLogin(
(formLogin) -> formLogin.loginPage(this.adminServerProperties.path("/login")).successHandler(successHandler).and()
).logout((logout) -> logout.logoutUrl(this.adminServerProperties.path("/logout")))
.httpBasic(Customizer.withDefaults())
.csrf(
(csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
new AntPathRequestMatcher(this.adminServerProperties.path("/instances"),
HttpMethod.POST.name()),
new AntPathRequestMatcher(this.adminServerProperties.path("/instances/*"),
HttpMethod.DELETE.name()),
new AntPathRequestMatcher(this.adminServerProperties.path("/actuator/**"))
));
return http.build();
}
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.withUsername(securityProperties.getUser().getName())
.password("{noop}" + securityProperties.getUser().getPassword())
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
also, I have added this in the application.yaml file
spring:
security:
user:
name: ****
password: ****
roles:
- USER
boot:
admin:
monitor:
status-interval: 30000
status-lifetime: 30000
ui:
title: "Invoice Matching Admin"
remember-me-enabled: false
Main class looks like this:
#EnableAdminServer
#SpringBootApplication
public class InvoiceAdminServiceApplication {
public static void main(String[] args) {
SpringApplication.run(InvoiceAdminServiceApplication.class, args);
}
}
pom.xml contains this dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
In case that somebody will encounter the same problem, I managed to find the solution.
In the filterChain method you should add this:
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll()
Here is a documentation (https://codecentric.github.io/spring-boot-admin/3.0.0-M7/security.html#securing-spring-boot-admin) for Spring Admin security.
Related
Im new to Spring Boot and was trying to implement basic Spring Security for a single endpoint in my Spring Boot controller. But I dont know how to resolve the Circular View Error
My Controller
#Controller
#RequestMapping("/api")
public class HelloSecurityController {
#RequestMapping({"/hello"})
public static String helloWorld() {
return "hello";
}
My Security Configurer Class
#EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService myUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
```
My MyUserDetailsService which returns a simple User with Password
#Service
public class MyUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
return new User("foo","foo", new ArrayList<>());
The Dependencies I kept in maven pom file during initializing Spring Boot Project
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The Package structure of my project:
proj_struct
After logging in using the username and password at the login form page generated by spring security Im getting the error:
javax.servlet.ServletException: Circular view path [hello]: would dispatch back to the current handler URL [/api/hello] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
I have not kept any static template (HTML/JSP) in the templates folder. I dont know if I have to as after login I just want to see a simple String. How do I resolve this?
I am writing a simple demo application using hazelcast session replication based on the shared security example
mentioned in here https://github.com/peholmst/vaadin4spring.
The problem is that if i am starting the application it stays on the login site with "communication error".
With vaadin debug on, it shows me "Response didn't contain a server id. Please verify that the server is up-to-date and that the response data has not been modified in transmission."
In the IDE no stack trace is shown.
I tried also the working example here https://vaadin.com/blog/microservices-high-availability, but it didnĀ“t give me a hint how to integrate hazelcast with vaadin4spring.
Here is my configuration:
public void configure(AuthenticationManagerBuilder auth) throws Exception {
User.UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication().withUser("user").password("{noop}user").roles("USER")
.and()
.withUser("admin").password("{noop}admin").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // Use Vaadin's built-in CSRF protection instead
http.authorizeRequests().antMatchers("/login/**").anonymous()
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll().anyRequest().authenticated();
http.httpBasic().disable();
http.formLogin().disable();
// Remember to add the VaadinSessionClosingLogoutHandler
http.logout().addLogoutHandler(new VaadinSessionClosingLogoutHandler()).logoutUrl("/logout")
.logoutSuccessUrl("/login?logout").permitAll();
http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
// Instruct Spring Security to use the same RememberMeServices as Vaadin4Spring. Also remember the key.
//http.rememberMe().rememberMeServices(rememberMeServices()).key("myAppKey");
// Instruct Spring Security to use the same authentication strategy as Vaadin4Spring
http.sessionManagement().sessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/VAADIN/**");
}
/**
* The {#link AuthenticationManager} must be available as a Spring bean for Vaadin4Spring.
*/
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* The {#link SessionAuthenticationStrategy} must be available as a Spring bean for Vaadin4Spring.
*/
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new SessionFixationProtectionStrategy();
}
#Bean(name = VaadinSharedSecurityConfiguration.VAADIN_AUTHENTICATION_SUCCESS_HANDLER_BEAN)
VaadinAuthenticationSuccessHandler vaadinAuthenticationSuccessHandler(HttpService httpService,
VaadinRedirectStrategy vaadinRedirectStrategy) {
return new VaadinUrlAuthenticationSuccessHandler(httpService, vaadinRedirectStrategy, "/");
}
#Bean
public HazelcastInstance hazelcastInstance() {
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.setProperty("hazelcast.max.no.heartbeat.seconds", "60")
.getMapConfig("spring:session:sessions")
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
config.getGroupConfig().setName("admin");
return Hazelcast.newHazelcastInstance(config);
}
pom snippet:
<java.version>1.8</java.version>
<vaadin.version>8.7.2</vaadin.version>
<spring-boot-ext.version>2.0.0.RELEASE</spring-boot-ext.version>
</properties>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-hazelcast</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.vaadin.spring.extensions</groupId>
<artifactId>vaadin-spring-ext-boot</artifactId>
<version>${spring-boot-ext.version}</version>
</dependency>
<dependency>
<groupId>org.vaadin.spring.extensions</groupId>
<artifactId>vaadin-spring-ext-security</artifactId>
<version>${spring-boot-ext.version}</version>
</dependency>
<dependency>
<groupId>org.vaadin.spring.extensions</groupId>
<artifactId>vaadin-spring-ext-core</artifactId>
<version>${spring-boot-ext.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Trying to upgrade existing resource services from Spring Boot 1.x to 2.x. Spring Security 4.5 is running on the authentication server and encodes JWT tokens like this:
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
converter.setVerifierKey(publicKey);
return converter;
}
A resource server upgraded to Spring Boot 2.1.3.RELEASE throws this error:
OAuth2AuthenticationProcessingFilter:165 :
Authentication request failed: error="invalid_token",
error_description="Invalid access token:****..."
The log reveals that the OAuth2AuthenticationProcessingFilter is using the MappingJackson2HttpMessageConverter to extract the JWT token. Spring Security auto-configuration should provide the JwtAccessTokenConverter bean instead of the MappingJackson2HttpMessageConverter bean since there is a key-value in my properties file:
security:
oauth2:
resource:
jwt:
key-value: |
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
Here is the Spring Security ResourceServerTokenServiceConfiguration class that should detect it. The property matches "security.oauth2.resource.jwt.key-value".
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth JWT Condition");
Environment environment = context.getEnvironment();
String keyValue = environment
.getProperty("security.oauth2.resource.jwt.key-value");
String keyUri = environment
.getProperty("security.oauth2.resource.jwt.key-uri");
if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) {
return ConditionOutcome
.match(message.foundExactly("provided public key"));
}
return ConditionOutcome
.noMatch(message.didNotFind("provided public key").atAll());
}
These are the resource server security dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
This is the resource server configuration. It is essentially the same as it was with Spring Boot 1.5.x.
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my-service";
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
}
What am I missing?
The problem was an issue with the properties. I had moved "security.oauth2" to "spring.security.oauth2." When I turned on logging for org.springframework.boot.autoconfigure.security.oauth2 and saw this:
did not match due to OAuth Client ID did not find security.oauth2.client.client-id property
So, I decided to try moving the oauth2 properties back out from under "spring." and it was able to extract the JWT token.
I have tried security configuration for spring webFlux as described in documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc-webflux.html#explicit-webflux-security-configuration
Security configuration ignores userDetailsServiceBean - i cannot login with user:pass, but may login with credentials from autoconfigurated
UserDetailsServiceAutoConfiguration:
2019-04-22 11:29:24.571 INFO 12343 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: ff00a80a-d810-43d6-9c89-e861eb1bed96
My pom.xml (fragment):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
My security config:
#EnableWebFluxSecurity
#EnableWebSecurity
#Configuration
public class WebfluxSecurityConfig {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange().authenticated()
.and()
.httpBasic().and()
.formLogin();
return http.build();
}
#Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("pass")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user);
}
}
Why security config ignores userDetailsService ?
Is there error in spring-security documentation?
Remove the #EnableWebSecurity annotation. It's used for Servlet applications and isn't applied on WebFlux.
You might also consider defining a bean of type UserDetailsRepositoryReactiveAuthenticationManager in your WebFlux Configuration; such as the following:
#Bean
public ReactiveAuthenticationManager authenticationManager(ReactiveUserDetailsService detailsService) {
return new UserDetailsRepositoryReactiveAuthenticationManager(detailsService);
}
In your case, most probably, the #EnableWebSecurity annotation configures a bean of type InMemoryUserDetailsManager which is the non-reactive variant of ReactiveUserDetailsManager.
NOTE: You can remove the following from your POM if you're planning to use WebFlux only: spring-boot-starter-tomcat and spring-boot-starter-web
I want to implement LDAP authentication in my Spring Boot app. I have configuration class as below:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//#Value("${ad.domain}")
private String AD_DOMAIN;
//#Value("${ad.url}")
private String AD_URL;
WebSecurityConfig() {
AD_DOMAIN = "domain.com";
AD_URL = "ldap://URL";
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin().permitAll().and().logout().permitAll();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, AD_URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
controller:
#Controller
public class HomeController {
#GetMapping("/")
public String index() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String user = auth.getName();
return "Welcome to the home page "+ user;
}
And my pom file have dependencies as :
<dependencies>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Here all is working fine I am getting SpringBoot's default login page and Authentication is also working.
But now I want a Custom login Page for that in my WebSecurityConfig class I have done the following :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();
}
where /login is my custom login page. But this is not working. I think that the AuthenticationManager is bind to the Spring's default login Page.
Can Someone suggest me, it will be helpful. Thanks