It seems that Spring can't autowire the UserDetailsService. I don't understand why.
WebConfig.java:
#Configuration
#ComponentScan("testproject")
#EnableWebMvc
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "testproject",
entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".html");
return bean;
}
#Bean
public UserDetailsService userDetailsService() {
UserDetailsService userDetailsService =
new UserDetailsServiceImpl();
return userDetailsService;
}
}
MyAppInitializer.java:
public class MyAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(final ServletContext sc) throws ServletException {
System.out.println("onStartup!");
AnnotationConfigWebApplicationContext root =
new AnnotationConfigWebApplicationContext();
root.register(WebConfig.class);
root.setServletContext(sc);
root.scan("testproject");
sc.addListener(new ContextLoaderListener(root));
ServletRegistration.Dynamic appServlet =
sc.addServlet("dispatcher", new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
SecurityConfig.java:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Bean
DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider =
new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
return daoAuthenticationProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
// .authorizeRequests().antMatchers("/login").permitAll()
// all other requests need to be authenticated
.authorizeRequests().anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
SecurityWebApplicationInitializer.java:
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Full error:
Error creating bean with name 'securityConfig':
Unsatisfied dependency expressed through field 'userDetailsService'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type
'org.springframework.security.core.userdetails.UserDetailsService'
available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Any help is appreciated!
EDIT:
UserDetailsServiceImpl.java:
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user != null) {
return new UserDetailsImpl(user);
} else {
throw new UsernameNotFoundException("User not found.");
}
}
}
Your #ComponentScan should point to the package of the classes, I'm assuming testproject is not the package name.
Related
Well, I am currently programming on a project with Spring Security where I'd like to register a user in the database.
The proble is, if I vistit the endpoint where the registration should happen I get redirected to the default Login page of Spring Boot which should not happen.
This is my SecurityConfiguration.java
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public BCryptPasswordEncoder encodePasswd(){
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}
My RestController called UserController.java looks like this:
#RestController
#RequestMapping("/secure/rest")
public class UserController {
#Autowired
private UserRepository userRepository;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#PostMapping("user/register")
public String addUser(#RequestBody User user){
String passwd = user.getPasswordUser();
String encryptPasswd = passwordEncoder.encode(passwd);
user.setPasswordUser(encryptPasswd);
userRepository.save(user);
return "addedUser";
}
}
Try this.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().disable();
}
}
I have a project based on SpringMVC 5 and an error happened when I tried to run it on JBoss EAP 7.3.
14:33:14,134 DEBUG
[org.springframework.beans.factory.support.DefaultListableBeanFactory]
(ServerService Thread Pool -- 99) Creating shared instance of
singleton bean 'userDetailsService'
14:33:14,140 ERROR [org.springframework.web.context.ContextLoader]
(ServerService Thread Pool -- 99) Context initialization failed:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'springSecurityFilterChain' defined in
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration:
Bean instantiation via factory method failed;......nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'com.aaa.bbb.security.service.UserService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Here's the code of class "UserDetailsServiceImpl":
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
#Autowired
private SessionUtils sessionUtils;
#Override
public UserDetails loadUserByUsername(String userID) throws UsernameNotFoundException {
User user = userService.get(userID);
if (user == null) throw new UsernameNotFoundException("Could not find user");
sessionUtils.expireUserSessions(user.getUser_name()); // Force expire other session
return new UserDetailsInfo(user);
}
}
And here's the code of class "UserService":
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public User get(String id) {
return userRepository.findById(id).get();
}
public User getuUserByUsername(String user_name) {
return userRepository.getuUserByUsername(user_name);
}
public int incLoginErrorCount(String id) {
return userRepository.incLoginErrorCount(id);
}
public int resetLoginErrorCount(String id) {
return userRepository.resetLoginErrorCount(id);
}
public int lockCount(String id) {
return userRepository.lockCount(id);
}
public int changeUPassword(String id, String password) {
return userRepository.changeUPassword(id, password);
}
}
And I'm using "WebAppInitializer":
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
private String TMP_FOLDER = "/tmp";
private int MAX_UPLOAD_SIZE = 5 * 1024 * 1024;
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebSecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebMvcConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new HiddenHttpMethodFilter(), new MultipartFilter(),
new OpenEntityManagerInViewFilter() };
}
#Override
protected void registerDispatcherServlet(ServletContext servletContext) {
super.registerDispatcherServlet(servletContext);
servletContext.addListener(new HttpSessionEventPublisher());
}
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
MultipartConfigElement multipartConfigElement = new MultipartConfigElement(TMP_FOLDER, MAX_UPLOAD_SIZE,
MAX_UPLOAD_SIZE * 2, MAX_UPLOAD_SIZE / 2);
registration.setMultipartConfig(multipartConfigElement);
}
}
To call "WebMvcConfig":
#ComponentScan(basePackages = {"com.aaa.bbb"})
public class WebMvcConfig implements WebMvcConfigurer {
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(100000);
return multipartResolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**", "/resources/**", "/resources/**", "/webjars/**", "/log/**")
.addResourceLocations("classpath:/css/", "classpath:/js/", "classpath:/json/", "/webjars/", "/META-INF/");
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasenames("message/messages");
source.setUseCodeAsDefaultMessage(true);
source.setDefaultEncoding("UTF-8");
return source;
}
}
If #ComponentScan worked, this error should not happen.
Thank you!
I think you should use #Service annotation on the UserDetailsServiceImpl class too. Since it is not annotated, componentScan doesn't know that it should autowire the services inside it. So UserDetailsServiceImpl class should look like this.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
#Autowired
private SessionUtils sessionUtils;
#Override
public UserDetails loadUserByUsername(String userID) throws UsernameNotFoundException {
User user = userService.get(userID);
if (user == null) throw new UsernameNotFoundException("Could not find user");
sessionUtils.expireUserSessions(user.getUser_name()); // Force expire other session
return new UserDetailsInfo(user);
}
}
Hi I develop a Spring Boot app (v1.5.18.BUILD-SNAPSHOT), but this fails when I try to start the app....
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 1 of constructor in com.eficacia.security.WebSecurity required a bean of type 'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' in your configuration.
The problem is that I have the bean configured:
#Configuration
public class AppConfiguration {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
WebSecurity:
#Configuration
#EnableWebSecurity
#ComponentScan({"com.eficacia.security"})
public class WebSecurity extends WebSecurityConfigurerAdapter {
public static final String USER_REGISTRATION_URL = "/v1/user";
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.cors().and().csrf().disable()
.authorizeRequests().antMatchers(HttpMethod.POST, USER_REGISTRATION_URL).permitAll()
.anyRequest().authenticated()
.and().addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
.addFilter(new JWTAuthorizationFilter(authenticationManager(), getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
//configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200","http://opusclick.com","https://gateway2.tucompra.com.co"));
configuration.addAllowedOrigin("*");
configuration.addAllowedHeader("*");
configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","PATCH","DELETE"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
WebConfigurer:
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
private final ApplicationContext applicationContext;
private final EntityManager entityManager;
#Autowired
public WebMvcConfiguration(ApplicationContext applicationContext, EntityManager entityManager) {
this.applicationContext = applicationContext;
this.entityManager = entityManager;
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
argumentResolvers.add(new DTOModelMapper(objectMapper, entityManager));
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
JacksonMapperConfiguration jacksonMapperConfiguration= new JacksonMapperConfiguration();
converters.add(jacksonMapperConfiguration.mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
}
My question is very simple, and that is why the definition of the class 'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' is not found if there really exists a configuration class for this.
Many Thanks!
Looks like your web configurer class isn't in the package com.eficacia.security or one of its subpackages.
Move WebMvcConfiguration in a place within the #ComponentScaned packages.
You are creating bean of type PasswordEncoder and try to autowire BCryptPasswordEncoder
#Configuration
public class AppConfiguration {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Change this:
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
to this :
private PasswordEncoder passwordEncoder ;
public WebSecurity(UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder ) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = PasswordEncoder ;
}
Spring could not found the BcryptPasswordEncoder type as you are returning PasswordEncoder
Ok guys, I hope you can help me, this is my last attempt. I am quite new to this spring security world and I cant get this to work. I tried many things, followed many tutorials and nothing.
The problem is as you saw in the title, make a custom user details service to work. It just not logs in, It appears that the customuserdetailsservice is not being called, as the sysouts are not showing in the console...
It works as a charm with spring security in memory features. Below are my codes.
Spring Security Config:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication().withUser("ram").password("ram123").roles("ADMIN");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/bower_components/**", "/resources/**", "/img/**"); // #3
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/call").permitAll() // #4
.antMatchers("/resource", "/video").hasRole("USER") // #6
.anyRequest().authenticated();
}
#Bean(name="passwordEncoder")
public PasswordEncoder passwordencoder(){
return new BCryptPasswordEncoder();
}
}
CustomUserDetailsService
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
private UserService userService;
#Override
public UserDetails loadUserByUsername(String ssoId)
throws UsernameNotFoundException {
User user = userService.findByUsername(ssoId);
System.out.println("User : "+user);
if(user==null){
System.out.println("User not found");
throw new UsernameNotFoundException("Username not found");
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
user.isEnabled(), true, true, true, getGrantedAuthorities(user));
}
private List<GrantedAuthority> getGrantedAuthorities(User user){
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(user.getRole()));
System.out.print("authorities :"+authorities);
return authorities;
}
}
Initializer Class
#SpringBootApplication
#EnableWebSocket
public class One2OneCallApp implements WebSocketConfigurer {
#Bean
public CallHandler callHandler() {
return new CallHandler();
}
#Bean
public UserRegistry registry() {
return new UserRegistry();
}
#Bean
public KurentoClient kurentoClient() {
return KurentoClient.create();
}
#Bean
public UiApplication uiApplication(){
return new UiApplication();
}
#Bean
public CustomUserDetailsService customUserDetailsService(){
return new CustomUserDetailsService();
}
#Bean
public SecurityConfig securityConfig(){
return new SecurityConfig();
}
#Bean
public EncryptPassword encryptPassword(){
return new EncryptPassword();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(callHandler(), "/call");
}
public static void main(String[] args) throws Exception {
System.out.println("Iniciando");
new SpringApplication(One2OneCallApp.class).run(args);
}
}
I've also tested the communication with the database and it works perfectly fine. I'm seeking any help. Sorry for bad English. Thank you all!
Edit: Answered my own question down below.
In SecurityConfig class:
#Autowired
CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
Change
#Autowired
UserDetailsService userDetailsService;
to
#Autowired
CustomUserDetailsService userDetailsService;
Also, import the security config in you web/socket config and move the component scan there, not on the security
#ComponentScan(basePackageClasses = CustomUserDetailsService.class)
#Import(value = { SecurityConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter { /*...*/ }
You are setting hasRole(" ") in security config and you are using authorities for authentication.
instead of using .antMatchers("/resource", "/video").hasRole("USER")
use .antMatchers("/resource", "/video").hasAuthority("USER")
I ended up staying with the built in memory anthentication just for the presentation I had to do. I think my problem had to do with something in spring boot and the initialization in my application.
So I have looked around and everywhere says that I need to check my configuration but what doesn't make sense to me is that my app will run on a different computer but the one I use for development. Just to provide some context, this app is purely restful with token authentication. Here's what my config looks like:
Security Init:
#Order(1)
public class SecurityWebappInitializer extends AbstractSecurityWebApplicationInitializer
{
}
App Init:
#Order(2)
public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected Class<?>[] getRootConfigClasses()
{
return new Class[]{RootConfiguration.class, SecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses()
{
return new Class<?>[]{WebConfiguration.class};
}
#Override
protected String[] getServletMappings()
{
return new String[]{"/", "/rest/*"};
}
#Override
protected Filter[] getServletFilters()
{
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[] {characterEncodingFilter};
}
}
SecurityConfig:
#EnableWebSecurity
#EnableGlobalMethodSecurity(jsr250Enabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private NoOpAuthProvider noOpAuthenticationProvider;
#Autowired
private TokenFilter tokenFilter;
public SecurityConfig()
{
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/rest/authentication/login")
.antMatchers("/services/**")
.antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers("**").hasRole("RUN").and()
.addFilterAfter(tokenFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().and()
.logout();
}
#Bean
public AuthenticationManager authenticationManager() throws Exception
{
List<AuthenticationProvider> authenticationProviders = new ArrayList<AuthenticationProvider>();
authenticationProviders.add(noOpAuthenticationProvider);
return new ProviderManager(authenticationProviders);
}
}
WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.example.tinker.web")
public class WebConfiguration extends WebMvcConfigurerAdapter
{
#Autowired
private PrincipalRetrievalService principalRetrievalService;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**").addResourceLocations("resources/").setCachePeriod(31556926);
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers)
{
argumentResolvers.add(new WebArgumentResolver(principalRetrievalService));
super.addArgumentResolvers(argumentResolvers);
}
}
Any idea what would be causing my problems?
There is no need to implement AbstractSecurityWebApplicationInitializer.
Security is initialized via ApplicationInitializer/RootConfiguration/#ComponentScan/SecurityConfig. Also there si no need to add SecurityConfig in getRootConfigClasses.