I have just looked at many examples regarding custom login page and every single example uses the same "/login" path. After so much frustration I finally got the login to work with the default.
I want the login form to be rendered at "/" and not login.
Once authenticated, I want it to go "/home".
I am assuming the POST still goes to the default "/login"?
I have tried both on the POST form "/" (Same as the path of the form with GET) & "/login"
Now when I try to login, it keeps redirecting me back to the same "/" with the form.
Here is the basic API logic again: Default login page should be at "/", Form is posted to "/login", Success Url after login is "/home", "/home" and "/mama" are protected routes. After logout, it should redirect to "/"
I cant get through the app, and not sure if anything is missing, It keeps showing the same form login as if I am not getting through even though the password is clearly okay
Here are the routes as explained in the WebConfigurerAdapter filer:
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
/*auth.inMemoryAuthentication()
.withUser("appuser").password("1234").roles("HEAD")
.and()
.withUser("Mama").password("Mama").roles("MAMA");*/
}
@Override
/*
* Now we have learnt the basics of Spring Security & Authrization method is completed.
* Lets fix Authentication first!
* Got it to work with hasAuthority & hasAnyAuthority but not with roles, not sure why, but it works atm
*
* */
protected void configure(HttpSecurity http) throws Exception {
//Disabled for development
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/mama").hasAuthority("MAMA")
.antMatchers("/home").hasAnyAuthority("HEAD", "MAMA")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/").permitAll()
.defaultSuccessUrl("/home")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutSuccessUrl("/");
}
@Bean
/*
* Returning no op password encoder for now, as we are not encoding passwords as no registration
* implemented for Prototype. We would need to add the users from a separate service. W
*
* */
public PasswordEncoder getPasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
//LoginApi:
@RestController
public class LoginApi {
@RequestMapping("/")
public String index(){
return "<form method='POST' action='/login'>" +
"<div>" +
"<input type='text' name='username' placeholder='Username: ' />" +
"</div>" +
"<div>" +
"<input type='password' name='password' placeholder='Password: ' />" +
"</div>" +
"<div>" +
"<input type='submit' name='submit' value='Login' />" +
"</div>" +
"</form>";
}
@RequestMapping("/home")
public String home(){
return "Welcome to Home!";
}
/*
* This method can be deleted in the end
* */
@RequestMapping("/mama")
public String roleTest(){
return "This end point is only for Mama!";
}
}
For this test, I am not using the database, but I have a working implementation of UserPrincipal and UserDetailsService which works perfectly on the default login setup. Happy to share that code if needed. But at this point, I cant see what could go wrong.
In case someone wants to see the UserDetailsService & UserDetails code, that has been included too:
@Service
public class EmployeeDetailsService implements UserDetailsService {
@Override
/*
* First, we are testing the Employee details service, independent of the Database, just to make sure we have this part working,
* For the purpose of these prototypes, we wont use password encoder because we are not registering,
*
* */
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (!username.equals("Mama")){
throw new UsernameNotFoundException("You got the wrong Username, should be mama");
}
Employee employee = new Employee();
Role role = new Role();
role.setName("HEAD");
employee
.setUsername(username)
.setPassword("1234")
.setRole(role);
return new EmployeePrincipal(employee);
}
}
public class EmployeePrincipal implements UserDetails {
private Employee employee;
public EmployeePrincipal(Employee employee){
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(employee.getRole().getName()));
return authorities;
}
@Override
public String getPassword() {
return employee.getPassword();
}
@Override
public String getUsername() {
return employee.getUsername();
}
/*
* Methods below are the rubbish methods, we keep as true for now
*
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
See image attached from Networking, I am not understanding what is happening? The POST request is getting a 302 redirect back to "/" with a 200 status code?
This is happening regardless of weather the credentials are right or wrong
Any advice would be appreciated
CSRF needs to be implemented with a custom form, so for testing and in dev, its best to disable CSRF
protected void configure(HttpSecurity http) throws Exception {
//Disabled for development
http.authorizeRequests()
.antMatchers("/mama").hasAuthority("MAMA")
.antMatchers("/home").hasAnyAuthority("HEAD", "MAMA")
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin()
.loginPage("/").permitAll()
.loginProcessingUrl("/login")
.defaultSuccessUrl("/home")
.and()
.logout()
.logoutSuccessUrl("/");
}