Can't inject dependencies in Spring Security configuration - spring

I have a spring project using Spring Security. I want to inject dependencies into my WebSecurityConfigurerAdapter extending class but the dependencies do not seem to be getting injected. My controllers also use dependency injection and it does work there.
My SecSecurity class:
#Configuration
#Component
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserPrincipalDetailsService userPrincipalDetailsService;
LogoutSuccessHandler handler = new LogoutSuccess();
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http. logout().and().
httpBasic()
.and()
.authorizeRequests()
.antMatchers( "/", "/home", "/user", "/vestigingen", "/available", "/logout").permitAll()
.anyRequest().authenticated()
.and().logout().logoutSuccessHandler(handler).deleteCookies("JSESSIONID").invalidateHttpSession(false).permitAll()
.and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
#Bean
public UserDetailsService userDetailsService() {
return new UserPrincipalDetailsService();
}
DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userPrincipalDetailsService);
return daoAuthenticationProvider;
}
}
It cannot find beans for the userPrincipalDetailsService class.
UserPrincipalDetailService class:
#Component
public class UserPrincipalDetailsService implements UserDetailsService {
private UserRepositorySQL userRepository = new UserRepositorySQL();
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
UserEntity user = userRepository.findUserByUsername(s);
UserPrincipal userPrincipal = new UserPrincipal(user);
return userPrincipal;
}
}
to clarify: I can't inject any dependencies within my SecSecurityConfig class eventhough i have tried annotating it in many different ways.

Define the bean name #Component("userPrincipalDetailsService") in the UserPrincipalDetailsService class.
#Component("userPrincipalDetailsService")
public class UserPrincipalDetailsService implements UserDetailsService {
private UserRepositorySQL userRepository = new UserRepositorySQL();
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
UserEntity user = userRepository.findUserByUsername(s);
UserPrincipal userPrincipal = new UserPrincipal(user);
return userPrincipal;
}
}

Related

Security configuration with spring boot

I'm a beginner, I want to code the password when adding users but there is an error (#Bean).
Error:
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Description:
Field passwordEncoder in com.example.demo.service.UsersService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
My code:
#Configuration
#EnableConfigurationProperties
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UsersService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().disable() //TODO implement csrf
.cors().and().authorizeRequests().antMatchers(HttpMethod.OPTIONS,"/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/add-users").permitAll()
.and().authorizeRequests().anyRequest().authenticated();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
public class UsersService implements UserDetailsService {
#Autowired
UsersRepository repo;
#Autowired
private PasswordEncoder passwordEncoder;
public Users save(Users u) {
String encodpass=passwordEncoder.encode(u.getPassword());
String confpass=passwordEncoder.encode(u.getConfirmepass());
u.setConfirmpass(confpass);
u.setPassword(encodpass);
u.setLock(false);
u.setEnable(true);
return repo.save(u);
}
}
#RestController
public class UsersController {
#Autowired
private UsersService service;
#PostMapping("/add-users")
public Users add(#RequestBody Users u) {
return service.save(u);}
The problem is that in your SecurityConfiguration you are injecting UsersService, and the UserService bean depends on a bean of type PasswordEncoder that has not been created yet.
I'll simplify your configuration by using a more ellegant way of configuring Spring Security:
#Configuration
//your other annotations
public class SecurityConfiguration {
#Bean
public SecurityFilterChain app(HttpSecurity http) throws Exception {
http.csrf().disable().cors().and().authorizeRequests().antMatchers(HttpMethod.OPTIONS,"/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/add-users").permitAll()
.and().authorizeRequests().anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
And that's it, you don't really need the AuthenticationManager bean unless you are actually using it. In case that you need it, you can create it like this:
#Bean
public AuthenticationManager authenticationManager(UsersService usersService) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(usersService);
ProviderManager manager = new ProviderManager(daoAuthenticationProvider);
return manager;
}
I apologize for any compilation error beforehand because I wrote this from a phone.
add #Service annotation to UserService class. also add #EnableWebSecurity to config class.

Spring security cannot access userService while login NullPointerException

When making post request to login this error shows up telling me that userService is null.
When I create an object of UserService instead of autowiring it it passes it but tell me that the repository called in userService is null. The repository is autowired and i cannot instanciate it because its an interface.
Here is the service class:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserService userService;
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new MyUserDetails(userService.getByUsernameOrEmail(s));
}
}
And this is the security configuration class:
Also I am creating an object of MyUserService because spring cannot autowire it telling me that no bean have such name.
#Configuration
#EnableWebSecurity
public class UserSercurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new MyUserDetailsService());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().hasRole(UserType.ADMIN.toString())
.and().formLogin();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
You cannot instantiate method or variables in your repository as it is an interface but you can autowire your repository class to use your method declared in userRepository interface, you have to autowire your repository class.You can do it this way as I have done in the below code.
Your service class should be like this:
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = userRepository.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Could not find user");
}
return new MyUserDetails(user);
}
And your repository should be like this:
#Repository
public interface UserRepository extends JpaRepository<Users, Long> {
#Query("SELECT u FROM Users u WHERE u.name = ?1")
public Users getUserByUsername(String username);
}
And also autowire UserDetailsService in your configuration class and pass the instance userDetailsService in your configure(AuthenticationManagerBuilder auth) method, UserDetailsService provides you instance of your MyUserDetailService class.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.and()
.formLogin().permitAll()
.defaultSuccessUrl("/", true)
.and()
.logout().permitAll()
.logoutSuccessUrl("/");
}
Try to implement in this way, If this is solution of your question please let me know and if you still have doubt feel free to ask.

Spring boot and jwt Encoded password does not look like BCrypt

I know there are several questions about this, but none of them helped, everything seemed ok to me... I hope I'm wrong about this.
I've followed this tutorial to create authentication with JWT for my Spring Boot application, but I'm stuck with this warning
o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
This is the class that extends WebSecurityConfigurerAdapter:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()));
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
}
And this is the class that implements UserDetailsService:
public class UserDetailsServiceImpl implements UserDetailsService {
private ApplicationUserRepository applicationUserRepository;
public UserDetailsServiceImpl(ApplicationUserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("loadUserByUsername");
ApplicationUser applicationUser = applicationUserRepository.findByUsername(username);
if (applicationUser == null) {
System.out.println("User non trovato");
throw new UsernameNotFoundException(username);
}
System.out.println("Username: " + applicationUser.getUsername());
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}
Also, I'm wondering where the class WebSecurity is istantiated with the correct parameters passed. Is this managed automatically by Spring?
Found the problem, it was that my class UserDetailsServiceImpl did not have the "#Service" annotation. Now it's working fine.

Spring security annotations don't work on service layer

I'm using Spring security to define access rules at method-level and facing with the issue is Spring security annotations are not working on service layer. But they work normal on controller layer.
Here are my configuration:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/api/register")
.antMatchers("/api/activate")
.antMatchers("/api/lostpassword")
.antMatchers("/api/resetpassword");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Autowired
private MutableAclService mutableAclService;
#Autowired
private RoleHierarchy roleHierarchy;
public GlobalSecurityConfiguration() {
super();
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(mutableAclService));
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
}
}
Service does not work:
#Override
#PreAuthorize("hasRole('ROLE_ADMIN')")
public Iterable<Appliance> getAll() {
return applianceRepo.findAll();
}
Controller works well:
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<PagedResources<Appliance>> getPage(#PageableDefault Pageable pageable, PagedResourcesAssembler pagedAssembler) {
Page<Appliance> appliancePage = applianceService.getPage(pageable);
return ResponseEntity.ok(pagedAssembler.toResource(appliancePage, applianceAssembler));
}
I realized that I'm wrong when putting #PreAuthorize on getAll() method but I was testing on getPage(pageable) method. Spring security configurations work well. Sorry for any inconveniences.

Spring Security 3.2: #Autowire doesn't work with java configuration and custom AuthenticationProvider in Spring MVC application?

This problem is relatively well discussed in several blog posts and SO questions. Nevertheless, I wasn't able to find one specifically addressing the problem with java configuration. I'm suspecting that I'm doing something wrong in my java configuration files, since I've found some posts indicating that the problem can be resolved by removing the debug XML tag (https://jira.springsource.org/browse/SEC-1885).
I'm using 3.2.0.RELEASE of spring security, and 3.2.6.RELEASE of spring framework. Below the main files used in the spring security/mvc configuration and the custom AuthenticationProvider.
WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.mypackage"})
#ImportResource( { "classpath:/spring-data.xml", "classpath:/trace-context.xml" })
#EnableTransactionManagement
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
#Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
#Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("target/temp.db");
}
#Bean
public RepositoryInitializer repositoryInitializer() {
return new RepositoryInitializer();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(StringUtils.parseLocaleString("en"));
return cookieLocaleResolver;
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:messages/messages", "classpath:messages/validation");
// if true, the key of the message will be displayed if the key is not
// found, instead of throwing a NoSuchMessageException
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
// # -1 : never reload, 0 always reload
messageSource.setCacheSeconds(0);
return messageSource;
}
}
WebInitializer:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[] { characterEncodingFilter, new SiteMeshFilter()};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
//servletContext.addListener(new HttpSessionEventPublisher());
}
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().permitAll();
// .antMatchers("/", "/login").permitAll()
// .anyRequest().authenticated();
http
.formLogin()
.defaultSuccessUrl("/hello")
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll();
http
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**");
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(new ApplicationAuthenticationProvider());
}
}
WebSecurityInitializer:
public class WebSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
AuthenticationProvider:
#Component(value = "authenticationProvider")
public class ApplicationAuthenticationProvider implements AuthenticationProvider {
#Autowired
public UserService userService;
public ApplicationAuthenticationProvider() {}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
User user = userService.loadUserByUsername(username);
if (user == null) {
throw new BadCredentialsException("Username not found.");
}
if (!password.equals(user.getPassword())) {
throw new BadCredentialsException("Wrong password.");
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(username, password, authorities);
}
#Override
public boolean supports(Class<?> arg0) {
return true;
}
}
UserService:
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
Spring is throwing an exception while it is building its application context (during application initialization):
[ERROR] [main 11:53:37] (FrameworkServlet.java:initServletBean:467) Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationProvider': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public com.evidencefactory.service.UserService com.evidencefactory.security.ApplicationAuthenticationProvider.userService; nested exception is java.lang.IllegalArgumentException: Can not set com.evidencefactory.service.UserService field com.evidencefactory.security.ApplicationAuthenticationProvider.userService to sun.proxy.$Proxy71
I don't understand why it is happening, but if I remove the UserDetailsService interface implementation from UserService class, then the application starts successfully. However, when ApplicationAuthenticationProvider is invoked by Spring, the UserService is not autowired into it and the application throws a NullPointerException.
java.lang.NullPointerException
at com.evidencefactory.security.ApplicationAuthenticationProvider.authenticate(ApplicationAuthenticationProvider.java:33)
Figured out how to put it to work, although there still some issues unanswered.
1) I still don't know why Spring context initialization fails when UserService implements UserDetailsService. Given that I'm not seeing use for it, since I'm using a custom AuthenticationProvider, I just removed this implementation and things are ok for now.
To the best of my knowledge (from what I could understand from my first initial reading of Spring Security reference documentation) providing a custom AuthenticationProvider or an UserDetailsService implementation are exclusive alternatives.
2) As noticed by one of the respondents (#Sotirios Delimanolis) I was instantiating ApplicatinoAuthenticationProvider by hand and since it wasn't being managed by Spring this instance would not have an UserService instance autowired into it. Based on this, I changed WebSecurityConfig to get an autowired instance of ApplicationAuthenticationProvider as can be seen below:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ApplicationAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(authenticationProvider);
}
}
This wasn't still sufficient, because ApplicationAuthenticationProvider wasn't being autowired into WebSecurityConfig. Based on this link Spring Security 3.1.3 #Autowired not Work when using WebApplicationInitializer I noticed that this was because security config should have a component scan declaration too. Adding #ComponentScan(basePackages = {"com.mypackage"}) to WebSecurityConfig resolved the problem.
I'm going to assume that UserService is a class and has some #Transactional annotation either on itself or one of its methods.
You'll need to add CGLIB to your classpath and change your #EnableTransactionManagement to
#EnableTransactionManagement(proxyTargetClass = true)
so that Spring uses CGLIB proxying (which can proxy classes) instead of JKD proxies (which cannot).
Alternatively, you can create an interface UserService and implement (and annotate with #Service) a UserServiceImpl class. Your autowired UserService field would remain the same, but Spring will be able to use JDK proxies.

Resources