In a spring mvc app using spring security, I want to use a custom AuthenticationProvider
to check n-number of additional fields beyond the default username
and password
. I am trying to use Java configuration. How should I set it up?
First, some explanation about the interfaces you are working with and the role they play in the authentication process:
Authentication
- represents the result of authenticating a user. Holds the authorities granted to that user and any additional details that may be needed about the user. As there is no way for the framework to know, what details are going to be needed, the authentication object has a getDetails
method that can return any object
AuthenticationProvider
- object that can create an Authentication
object in some way. To make them more reusable, some (or most) of AuthenticationProvider
s refrain from setting the user details on the Authentication
object, as each application may need specific user details. Instead they delegate the process of resolving the user details to a settable UserDetailsService
UserDetailsService
- a strategy for retrieving the user details required in your application.So, if you are creating a custom AuthenticationProvider
you may not even need to implement it in a way that requires a UserDetailsService
. The decission is up to you and depends, on whether you plan on reusing your implementation in other projects.
As for the compilation problems in your code, you are mixing two ways of providing the UserDetailsService
. In the CustomAuthenticationProvider
you have annotated the userService
field with the @Inject
annotation .This means, that the container (Spring application context in your case) is to find a suitable implementation and inject it into that field at runtime using reflection. The process of setting this field by the context is called dependency injection. In the SecurityConfig
class you are trying to provide the implementation yourself by setting the field through the setUserDetailsService
method that does not exist in your class.
To resolve this problem you need to decide to use one of the ways to provide the UserDetails service and either:
@Inject
annotation and create the setUserDetailsService
method, orUserDetailsService
as a beanAs for which of the ways should you choose, the dependecy injection way may by better if you can find a way of making your SecurityConfig
class reusable in other projects. In that case you could just import it (by using the @Import
annotaion) and declare a different UserDetailsSerice
implementation as a bean in your next application and have it working.
Usually, classes like the SecurityConfig
are not really reusable, so creating the setter and removing the dependency injection would probably be my first choice.
EDIT
A working, albeit a simplistic implementation (based heavily on this blog entry) would be:
public class CustomAuthenticationProvider implements AuthenticationProvider{ @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String name = authentication.getName(); String password = authentication.getCredentials().toString(); List<GrantedAuthority> grantedAuths = new ArrayList<>(); if (name.equals("admin") && password.equals("system")) { grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN")); } if(pincodeEntered(name)){ grantedAuths.add(new SimpleGrantedAuthority("ROLE_PINCODE_USER")); } Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } private boolean pincodeEntered(String userName){ // do your check here return true; } }
Then in your config class change the following method:
@Bean AuthenticationProvider customAuthenticationProvider() { return new CustomAuthenticationProvider(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With