Create a Logout Link in Html Page - spring-boot

I have working login and logout forms with using Spring Security Basic Authentication and thymeleaf.
my problem is my logout link is display as a button. but I want to include it in a <a href=""> tag. but when I include my logout link in the <a href=""> tag it isn't working. here is my code.
Logout Controller - Spring Boot
#RequestMapping(value = {"/logout"}, method = RequestMethod.POST)
public String logoutDo(HttpServletRequest request,HttpServletResponse response){
HttpSession session= request.getSession(false);
SecurityContextHolder.clearContext();
session= request.getSession(false);
if(session != null) {
session.invalidate();
}
for(Cookie cookie : request.getCookies()) {
cookie.setMaxAge(0);
}
return "redirect:/login?logout";
}
Logout form - Front End
<form th:action="#{/logout}" method="POST">
<input type="submit" value="Logout"/>
</form>
Security Configurations
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/login", "/", "/customer/**", "/registration", "/admin/**")
.and()
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority("ADMIN").antMatchers("/customer/**").hasAuthority("CUSTOMER")
.antMatchers("/login", "/registration", "/").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().and().csrf().disable()
.formLogin() //login configuration
.loginPage("/login")
.loginProcessingUrl("/login")
.usernameParameter("email")
.passwordParameter("password")
.defaultSuccessUrl("/customer/home")
.and().logout() //logout configuration
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and().exceptionHandling() //exception handling configuration
.accessDeniedPage("/error");
}

A workaround is to submit a 'hidden' form:
<a href="javascript: document.logoutForm.submit()" > Logout Link </a>
<form name="logoutForm" th:action="#{/logout}" method="post" th:hidden="true">
<input hidden type="submit" value="Logout"/>
</form>

Related

CSRF token not provided with spring boot 2.7.1

I was using version 2.6.x of spring boot with success.
I've switched to 2.7.1. and adapted some of my security config to match the new format.
But now the CSRF token are no longer generated for me and injected in my login form, maybe the same with other forms, but I can't test it out.
Here is my security config
#Order(1)
#Bean
public SecurityFilterChain actuatorFilterChain(final HttpSecurity http) throws Exception {
// #formatter:off
return http
.requestMatchers().antMatchers("/jobs/**", "/actuator/**").and()
.authorizeRequests().antMatchers("/jobs/**", "/actuator/**").hasRole("SYSTEM").and()
.httpBasic()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.build()
;
// #formatter:on
}
#Bean
public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
// #formatter:off
return http
.requestMatchers().antMatchers(applicationRoutes).and()
.authorizeRequests()
.antMatchers(applicationRoutes).hasAnyRole(applicationAuthorities).and()
.csrf().disabled().and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.headers()
.httpStrictTransportSecurity().disable()
.and()
.build()
;
// #formatter:on
}
}
Here is my login page
<html>
<head>
</head>
<body>
<form th:action="#{/login}"
method="POST"
>
<div class="field">
<input type="text"
id="username"
name="username"
placeholder="&#xf007 Nom d'utilisateur"
style="font-family:Lato,'Helvetica Neue', FontAwesome">
</div>
<div class="field">
<input type="password"
id="password"
name="password"
placeholder="&#xf023 Mot de passe"
style="font-family:Lato,'Helvetica Neue', FontAwesome">
</div>
<button class="ui primary button"
style="width:100%"
type="submit">Connexion</button>
</form>
</body>
</html>
The error that is shown to me is a 405 saying /login doesn't support POST.
But the form sended to the browser doesn't include CSRF token in the form.
I suspect that I miss some new bits of configuration. But I don't know where to look for.
When adding http.requestMatchers() to a filter chain, the FilterChainProxy will ignore routes that do not match. In your case, /login is not matched by a filter chain. Therefore, no CSRF token is available for your login page.
Ensure /login is available in the applicationRoutes field in your example to fix the issue. You can add a permitAll rule to your /login route with .mvcMatchers("/login").permitAll() or adding .permitAll() to the .formLogin() DSL.

Springboot login from different url

I want to implement Springboot Security for my first project and I don't know how to do the next thing:
I have the frontend and backend in different folders
on the frontend I use live-server with the url http://127.0.0.1:9000/ and the backend with the url http://localhost:8080
my login page is http://127.0.0.1:9000/login.html
Can anyone help me binding the frontend login page with the backend ?
My HTML:
<body>
<div class="login-container">
<div class="login-box">
<h1 class="login-header">LOGIN</h1>
<div class="login-input-container">
<form action="http://localhost:8080/login" method="POST">
<div class="login-input-container-email-wrapper">
<input type="text" class="login-input-email" placeholder="Email" autocorrect="off" autocomplete="off" required/>
<svg class="login-input-email-icon">
<use xlink:href="img/sprite.svg#icon-mail_outline"></use>
</svg>
</div>
<div class="login-input-container-password-wrapper">
<input type="password" class="login-input-password" placeholder="Password" required/>
<svg class="login-input-password-icon">
<use xlink:href="img/sprite.svg#icon-vpn_key"></use>
</svg>
</div>
<div class="login-submit">
<button class="login-submit-button login-submit-button--green">Submit</button>
</div>
</form>
<span class="login-separator">Or Login With</span>
<div class="login-google">
<div class="g-signin2" data-onsuccess="onSignIn" id="my-signin2"
style="display:flex;justify-content:stretch;align-items: stretch;width: 500;" data-longtitle="true"></div>
</div>
</div>
</div>
</div>
</body>
MY Springboot security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("http://127.0.0.1:9000/login.html").permitAll()
.defaultSuccessUrl("http://127.0.0.1:9000")
.permitAll();
}
You need to write a login controller with a rest end point where you verify credentials and you need to map that in configure() method. Not the front-end url. From front-end you should call that rest end point. Modify the configure() as below.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.defaultSuccessUrl("http://127.0.0.1:9000")
.permitAll();
}
And the login controller should be like
#RestController
public class LoginController {
#CrossOrigin
#RequestMapping(value = "/login"", method = RequestMethod.POST)
#ResponseBody
public User getUserByDetails(#RequestBody Map<String, String> credentials, HttpServletResponse response)
throws IOException {
// business logic
}
}
You can refer https://www.baeldung.com/spring-security-login-angular for further details.
Do refer below config and modify your config file as required!
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") // Specifies the login page URL (goes to controller to look for this URL)
//.loginProcessingUrl("/login") // Specifies the URL,which is used in action on the <from> tag
//.successHandler(successHandler)
.defaultSuccessUrl("http://127.0.0.1:9000")
.and()
.logout()
.logoutUrl("/logout");
// Following is to go to direct controller URL for access denied.
// exceptionHandling().accessDeniedPage("/accessDenied");
}
Here, you must understand the difference between loginPage() and loginProcessingUrl().
Now your controller should have mapping for /login
So, your controller will have method to call your custom-login-page with below config
#GetMapping("/login")
public String customLoginPage() {
return "custom-login-page";
}

Spring Security getting Acess Denied with Custom Login page

I have created one Inmemory user. Initially I tried with default formLogin. After successful login calling another api using defaultSuccessUrl till here everything fine. Now I have implemented one custom login html page when I try to login with both correct and wrong credentials getting access denied and redirecting login page again.
Anyone please help me.
Security config:
#Configuration
#EnableWebSecurity
public class SecurityConfige extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/","index","/courses","login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/courses", true);
}
#Override
#Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
UserDetails admin= User.builder()
.username("nithin")
.password(passwordEncoder.encode("nithin"))
.roles("ADMIN")
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN"))
.build();
return new InMemoryUserDetailsManager(admin);
}
}
Html Login page:
<html lang="en">
</head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/login">
<h2 class="form-signin-heading">Please login to Nithincode</h2>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required=""
autofocus="">
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password"
required="">
</p>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
</div>
</body>
</html>
Login Api:
#GetMapping("login")
public String getLoginView() {
return "login";
}
I think that you are not configuring the user details service correctly and your user is not found (for which Spring shows bad credentials). Maybe try to change your configuration a bit:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/","index","/courses","login").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/courses", true);
}
#Bean
public UserDetailsService myService() throws Exception {
UserDetails admin= User.builder()
.username("nithin")
.password(new BCryptPasswordEncoder().encode("nithin"))
.roles("ADMIN")
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN"))
.build();
return new InMemoryUserDetailsManager(admin);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(myService).passwordEncoder(
new BCryptPasswordEncoder());
}
}

Spring Security - Invalid CSRF token at /logout

I have an app that uses Thymeleaf templates and Spring Boot configuration. I am having a Invalid CSRF token problem meanwhile using some of the endpoints. App stores a CSRF token at cookies.
At one of the templates, where the logout button is, when I do not include any links (stylesheet) and scripts (JS) at , everything is working well. But when I include any or headers, I have a "Invalid CSRF token for localhost:/logout" problem.
Spring security configuration:
#Configuration
#EnableWebSecurity
#Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(authenticationProvider())
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.csrfTokenRepository(new CookieCsrfTokenRepository())
// .disable()
.and()
.exceptionHandling()
.accessDeniedPage("/dashboard")
.and()
.httpBasic()
.disable()
.addFilterBefore(customFilter(), OAuth2LoginAuthenticationFilter.class)
.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/home", "/webjars/**", "/static/**", "/css/**", "/js/**")
.permitAll()
.antMatchers("/",
"/home",
"/webjars/**",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/logon","/login/**", "/oauth2_register/**","/register/**")
.anonymous()
.anyRequest()
.authenticated()
.and()
.formLogin()
.disable()
.logout()
.deleteCookies("access_token")
.permitAll()
.and()
.oauth2Login()
.authorizationEndpoint()
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.authorizationRequestResolver(customAuthorizationRequestResolver())
.and()
.loginPage("/login")
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler());
}
}
index.html where I use the default "/logout" endpoint provided by Spring Security:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="'Welcome '+${user.getUsername()}+'!'"></title>
<!-- When I include the part with Bootstrap, an Invalid CSRF token error is thrown at Spring -->
<!--Bootstrap-->
<!--<link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css"/>-->
<!--<link rel="stylesheet" href="/webjars/bootstrap-social/bootstrap-social.css"/>-->
<!--<link rel="stylesheet" href="/webjars/font-awesome/css/all.css"/>-->
<!--<script src="/webjars/jquery/jquery.min.js"></script>-->
<!--<script src="/webjars/popper.js/popper.min.js"></script>-->
<!--<script src="/webjars/bootstrap/js/bootstrap.min.js"></script>-->
<!--<script th:src="#{js/fade.js}"></script>-->
<!--Bootstrap-->
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="alert alert-danger" role="alert" id="backend-validation-alert" th:if="${errorMessage}">
<span th:text="${errorMessage}"></span>
<script>fade();</script>
</div>
</div>
<h1 th:text="'Welcome '+${user.getUsername()}+'!'"></h1>
<form role="form" th:action="#{/logout}" method="POST">
<div><input type="submit" value="Log out"/></div>
</form>
<form role="form" th:action="#{/user/settings/delete}" method="POST">
<div><input type="submit" value="Delete account"/></div>
</form>
</div>
</body>
</html>
When I debugged the code and checked CsrfFilter, I see that a different hidden _csrf parameters are created at the Thymeleaf template for the POST forms, which doesn't matches the CSRF at the cookie and an exception is thrown. Filter is taking the right XSRF-TOKEN value as the token to be validated, but the CSRF token stored as a parameter in the request is wrong and different.
Did anyone experienced a similar problem? When exactly a new CSRF token should be generated? Because in my case, when I check the network information, I see that a new CSRF token is generated for every single resource (.js, .css, .html) after login, but only the .html CSRF token is stored as hidden parameter at Thymeleaf, which results in Invalid CSRF token error.
Additional note: I have observed that first the initial html is being loaded and correct CSRF form parameters are both set at HTML hidden inputs and as a cookie. But then, JS and CSS and other files are going into CSRF authentication and resulting in changing of cookie CSRF token. I guess solution will include somehow excluding these header scripts and links from authentication.

Spring Boot Thymeleaf variables blocking login page

I'm making a webshop in Spring Boot 2.1.1, with ThymeLeaf 3.0.11
My login page does not appear in every cases, everytime it wants to load it gives a "TemplateInputException" and fails to show up.
I figured it out, if I delete thymeleaf variables from the body or div tags (I mean th:with attributes), then it works until it reaches the next html tag with TL variables, after that, the html page just stops to render. What could be the problem? There is no scenario where i dont use those variables, I need them in the container tag. What is the relation between the Spring Boot login page and template variables?
I copy some code, if you need any more, please let me know.
Any help would be appreciated!
Here is my Webconfig:
#Configuration
public class WebConfig implements WebMvcConfigurer{
#Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/login").setViewName("auth/login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
My security config's configure method:
#Override
public void configure (HttpSecurity httpSec)throws Exception {
httpSec
.authorizeRequests()
.antMatchers("/", "/reg", "/login", "/css/**","/images/**",
"/js/**", "/register", "/error",
"/records", "/search", "/record", "/altercart",
"/showcart", "/category", "/viewchange",
"/images").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login?logout")
.permitAll();
}
My login page(using layout dialect):
<div layout:fragment="content">
<div>
<div th:if="${param.error}" th:text="#{login.error.wrongusernameorpassword}" class="col-12 error-message"/>
<div th:if="${param.logout}" th:text="#{logoutSuccess}" class="col-12 success-message"/>
<p th:text="#{logingreetings}" class="col-12"/>
<form method="post" th:action="#{login}">
<input type="text" name="username" th:placeholder="#{login.ph.username}" required class="col-12"/>
<br />
<input type="password" name="password" th:placeholder="#{login.ph.password}" required class="col-12"/>
<br />
<input type="submit" th:value="#{loginSubmitButton}" class="col-12"/>
<br /><br />
</form>
<br />
<a class="col-12 anchor" th:href="#{register}" th:text="#{misc.registration}">Registration</a>
</div>
</div>
Beggining of stack trace:
org.thymeleaf.exceptions.TemplateInputException: An error happened during
template parsing (template: "class path resource
[templates/auth/login.html]"

Resources