Reputation: 1703
I have researched the web quite a bit, but still cant find solution to my problem. Everywhere people using stuff that does not fit my case. My scenerio:
user sends with post {email:"admin", password:"admin"} then there is Login controller which checks if input is correct and after that token is returned in header/body.
Im stuck with the part where I pass the values from controller to Spring authentication process.
Below is my config:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private TokenAuthenticationProvider tokenAuthenticationProvider;
@Autowired
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(this.tokenAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated().and()
.csrf().disable();
}
}
here is my controller :
@RestController
public class LoginController {
@Autowired
private AuthenticationManager authenticationManager;
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> login(@RequestBody LoginCredentials payload){
Authentication authentication = this.authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
payload.getUsername(),
payload.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
return ResponseEntity.ok("OK");
}
}
and this is my authentication provider
@Component
public class TokenAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(authentication.getName().equals("admin") && authentication.getCredentials().equals("admin")) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), grantedAuths);
} else {
return null;
}
}
@Override
public boolean supports(Class<? extends Object> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
Im doing post request with credentials and this is the answer Im getting:
{
"timestamp": 1486995388844,
"status": 403,
"error": "Forbidden",
"exception": "org.springframework.security.authentication.ProviderNotFoundException",
"message": "Access Denied",
"path": "/login"
}
Upvotes: 0
Views: 2471
Reputation: 759
It is interesting that you have chosen to create a new controller to deal with the login, but essentially you are duplicating the work that Spring Security does for you. Think about it for a second: you send a HTTP POST request to your custom controller (at a certain endpoint) which will check the credentials for the user to be authenticated by invoking the authentication provider - this is already the mechanism by which Spring Security module works (you need to perform a POST to a certain endpoint and the security container will invoke the defined authentication providers and decide if the user should be granted access or not).
I believe you do not need that controller: you just need some extra configuration to your security container, so let's begin!
BTW: Even if Spring configuration is mostly written in Java, Spring Security configuration doesn’t yet support full Java and still needs to be XML for the most part.
You can configure Spring Security to initialize the authentication process when a certain endpoint is hit (with that HTTP POST request). In XML, this is done as follows:
<http>
<form-login login-processing-url="/perform_login"/>
...(other configuration)
</http>
Although I have tried to find it in the documentation, I can't find the equivalent Java config. We can invoke the formLogin()
method on the HttpSecurity
object like this:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
but there seems to be no Java method to configure the login processing URL. The loginPage()
method simply specifies the URL to send users to if login is required (that's not what we are looking for).
If you do not configure the login processing URL, the security container will just use the default one which is /j_spring_security_check
.
So now we can POST our authentication credentials to /perform_login
, and Spring Security will use those against its authentication filters to grant you access (or not).
Are we done? Not yet.
The default username
and password
parameters are called j_username
and j_password
, so you will have to POST these to /perform_login
to get the desired behaviour. Luckily, we can configure the name of these two (in XML again - sorry):
<http>
<form-login login-processing-url="/perform_login"/>
username-parameter="email"
password-parameter="password"
...(other configuration)
</http>
Now we are set! By issuing a HTTP POST with two parameters (or more if you want the CSRF tokens) - namely email and password to /perform_login, we can authenticate properly (and we didn't have to create yet another class called LoginCredentials
like you did.
Upvotes: 3