1

I'm working on a blog application with Spring Boot Security.
My overridden configure method looks like:

@Override
    protected void configure(HttpSecurity httpSec) throws Exception {
        httpSec
          .authorizeRequests()
            .antMatchers("/users").authenticated()
            .antMatchers("/admin", "/db").hasRole("ADMIN")
            .antMatchers("/**").permitAll()
            //.anyRequest().authenticated()
           .and()
            .formLogin()
              .loginPage("/login").permitAll()
             .and()
              .logout().logoutSuccessUrl("/login?logout").permitAll();
        httpSec.csrf().disable();
        httpSec.headers().frameOptions().disable();
    }

And my custom login form:

<form name="login" th:action="@{/login}" method="post" class="form-signin">
            <h1>Please log in!</h1>
            <div th:if="${param.error}" class="alert alert-danger">Wrong username and/or password.</div>
            <div th:if="${param.logout}" class="alert alert-success">You logged out successfully.</div>
            <label for="username">Username</label>
            <input type="text" name="username" class="form-control" placeholder="username" required="true"/>
            <label for="password">Password</label>
            <input type="password" name="password" class="form-control" placeholder="password" required="true"/>
            <br/>
            <button type="submit" class="btn btn-lg btn-primary btn-block">Log in</button>
            <br/>
            <a href="/registration" style="color: blue !important">You can register here.</a>
            <hr/>
    </form>

The default behaviour of Spring Security is that when I send a request to an URL which needs to be authenticated (for example /users or /admin in my case) it automatically redirects to this custom login page.

I would like to disable this automatic redirection. When authentication is needed I would like to throw a custom exception (which I handle with a separate Class with @ControllerAdvice annotation) instead with message like "You have to log in to see this content". But I would like to reach my custom login page via navigation menu to authenticate "manually".

How could I reach this?

So far I have tried .formLogin().disabled(). In this way I can still reach my custom login page, but when I try to submit it gave an error "Method not allowed". But it is logical since th:action="@{/login}" can't send the username and password to /login.

1
  • 2
    You probably need to look into AuthenticationEntryPoint. Commented Jul 28, 2020 at 15:22

1 Answer 1

1

I have found a solution. Maybe not perfect, but it works.
First of all, I have commented out the login/logout part in my configure method:

@Override
protected void configure(HttpSecurity httpSec) throws Exception {
    httpSec
        .authorizeRequests()
        .antMatchers("/users").authenticated()
        .antMatchers("/admin", "/db").hasRole("ADMIN")
        .antMatchers("/**").permitAll()
        /**
        .anyRequest().authenticated()
      .and()
        .formLogin()
        .loginPage("/login").permitAll()
      .and()
        .logout().logoutSuccessUrl("/login?logout").permitAll();
        */
      .and()
        .csrf().disable()
        .headers().frameOptions().disable();
}

From now when authentication is needed it won't redirect to any (custom) login screen. But the Forbidden (403) error should be handled:

case "Forbidden":
    if (SecurityContextHolder.getContext().getAuthentication().getAuthorities().toString().equals("[ROLE_ANONYMOUS]"))
        error.put("error", "You have to log in to see this content");
    else error.put("error", "It is only for admins");
    break;

Next step is creating a login form into the navigation menu:

<form th:action="@{/loginauth}" method="post">
    <span sec:authorize="!isAuthenticated()">
        <input type="text" name="email" placeholder="e-mail" required="true"/>
        <input type="password" name="password" placeholder="password" required="true"/>
        <button type="submit" class="btn btn-success btn-xs">Log in</button>
    </span>
</form>

After submit we have to be forwarded to a @PostMapping in one of the @Controller classes:

@PostMapping("/loginauth")
public String authenticateLogin(HttpServletRequest request) {
    loginService.authenticateBlogUser(request.getParameter("email"), request.getParameter("password"));
    return "redirect:/";
}

Finally this data should be sent to a service layer to process:

BlogUserRepository blogUserRepository;
@Autowired
public void setBlogUserRepository(BlogUserRepository blogUserRepository) {
    this.blogUserRepository = blogUserRepository;
}
public void authenticateBlogUser(String email, String password) throws UsernameNotFoundException {
    BlogUser user = blogUserRepository.findByEmail(email);
    if (user == null || !user.getPassword().equals(password))
        throw new UsernameNotFoundException("Wrong e-mail and/or password");
    Collection<GrantedAuthority> authorities = new HashSet<>();
    Set<Role> roles = user.getRoles();
    for (Role role : roles)
        authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getAuth()));
    SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword(), authorities));
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.