To get familiar with Spring Security basic concepts you can refer to my previous posts. In this post, we will see how we can use a custom authentication provider to perform the authentication.
The most common approach to verifying an authentication request is to load the corresponding UserDetails and check the loaded password against the one that has been entered by the user. This is the approach used by the
DaoAuthenticationProvider. It is the simplest AuthenticationProvider implemented by Spring Security.It leverages a UserDetailsService (as a DAO) in order to lookup the username, password and GrantedAuthorities. It authenticates the user simply by comparing the password submitted in a UsernamePasswordAuthenticationToken against the one loaded by the UserDetailsService.
web.xml contents remain the same as given in my previous post for using a custom UserDetailsService.Following are the dependencies for this example.
pom.xml
4.0.0
SpringSecurity
SpringSecurity
war
0.0.1-SNAPSHOT
SpringSecurity1 Maven Webapp
http://maven.apache.org
junit
junit
3.8.1
test
org.springframework
spring-orm
3.2.0.RELEASE
org.springframework
spring-webmvc
3.2.0.RELEASE
org.springframework.security
spring-security-web
3.2.0.RELEASE
org.springframework.security
spring-security-config
3.2.0.RELEASE
org.springframework.security
spring-security-taglibs
3.2.0.RELEASE
jstl
jstl
1.2
compile
taglibs
standard
1.1.2
compile
javax
javaee-api
7.0
org.hibernate
hibernate-core
3.6.10.Final
mysql
mysql-connector-java
5.1.26
commons-dbcp
commons-dbcp
20030825.184428
commons-pool
commons-pool
20030825.183949
commons-collections
commons-collections
3.2.1
javassist
javassist
3.12.1.GA
org.codehaus.jackson
jackson-mapper-asl
1.9.12
SpringSecurity
spring-security.xml
This is how you can configure an authentication provider. 'authService' bean is the class which implements the UserDetailsService interface. It is the same as given in the previous post. The PasswordEncoder is optional. A PasswordEncoder provides encoding and decoding of passwords presented in the UserDetailsobject that is returned from the configured UserDetailsService.
Spring Security’s PasswordEncoder interface is used to support the use of passwords which are encoded in some way in persistent storage. You should never store passwords in plain text. Always use a one-way password hashing algorithm such as bcrypt which uses a built-in salt value which is different for each stored password. Do not use a plain hash function such as MD5 or SHA, or even a salted version. Bcrypt is deliberately designed to be slow and to hinder offline password cracking, whereas standard hash algorithms are fast and can easily be used to test thousands of passwords in parallel on custom hardware. You might think this doesn’t apply to you since your password database is secure and offline attacks aren’t a risk. If so, do some research and read up on all the high-profile sites which have been compromised in this way and have been pilloried for storing their passwords insecurely. It’s best to be on the safe side.
Using 'org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder' is a good choice for security. Note that for the authentication provider to be able to compare password submitted in a UsernamePasswordAuthenticationToken against the one loaded by the UserDetailsService, you will need to store these passwords inside some database only after encrypting it using the BCryptPasswordEncoder.
spring-servlet.xml
classpath:hibernate.cfg.xml
org.hibernate.cfg.AnnotationConfiguration
Controller
package com.spring.security.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.spring.security.domain.MyUser;
import com.spring.security.service.IUserService;
@Controller
public class HelloController {
@Autowired
IUserService userService;
@RequestMapping(value = "/user/welcome", method = RequestMethod.GET)
public String printWelcomeUser() {
return "hello";
}
@RequestMapping(value = "/admin/welcome", method = RequestMethod.GET)
public String printWelcomeAdmin() {
return "admin";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(Model model) {
return "login";
}
@RequestMapping(value = "/home", method = RequestMethod.GET)
public String getHomePage(Model model) {
return "hello";
}
@RequestMapping(value = "/accessdenied", method = RequestMethod.GET)
public String getFailurePage(Model model) {
return "failure";
}
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String getLogoutPage(Model model, HttpServletRequest req) {
req.getSession().invalidate();
return "logout";
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
String saveUser(@RequestBody MyUser user, HttpServletResponse response) {
System.out.println("User:" + user.getUsername());
userService.saveUser(user);
response.setStatus(201);
return "success";
}
}
AuthService
package com.spring.security.service.impl;
import java.util.ArrayList;
import java.util.Collection;
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.spring.security.dao.IUserDao;
import com.spring.security.domain.MyUser;
import com.spring.security.service.IAuthService;
@Service
public class AuthServiceImpl implements IAuthService, UserDetailsService {
@Autowired
IUserDao userDao;
@Transactional
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
MyUser details = userDao.getUser(username);
Collection authorities = new ArrayList();
SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority(
"ROLE_USER");
SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority(
"ROLE_ADMIN");
if (details.getRole().equals("user"))
authorities.add(userAuthority);
else if (details.getRole().equals("admin")) {
authorities.add(userAuthority);
authorities.add(adminAuthority);
}
UserDetails user = new User(details.getUsername(),
details.getPassword(), true, true, true, true, authorities);
return user;
}
}
UserSevice
package com.spring.security.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.spring.security.dao.IUserDao;
import com.spring.security.domain.MyUser;
import com.spring.security.service.IUserService;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
IUserDao userDao;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Transactional
@Override
public void saveUser(MyUser user) {
String password = user.getPassword();
String encryptedPassword = passwordEncoder.encode(password);
user.setPassword(encryptedPassword);
userDao.saveUser(user);
}
}
As seen above, the password is first encrypted and then passed to the userDao to get saved.
UserDao
package com.spring.security.dao.impl;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.spring.security.dao.IUserDao;
import com.spring.security.domain.MyUser;
@Repository
public class UserDaoImpl implements IUserDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public MyUser getUser(String username) {
Session session = sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(MyUser.class);
criteria.add(Restrictions.eq("username", username));
MyUser user = (MyUser) criteria.uniqueResult();
return user;
}
@Override
public void saveUser(MyUser user) {
sessionFactory.getCurrentSession().save(user);
}
}
You can view/download the complete source code from here.
Thanks !