I'm facing problem with enabling user log-in page - 404 not found.
This is tutorial that I'm using as base of my application security.
That's how configure function looks like:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
I have tried to simply add here:
.and()
.formLogin()
.loginPage("/login")
.permitAll();
and changing .addFilter
to .addFilterAfter()
i still get 404.
As you can see in the tutorial, that's how login function is accessed:
curl -i -H "Content-Type: application/json" -X POST -d '{
"username": "admin",
"password": "password"
}' http://localhost:8080/login
Is it even possible to enable built-in login form for this purpose?
And if not, what's the sollution there? Do i have to create /login endpoint in controller, and then POST data to http://localhost:8080/login?
And what about added authenticationFilter
. Does changes have to be made there?
You have two problems here:
JWTAuthenticationFilter
extends from UsernamePasswordAuthenticationFilter
which by default responds to the URL /login. formLogin()
also generates a login form in this URL. So, you have two places accepting input for /login. If you choose to do a custom login page (by .loginPage("/login")
) you have to do this in a different URL, and provide the HTML view to this page. But you said that you wanted to use the built-in login form. So, here comes another problem: JWTAuthenticationFilter
. It can be achieved by setting a custom URL in AbstractAuthenticationProcessingFilter
as saw here. This works like a charm, but the implementation from JWTAuthenticationFilter
is expecting as input an JSON, which is not provided by /login form (it send parameters in POST). So you have to change the code for JWTAuthenticationFilter.attemptAuthentication
to decide if the input comes from a JSON body or parameters. I implemented this in my environment and worked great. Below is the code (just the snippets):
WebSecurity:
public JWTAuthenticationFilter getJWTAuthenticationFilter() throws Exception {
final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());
filter.setFilterProcessesUrl("/api/auth/login");
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(getJWTAuthenticationFilter())
.addFilter(new JWTAuthorizationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.formLogin()
.loginProcessingUrl("/api/auth/login")
.permitAll();
}
JWTAuthenticationFilter:
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
ApplicationUser creds = null;
if (req.getParameter("username") != null && req.getParameter("password") != null) {
creds = new ApplicationUser();
creds.setUsername(req.getParameter("username"));
creds.setPassword(req.getParameter("password"));
} else {
creds = new ObjectMapper()
.readValue(req.getInputStream(), ApplicationUser.class);
}
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}