2

I just learned about spring security and want to make a connection to a database using java hibernate configuration but I find very few examples or tutorial. I find a lot more by using xml configuration. And i'm using Spring 4.0.2, Spring-Security 3.2.0 and Hibernate 4.3.2 here

my question is: how the following xml converted into java configuration?

<authentication-manager>
    <authentication-provider user-service-ref="customUserDetailsService">
        <password-encoder hash="plaintext">
    </password-encoder></authentication-provider>
</authentication-manager>

where the CustomUserDetailsService.java

package com.whatever.svtest.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.whatever.svtest.dao.UserDao;

@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        com.whatever.svtest.model.User domainUser = userDao.getByUsername(username);

        if (domainUser == null) {
            throw new UsernameNotFoundException("user not found");
        }

        List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority("USER"));        

        return new User(username, domainUser.getPassword(), true, true, true, true, authorities);
    }

}

on SecurityConfig.java I use the default login form created by spring. I'm trying to figure out by myself how to convert the xml config to java config.

package com.whatever.svtest.init;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

import com.whatever.svtest.service.impl.UserServiceImpl;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(new UserServiceImpl()).passwordEncoder(NoOpPasswordEncoder.getInstance());

    }

}

and i put the SecurityConfiguration.java on the Initializer.java like this

package com.whatever.svtest.init;

import javax.servlet.Filter;

import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        // return null;
        return new Class[] { SecurityConfiguration.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebAppConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] { new DelegatingFilterProxy("springSecurityFilterChain") };
    }

}

WebAppConfig.java

package com.whatever.svtest.init;

import javax.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@Import({ DatabaseConfig.class })
@ComponentScan(basePackages = { "com.whatever.svtest.controller" })
@PropertySource({ "classpath:persistence-mysql.properties" })
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Resource   
    private Environment env;

    @Override   
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
    }

    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource source = new ResourceBundleMessageSource();
        source.setBasename("messages");
        source.setUseCodeAsDefaultMessage(true);
        return source;
    }

    @Bean
    public ViewResolver setupViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

}

When i run my webapps i got this. (i put the image here https://i.stack.imgur.com/Mssrc.jpg)

and i also read (somewhere) about creating an custom implementation of AuthenticationProvider.java but i dont know where to put this code..

package com.whatever.svtest.init;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import com.whatever.svtest.dao.UserDao;
import com.whatever.svtest.model.User;

public class MyAuthProvider implements AuthenticationProvider {

    @Autowired
    private UserDao userDao;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        User user = userDao.getByUsername(name);
        authentication.setAuthenticated(user != null && password.equals(user.getPassword()));
        return authentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {

        return (MyAuthProvider.class.isAssignableFrom(authentication));
    }

}
Mirza
  • 281
  • 1
  • 4
  • 14
  • Can I know why you are particularly looking for annotations to configure spring security ? At least for spring security its has more advantage to have it as xml configuration which gives the flexibility to change it without touching the existing code. You could get some programmatic configurations in this link http://stackoverflow.com/questions/19353578/security-method-annotations-with-java-configuration-and-spring-security-3-2 – Jay Mar 04 '14 at 16:22
  • hi Jay, I have no reason "why". I'm a new comers of spring framework. I just studied Spring Framework since 3 weeks ago. I see a lot of changes lately. Everything convert from xml to java config. So, why not i learn the newest one.. :) – Mirza Mar 04 '14 at 16:46

2 Answers2

1

Inconsistent Configuration?

The configuration you posted doesn't quite make sense to me. Specifically the following:

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(new UserServiceImpl()).passwordEncoder(NoOpPasswordEncoder.getInstance());
}

A solution

It seems that you have not defined UserServiceImpl, but you have defined CustomUserDetailsService (which is likely the argument that should be passed in. However, in order for a bean to be autowired, you need to create it as a bean. So you should change your configuration as such:

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(uds());
}

@Bean
public CustomUserDetailsService uds() {
    return new CustomUserDetailsService();
}

By returning CustomUserDetailsService as a @Bean you ensure Spring autowire it properly.

A few additional notes:

  • You do not need a custom AuthenticationProvider. This is since you are authenticating with a username / password a UserDetailsService is fine. If you wanted to authenticate w/ something other than a username / password you would create a custom AuthenticationProvider
  • There is no need to specify the no-op Password Encoder since this is the default.

Improving CustomUserDetailsService

One thing to point out with your current implementation is that while you can @Autowire fields directly, it makes it much easier to make mistakes, so you should probably change your CustomUserDetailsService to have a constructor that allows injecting the UserDao. This also makes unit testing easier (so you don't need to use reflection to set the UserDao). So you would update CustomUserDetailsService to be:

@Service
@Transactional(readOnly = true)
public class CustomUserDetailsService implements UserDetailsService {

    private UserDao userDao;

    @Autowired
    public CustomUserDetailsService(UserDao userDao) {
        this.userDao = userDao;
    }

Then your configuration can be the following:

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(uds());
}

@Autowired
private UserDao userDao;

@Bean
public CustomUserDetailsService uds() {
    return new CustomUserDetailsService(userDao);
}

Update based on new error

You also need to ensure your UserDao is picked up as a Bean. For example:

@Bean
public UserDao userDao() {
    return new UserDao(...);
}

NOTE: Make Sure you initialize the UserDao properly (i.e. ensure all its dependencies are initialized. If you use Autowired on UserDao ensure those dependencies are @Bean also.

Rob Winch
  • 21,440
  • 2
  • 59
  • 76
  • thanks for the answer. I follow your instruction. but it still not working. i got Error http://pastebin.com/htHBH5RN – Mirza Mar 04 '14 at 22:33
  • i found this link (http://stackoverflow.com/questions/8162698/no-authenticationprovider-found-for-usernamepasswordauthenticationtoken) it solve my problem although it does not use hibernate, but as you said that "i dont need to custom" the AuthenticationProvider it make me more confuse. – Mirza Mar 05 '14 at 01:41
  • Your UserDao needs to be defined as a Bean too. See my update – Rob Winch Mar 05 '14 at 02:05
1

[SOLVED]

after two days struggling with my own code finnaly i found the solution..!

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = SecurityConfiguration.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

}

I dont have to create the new bean. I just need to pass the UserService object into userDetailsService method, put the autowired and of course use the @ComponentScan to the current class. UserService class already have a UserDao and i implement the UserDetailsService in it.

@Service("userService")
@Transactional(readOnly = true)
public class UserServiceImpl implements UserService, UserDetailsService {

    @Autowired
    private UserDao userDao;

    // other method

    @Override
    public User getByUsername(String username) {
        return userDao.getByUsername(username);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = getByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("user not found");
        } else {
            List<GrantedAuthority> listAuthorities = new ArrayList<GrantedAuthority>();
            listAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            return new org.springframework.security.core.userdetails.User(username, user.getPassword(), true, true, true, true, listAuthorities);
        }

    }
}

thanks to Rob Winch for giving a clue.

Mirza
  • 281
  • 1
  • 4
  • 14