I am using Spring Boot 1.2.0 with embedded h2database.
The database is initialized using schema.sql file in the classpath.
It worked fine until I added #EnableGlobalMethodSecurity annotation to the WebSecurityConfiguration class.
java.lang.IllegalStateException: ApplicationEventMulticaster not initialized exception is thrown from DataSourceInitializer.runSchemaScripts
What may be the problem ?
Here is the code:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#EnableGlobalMethodSecurity(securedEnabled = true)
#Configuration
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
I think the solution is to not use the init method to setup JDBC authentication. Try with a separate class that extends GlobalAuthenticationConfigurerAdapter and override the config method.
Related
Instead of using InMemory Auth I am trying to authenticate with BD user, so a created class user and roles, and added this code to this class
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetails customUserDetails;
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// Set default security policy that permits Vaadin internal requests and
// denies all other
super.configure(http);
setLoginView(http, LoginView.class, "/logout");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication().withUser("user").password("{noop}userpass").roles("USER");
auth.userDetailsService(customUserDetails).passwordEncoder(passwordEncoder());
}
#Bean
public DaoAuthenticationProvider createDaoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(customUserDetails);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
}
But when i try to authenticate it does not accept
There is more than one way to do this, but usually it goes roughly like this. The relevant pieces, the complete solution is too lengthy for SO. There should not be anything Vaadin specific, but just follow Spring documentation on the matter.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter {
...
private final UserDetailsService userDetailsService;
#Autowired
public SecurityConfig (UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
...
}
And then implement UserDetailsService according to Spring spec.
#Service
#Primary
public class UserDetailsServiceImpl implements UserDetailsService {
...
#Autowired
private UserRepository repository; // or what ever you have named it ...
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// implement this according to your actual implementation of user database
}
}
Prior to Spring Security 5.7 it was possible to add additional AuthenticationProviders to the global AuthenticationManager this way:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
With Spring Security 5.7 the WebSecurityConfigurerAdapter was deprecated.
Question: ho should i migrate this code to solve the deprecation?
When i try to register the additional AuthenticationProvider as #Bean, the autocreated authentication provider for username/password based authentication gets replaced, leading to
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
I read the blog post https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter but found no hints about adding additional authentication providers to the global AuthenticationManager.
If you have a single AuthenticationProvider you can register it as a bean and it will be picked up by Spring Security:
#Bean
public CustomAuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
Alternatively, you can add additional AuthenticationProviders in the HttpSecurity configuration:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.authenticationProvider(new CustomAuthenticationProvider());
return http.build();
}
You can annotate your configuration class with #EnableGlobalAuthentication and will be able to configure a global instance of AuthenticationManagerBuilder:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
Please see the related documentation: https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/authentication/configuration/EnableGlobalAuthentication.html
I had a same problem when I want to add an custom AuthenticationProvider using Spring Security without WebSecurityConfigurerAdapter.
Here is what I did.
Code with WebSecurityConfigurerAdapter
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
Code without WebSecurityConfigurerAdapter
#EnableWebSecurity
#EnableGlobalAuthentication
public class SecurityConfiguration {
...
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
void registerProvider(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
}
Note: #EnableGlobalAuthentication and registerProvider().
Hope this will help.
I had a similar problem. I have a custom user details service and I also use an additional custom authentication provider. One is for actual users and the custom provider is for automated devices.
This is my code with the WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Autowired
private MyCustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private UserDetailsService userDetailsService;
...
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
var encoder = passwordEncoder();
customAuthenticationProvider.encoder(encoder);
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.authenticationProvider(customAuthenticationProvider);
}
...
}
This is my code without the WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
...
#Autowired
private MyCustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private UserDetailsService userDetailsService;
...
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
var encoder = passwordEncoder();
customAuthenticationProvider.encoder(encoder);
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.authenticationProvider(customAuthenticationProvider);
}
...
}
note: you might need to set spring.main.allow-circular-references to true in your properties file for this to work.
I am setting up an OAuth2 + OpenID connect server using Spring security. I have been trying to use the automatic /oauth/token & /oauth/authorize endpoints that are defined when you use the #EnableAuthorizationServer annotation on a class.
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter{
In the same class, I have autowired an AuthenticationManager to use in configuring the AuthorizationServerEndpointsConfigurer. I have debugged & confirmed that the correct bean is being autowired.
#Autowired
private AuthenticationManager authMan;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception{
endpoints
.tokenStore(tokenStore())
.userApprovalHandler(userApprovalHandler())
.authenticationManager(authMan);
}
The problem is, there are two WebSecurityConfigurers being created, the one I defined and what appears to be the default WebSecurityConfigurer. Here is part of the one I defined:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
private UserDetailsSrvc detailsSrvc;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(detailsSrvc);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
#Bean(name="myAuthenticationManager")
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
Unfortunately, the default is being called when I navigate to localhost:8080/outh/token with my browser. I can tell because my custom UserDetailsService is not being used during the authentication, and because I put a breakpoint on the getWebSecurityConfigurers method in org.springframework.security.config.annotation.web.configuration.AutowiredWebSecurityConfigurersIgnoreParents:
#SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
The beansOfType map has two entries, but only if I have a class with the #EnableAuthorizationServer annotation. (Only 1 if I comment out annotation)
How do I get my AuthorizationServerConfigurerAdapter (or whatever is actually processing the requests to /oauth/token) to use the WebSecurityConfigurer defined in my WebSecurityConfigurerAdapter? I believe I can get around this issue by defining my own endpoints, and maybe that's the only solution, but I was hoping to utilize the default endpoints.
I have a spring-boot web application that declares some security through this class:
#Configuration
#EnableWebSecurity
#Order(Ordered.LOWEST_PRECEDENCE - 50) // needs to be after SpringBootAuthenticationConfigurerAdapter to register default in memory user
public class StorefrontSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER - 1)
#Configuration
public static class MyStorefrontSecurityConfig extends WebSecurityConfigurerAdapter {
.....
}
and it all works fine. I also add these annotations to some of my service methods:
#PreAuthorize("hasPermission(#entity, 'APPROVE') or hasPermission(#entity, 'ADMINISTRATION') or hasRole('ROLE_ADMINGROUP')")
void approve(final EntityModificationEntityDefinition entity);
#PreAuthorize("hasPermission(#entity, 'APPROVE') or hasPermission(#entity, 'ADMINISTRATION') or hasRole('ROLE_ADMINGROUP')")
void reject(final EntityModificationEntityDefinition entity);
and for now they don't do much - which is perfectly fine. But now I create jar file with the following configuration:
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class PersonalizationConfig extends GlobalMethodSecurityConfiguration {
private final Logger LOG = LogManager.getLogger(getClass());
/* Global Method Security */
#Override
public AccessDecisionManager accessDecisionManager() {
final List<AccessDecisionVoter<? extends Object>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(new RoleVoter());
accessDecisionVoters.add(new AuthenticatedVoter());
accessDecisionVoters.add(new PreInvocationAuthorizationAdviceVoter(preInvocationAuthorizationAdvice()));
final UnanimousBased accessDecisionManager = new UnanimousBased(accessDecisionVoters);
accessDecisionManager.setAllowIfAllAbstainDecisions(true);
return accessDecisionManager;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return this.defaultMethodSecurityExpressionHandler();
}
This jar has a spring.factories file in META-INF so that being a spring-boot application the #Configuration is loaded. Now I expect when I include this jar in the classpath to have the #PreAuthorize annotations to start working. However what I see is that AbstractSecurityExpressionHandler is invoked and it calls the abstract method createSecurityExpressionRoot(authentication, invocation); which always goes to DefaultWebSecurityExpressionHandler and never to the DefaultMethodSecurityExpressionHandler. I can see the DefaultMethodSecurityExpressionHandler is constructed when my application starts, so I'm really not sure what happens here.
EDIT: Here's my spring.factories file:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.nemesis.platform.module.personalization.core.config.PersonalizationConfig
Working on moving from spring security xml configuration to Java Config in Spring Security.
In my class SecurityConfiguration that extends WebSecurityConfigurerAdapter. However, the problem is that the userDetailsService is not being used by the security filters specifically the UsernamePasswordAuthenticationFilter. I looked at the startup and it seems that this is not created before Spring boots creates the default InMemoryUserDetailsManager.
#Configuration
#EnableWebMvcSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http)
throws Exception {
http.userDetailsService(userDetailsService);
}
}
I have also tried to override the userDetailsServiceBean and userDetailsService in this class using the custom injected ApplicationUserDetailsService.
#Bean(name="myUserDetailsBean")
#Override
public UserDetailsService userDetailsServiceBean() {
return userDetailsService;
}
#Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
However, when I try to override the authenticationManagerBean it looks like it invokes my configuration before the spring boot configuration initializes but it throws an error (below) that there is a circular reference when initializing the UsernamePasswordAuthenticationFilter. Do I need really need to override the authenticationManagerBean because I need to define what goes into the UsernamePasswordAuthenticationFilter.
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
..
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter]: Circular reference involving containing bean 'securityBeansConfiguration' - consider declaring the factory method as static for independence from its containing instance. Factory method 'usernamePasswordAuthenticationFilter' threw exception; nested exception is java.lang.IllegalArgumentException: successHandler cannot be null
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.1.4.RELEASE.jar:4.1.4.RELEASE]
... 70 common frames omitted
Ideas?
Hi there is simple way to override UserDetailsService
import com.dog.care.domain.User;
import com.dog.care.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.inject.Inject;
import java.util.Optional;
#Component("userDetailsService")
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
private final Logger log = LoggerFactory.getLogger(UserDetailsService.class);
#Inject
private UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
String lowercaseLogin = login.toLowerCase();
Optional<User> userFromDatabase = userRepository.findOneByLogin(lowercaseLogin);
return userFromDatabase.map(user -> {
if (!user.getActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
return new CustomUserDetails(user);
}).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
}
}
This is important: #Component("userDetailsService")
Thanks
Aleksandar