Manually set Authenticated Spring User - java

I am building a SpringBoot application.
I am using Spring Security, and have a UserDetailsService implementation setup:
public class MyUserDetailService implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
org.springframework.security.core.userdetails.User springUser = null;
User user = userService.loadUserByUsername(username);
if(user != null){
List<SimpleGrantedAuthority> authorities = null;
List<Role> roles = user.getRoles();
if(roles != null){
authorities = new ArrayList<>();
for(Role currentRole: roles){
authorities.add(new SimpleGrantedAuthority(currentRole.name()));
}
}
springUser = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
return springUser;
}
}
I have a service layer that contains method for adding users to the database:
public interface UserService {
public Long addStandardUser(String firstName, String lastName, String username, String password);
#PreAuthorize("hasRole('ADMIN')")
public Long addAdministratorUser(String firstName, String lastName, String username, String password);
#PreAuthorize("hasRole('ADMIN')")
public User loadUserByUsername(String username);
public Iterable<User> getUsers(int pageNumber, int pageSize, Direction direction, String sort);
}
I also have a CommandLineRunner implementation that I use (in dev mode) to initialize the database with sample users:
#Component
#Profile("dev")
public class DBInitializer implements CommandLineRunner {
#Autowired
private UserService userService;
#Override
public void run(String... args) throws Exception {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ADMIN"));
Authentication authentication = new UsernamePasswordAuthenticationToken("foo", null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
userService.addAdministratorUser("John", "Doe", "jdoe", "1234");
System.out.println("done!");
}
}
The trouble is that I am getting an Access Denied exception from Spring in the CommandLineRunner when I try to add the user. I'm assuming that the issue is that I am manually 'injecting' a Spring User incorrectly. The addAdminUser() method has to be run by a user in the ADMIN role, so I need to temporarily run as an ADMIN user. I know there is a #RunAs annotation that I remember using in other J2EE apps, but I'm not sure how that interfaces with a Spring application, or if that is used in a different context altogether

...
// The default role prefix starts with `ROLE_`
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
...
More detail please see here

Related

Spring security UserDetails and controller (get user)

There is UserDetail and LoginController.
UserDetail gets the user from the database.
UserDetail
public class UserDetail implements UserDetailsService {
private final
UserServiceJpa userServiceJpa;
public UserDetail(UserServiceJpa userServiceJpa) {
this.userServiceJpa = userServiceJpa;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// Get user
User user = userServiceJpa.findUserByEmail(email);
if (user == null){
throw new UsernameNotFoundException("There is no such user " + email);
}
return new org.springframework.security.core.userdetails.User(user.getEmail(),
user.getPassword(),
user.getEnabled(),
user.getAccount_non_expired(),
user.getCredentials_non_expired(),
user.getAccount_non_locked(),
getAuthorities());
}
private Collection<? extends GrantedAuthority> getAuthorities(){
List<SimpleGrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return authList;
}
}
LoginController
#Controller
public class LoginController {
#GetMapping(value = "/login")
public String login () {
return "/login";
}
}
Question! How in LoginController get this user, which was received by UserDetail?
I do this in order not to reconnect to the database. Thus, I want to know how the user was blocked if he was blocked - enabled, accountNonExpired, credentialsNonExpired, accountNonLocked
You can create a UserDetails class from your LoginController by Autowiring it, and then call the function in your login class.
#Controller
public class LoginController {
#Autowired
private UserDetails userDetails;
#GetMapping(value = "/login")
public String login (#QueryParam("email")String email) {
userDetails.loadUserByUsername(email);
return "/login";
}
}

Spring Security - auto login not working after registering user

In my Spring Boot MVC application I used Spring Security to provide authentication and users registration. Authentication and registration of users are working correctly, but after creating user account I want to login him automatically. While doing this I am receiving BadCredentialsException. This user with the same credentials normally is correctly login in with the login form. I appreciate any help from you. Below is my code:
Method from controller
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") User user, BindingResult result,
WebRequest request, Errors errors) {
User registeredUser = null;
if (result.hasErrors() == false) {
registeredUser = createUserAccount(user, result);
}
if (registeredUser == null) {
return "/register";
}
securityService.autologin(registeredUser.getLogin(), registeredUser.getPassword());
return "/whiprounds";
}
SecurityServiceImpl (method authenticate is throwing exception)
#Service
public class SecurityServiceImpl implements SecurityService {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class);
#Override
public String findLoggedInLogin() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails) userDetails).getUsername();
}
return null;
}
#Override
public void autologin(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
}
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private RoleRepository roleRepository;
#Override
public User registerNewUserAccount(User user) throws LoginExistsException {
if (loginExists(user.getLogin())) {
throw new LoginExistsException("User with this login already exists");
}
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
user.setRoles(new HashSet<>((Collection<? extends Role>) roleRepository.findAll()));
return userRepository.save(user);
}
private boolean loginExists(String login) {
User user = userRepository.findByLogin(login);
if (user != null) {
return true;
}
return false;
}
}
UserDetailsServiceImpl
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findByLogin(s);
if (user == null) {
throw new UsernameNotFoundException(s);
}
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Role role : user.getRoles()) {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), grantedAuthorities);
}
}
First of all, this method throwing an exception because of the password problem (As far as I can see from your code). Your method registerNewUserAccount returning User object which password has already been hashed. Then you passing it here:
securityService.autologin(registeredUser.getLogin(), registeredUser.getPassword());
So it turns out that you're passing hashed password into authenticationManager later. This is wrong - you should pass original password into it. Smth like this:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registerUser(#ModelAttribute("user") User user, BindingResult result,
WebRequest request, Errors errors) {
User registeredUser = null;
String originalPassword = user.getPassword();
if (result.hasErrors() == false) {
registeredUser = createUserAccount(user, result);
}
if (registeredUser == null) {
return "/register";
}
securityService.autologin(registeredUser.getLogin(), originalPassword);
return "/whiprounds";
}
Second of all, authenticationManager.authenticate(usernamePasswordAuthenticationToken); - this method actually returns filled Authentication object (if authentication was successful), and you should put this object into SecurityContext, and not the one you've passed to `authenticationManager'. Smth like this:
#Override
public void autologin(String username, String password) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
Authentication auth = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
if (auth.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(auth);
logger.debug(String.format("Auto login %s successfully!", username));
}
}
Hope this helps.

Using Custom class instead of User class in spring security for encoding and password authentication

I am new to Spring Security .I have Android application and JAVA as back end service.Customer Service has been exposed via REST.
My requirement is to encode the password for customer and save it into the database while sign up process and later I can authenticate it using Spring Security in java backend. All the Customer data is feeded in JSON format from android while sign up and login process
I have seen that Spring has its own User and UserService class for authentication purpose.
I have a requirement to use Customer class instead of USER(Spring provided bean) class.Customer Bean is having password as a field . Is there any way to use our own class and table in spring security? How can i decode the password and later authenticate while login ? please shed some light over it as i have tight requirement not to use User class of spring
Customer Bean
#XmlRootElement(name = "customer")
#XmlAccessorType(value = XmlAccessType.FIELD)
public class CustomerWrapper {
#XmlElement
protected Long id;
#XmlElement
protected String firstName;
#XmlElement
protected String lastName;
#XmlElement
protected String emailAddress;
#XmlElement
protected String username;
#XmlElement
protected String primaryPhone;
#XmlElement
protected String secondaryPhone;
#XmlElement
protected String password;
#XmlElement(name = "customerAddress")
#XmlElementWrapper(name = "customerAddress")
protected List<AddressWrapper> customerAddress = new ArrayList<AddressWrapper>();
setter and getter
yes it's possible to use your own user class (MyUser in my example). What you need:
Your custom MyUserService must implement UserDetailsService, which forces you to implement loadUserByUsername:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = myUserRepository.findByUsername(username);
if (user == null) {
String msg = String.format("UserAccount '%s' not found.", username);
throw new UsernameNotFoundException(msg);
}
// assign authorities to the user:
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_REGISTERED"));
User springUser = new User(user.getUsername(), user.getPassword(), authorities);
return springUser;
}
In the above code, you return a Spring-specific user, but you will not need to work with it, it's just for the authentication manager.
in your xml config you need to connect your MyUserService with Spring's AuthenticationManager:
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" c:strength="12" />
<!-- manager responsible for loading user account with assigned roles -->
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="myUserService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
To log in (i.e. to authenticate a user), you need to do something like this (I put this in my AuthenticationService:
public boolean login(String username, String password) {
try {
Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
if (authenticate.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticate);
return true;
}
} catch (BadCredentialsException e) {
logger.warn("User {} tried to log in with bad credentials", username);
} catch (RuntimeException e) {
logger.error("An error occured during login of user {}", username, t);
}
return false;
}
To get your currently loggedIn MyUser:
public MyUser getLoggedInMyUser() {
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
return myUserService.findByUsername(auth.getName());
}
return null;
} catch (RuntimeException e) {
logger.error("An error occurred while getting logged in User.", e);
logout();
}
}
You have the option to extend UserDetailsService from spring framework and to create a custom CustomerUserDetails that will create a User from a Customer
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private CustomerService customerService // here it's your CustomerService
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
UserDetails userDetails = null;
Customer customer = custermerService.login(username);
userDetails = new CustomerUserDetails(customer);
return userDetails;
}
The class CustomerUserDetails should extends User, or implement UserDetails from spring and create a corresponding User. Basically it will need a username, password and GrantedAuthorities
public class CustomerUserDetails implements org.springframework.security.core.userdetails.UserDetails{
//must provide here username, password and GrantedAutority
}

Spring MVC Login Programmatically: No qualifying bean of type MyUserDetailsService

I have a Spring Application with spring security and it all works great. When I try to login the user upon registration I can't get it to work...
I have searched a lot and discovered that my principal is my UserDetails object thus I need a method that returns it by username which is in MyUserDetailsService:
public class MyUserDetailsService implements UserDetailsService {
private UserService userService;
#Autowired(required=true)
#Qualifier(value="userService")
public void setUserService(UserService us){
this.userService = us;
}
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
// Programmatic transaction management
/*
return transactionTemplate.execute(new TransactionCallback<UserDetails>() {
public UserDetails doInTransaction(TransactionStatus status) {
com.mkyong.users.model.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
});*/
Users user = userService.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRoleses());
return buildUserForAuthentication(user, authorities);
}
// Converts com.mkyong.users.model.User user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(Users user, List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRoles> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRoles userRole : userRoles) {
System.out.println("----------------------->"+userRole+"<---------------------------------------");
setAuths.add(new SimpleGrantedAuthority(userRole.getRoles().getRole() ));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
On my controller after register i try to log the user by doing this:
MyUserDetailsService myuds = new MyUserDetailsService();
UserDetails ud = myuds.loadUserByUsername(user.getUsername());
// Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(),
// AuthorityUtils.createAuthorityList("ROLE_CONCORRENTE"));
// SecurityContextHolder.getContext().setAuthentication(authentication);
Authentication authentication = new UsernamePasswordAuthenticationToken(ud, null);
SecurityContextHolder.getContext().setAuthentication(authentication);
And i even have the beans autowired:
private MyUserDetailsService myUserDetailsService;
#Autowired(required=true)
#Qualifier(value="myUserDetailsService")
public void setMyUserDetailsService(MyUserDetailsService userdetailsservice){
this.myUserDetailsService = userdetailsservice;
}
My Bean:
<bean id="myUserDetailsService" class="com.setelog.spring.service.MyUserDetailsService" >
<property name="userService" ref="userService" />
</bean>
There can be many issues with this case
1) have you defined the package within which spring would search the beans
2) Have you defined this class as service (e.g. by using #service annotation)
3) Have you provided its implementation?

Authentication with Spring Security + Spring data + MongoDB

I want to use Spring security with MongoDB (using Spring data) and retrieve the users from my own database for spring security. However, I can not do that since my userservice type does not seem to be supported.
This is my UserService class:
public class UserService {
private ApplicationContext applicationContext;
private MongoOperations mongoOperations;
public UserService() {
applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class);
mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate");
}
public User find(String username) {
return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class);
}
}
And my SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService); //THIS DOES NOT WORK
builder.inMemoryAuthentication().withUser("username").password("password").roles("USER");
}
}
The line I commented says:
The inferred type UserService is not a valid substitute for the bounded parameter <T extends UserDetailsService>.
How can I fix it so I can retrieve the users from my own database?
Service Layer
You have to create a separate service implementing org.springframework.security.core.userdetails.UserDetailsService and inject it inside the AuthenticationManagerBuilder.
#Component
public class SecUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here add user data layer fetching from the MongoDB.
I have used userRepository*/
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException(username);
}else{
UserDetails details = new SecUserDetails(user);
return details;
}
}
}
Model
UserDetails Should be also implemented. This is the POJO which will keep the user authenticated details by the Spring. You may include your Entity data object wrapped inside it, as I have done.
public class SecUserDetails implements UserDetails {
private User user;
public SecUserDetails(User user) {
this.user = user;
}
......
......
......
}
Security Config
Autowire the service that we created before and set it inside the AuthenticationManagerBuilder
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
SecUserDetailsService userDetailsService ;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
}
Create your own authentication provider providing a class that extends the UserDetailservice.
Ensure content scanning is enable in your spring context xml file.
<authentication-provider user-service-ref="userModelService">
<password-encoder hash="sha" />
</authentication-provider>
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Name not found!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
public void saveUserDetails(UserModel userModel)
{
repository.save(userModel);
}
}
This class will enable spring query mongo for the username and password required for authentication. Next create the user model class.
public class UserModel
{
private String id;
#Indexed(unique=true)
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Create the user implementation class that extends the DAO.
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Oops!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
Finally configure mongo and you're done.

Categories

Resources