Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How implement Spring security when login page having more field apart from user name and password?

I have a login page where the user need put the below information VIN number,email, zip code and accessCode which they will get from different application.

So to validate a user I need all the information in my custom UserDetailsService class and then will called a procedure to authenticate the user.

But I saw that when I implement the UserDetailsService like below

@Component
 public class LoginService implements UserDetailsService {
@Autowired
LoginStoredProcedureDao loginStoredProcedureDao;

public Map<String, Object> verifyLogin(LoginDetails details) {
    return loginStoredProcedureDao.verifyLogin(details);

}
@Override
public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {
    // TODO Auto-generated method stub
      //verifyLogin();
    return null;
}

}

The loginDetails Object is like below

public class LoginDetails {
String vin;
String email;
String zipcode;
String accessCode;
}

In the above situation how to use spring security. Here the user need to give all information to validate him self.

like image 293
Krushna Avatar asked Apr 19 '13 12:04

Krushna


People also ask

How do I limit the number of login attempts in Spring Security?

Solution. Review the existing Spring Security's authentication class, the “locked” feature is already implemented. To enable the limit login attempts, you need to set the UserDetails. isAccountNonLocked to false.

Does Spring Security use default login form?

Spring Boot Login Page tutorial shows how to work with a default login page. Spring security secures all HTTP endpoints by default. A user has to login in a default HTTP form. To enable Spring Boot security, we add spring-boot-starter-security to the dependencies.

How many ways we can implement Spring Security?

There are basically 2 ways to implement spring security. through bean configuration in . xml files and other by using Annotations. Annotation based method is easy to use in long term as it is less ambiguous.


1 Answers

It is not the responisibility of UserDetailsService to validate the Authentication token. This is what an AuthenticationProvider does.

So first leave your implementation of UserDetailsService the single responsibility of loading all the data of the user from the database by login:

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = null;
        try {
            user = userRepository.findByUsername(username);
        } catch (NotFoundException e) {
            throw new UsernameNotFoundException(String.format("No user found for username %s!", username);
        }
        retrun new UserDetailsImpl(user);
    }
}

Than to intercept additional parameters from a login form you need to implement AuthenticationDetailsSource. It may be a good idea to extend WebAuthenticationDetails, but you can have just any object returned by AuthenticationDetailsSource.

@Component
public class WebAuthenticationDetailsSourceImpl implements AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> {

    @Override
    public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) {
        // the constructor of MyWebAuthenticationDetails can retrieve
        // all extra parameters given on a login form from the request
        // MyWebAuthenticationDetails is your LoginDetails class
        return new MyWebAuthenticationDetails(context);
    }
}

And to do the validation implement your own AuthenticationProvider by either implementing the interface itself or extending AbstractUserDetailsAuthenticationProvider or DaoAuthenticationProvider:

@Component
public class UserDetailsAuthenticationProviderImpl extends AbstractUserDetailsAuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyWebAuthenticationDetails detais = (MyWebAuthenticationDetails) authentication.getDetails();
        // verify the authentication details here !!!
        // and return proper authentication token (see DaoAuthenticationProvider for example)
    }
}

Than you just need to pass your implementations to AuthenticationManager and UsernamePasswordAuthenticationFilter.

<util:list id="authenticationProviders">
    <ref bean="userDetailsAuthenticationProviderImpl" />
</util:list>

<!-- 
    This bean MUST have this exact ID to be the default authenticationManager!
    This is required prior Spring 3.1, as authentication-manager-ref is not
    present in sec:http element before!
 -->
<bean id="org.springframework.security.authenticationManager"
    name="authenticationManager"
    class="org.springframework.security.authentication.ProviderManager"
    c:providers-ref="authenticationProviders" />

<bean id="usernamePasswordAuthenticationFilter"
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
    p:authenticationManager-ref="authenticationManager"
    p:authenticationDetailsSource-ref="webAuthenticationDetailsSourceImpl" />

<sec:http authentication-manager-ref="authenticationManager">
    <sec:custom-filter position="FORM_LOGIN_FILTER" ref="usernamePasswordAuthenticationFilter" />
</sec:http>

Hope this helps!

P.S. Consider constructor injection over field injection! It's more testable and states the contract of the class better. See this discussion.

like image 73
Roadrunner Avatar answered Oct 22 '22 20:10

Roadrunner