Spring: Error Button Action on Form after add registration (Spring Security) - spring

I do an example with login page - Spring Security.
Shortly:
There's root page ("localhost:8080/") - here's a link on the main page.
Click a link on the main page go to main.html(localhost:8080/main/
If User doesn't authorize he is redirected to login page
When the user authorizes the main page is opened
The main page show messages and filter by tag
I enter a tag in input and push the button Search(Найти), messages are filtered by tag
When I have added authorization filter has stopped work.
This is my source code:
root page - have link on Main page
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Gretting start: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div>Hello, user</div>
<a th:href="#{/main}">Main page</a>
</body>
</html>
Main page
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div>
<form method="post">
<input type="text" name="text" placeholder="Введите сообщение" />
<input type="text" name="tag" placeholder="Тэг">
<button type="submit">Добавить</button>
</form>
</div>
<div>Список сообщений</div>
<form method="post" action="filter">
<input type="text" name="filter">
<button type="submit">Найти</button>
</form>
<div th:each = "message : ${messages}">
<b th:text = "${message.id}"></b>
<span th:text = "${message.text}"></span>
<i th:text = "${message.tag}"></i>
</div>
</body>
</html>
Controller processes all mapping
package com.example.sweater;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.sweater.domain.Message;
import com.example.sweater.repos.MessageRepo;
#Controller
public class GreetingController {
#Autowired
private MessageRepo messageRepo;
#GetMapping("/")
public String greeting(Model model) {
return "greeting";
}
#GetMapping("/main")
public String main(Model model) {
Iterable<Message> messages = messageRepo.findAll();
model.addAttribute("messages", messages);
return "main";
}
#PostMapping("/main")
public String add(#RequestParam String text, #RequestParam String tag, Model model) {
Message message = new Message(text, tag);
messageRepo.save(message);
Iterable<Message> messages = messageRepo.findAll();
model.addAttribute("messages", messages);
return "main";
}
#PostMapping("/filter")
public String filter(#RequestParam String filter, Model model) {
Iterable<Message> messages;
if (filter != null && !filter.isEmpty()) {
messages = messageRepo.findByTag(filter);
} else {
messages = messageRepo.findAll();
}
model.addAttribute("messages", messages);
return "main";
}
}
WebSecurityConfig have one In Memory User. antMathcers("/") permitAll and anyRequest authenticated
package com.example.sweater.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("u")
.password("p")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
My screenshots
root page
Main page
When I enter filter tag and push Button "Найти"(= Search), I've got an error:
#PostMapping("/filter") doesn't catch the action in the form. I checked in the debugger. I can't catch an error and don't know why does this happen.
I have GitHub repository: https://github.com/aalopatin/sweater
Commit with the comment "Add messages" - filter work
Commit with the comment "Add remote repository and Login" - filter doesn't work and add login

I found solving. In a form on the main page need to add 'th' attribute because I use Thymeleaf so template engine. It's needed for _csrf defending which auto insert token in a form if you use Thymeleaf:
<form method="post" th:action="filter">
<input type="text" name="filter">
<button type="submit">Найти</button>
</form>

Related

Spring boot spring security app reloads login page instead of succeeding or failing

I tried replacing the spring security login screen with my custom one. While the default one works perfectly my own one doesn't seem to react to posting the form. It just reloads the login screen.
login.mustache:
{{> partials/header}}
<body class="grey lighten-4">
<div class="container">
<form action="/login" method="POST">
<div class="md-form">
<input type="text" name="username" id="loginField" class="form-control">
<label for="loginField">Login</label>
</div>
<div class="md-form">
<input type="password" name="password" id="passwordField" class="form-control">
<label for="passwordField">Password</label>
</div>
<button class="btn red text-white float-right" type="submit">Log in</button>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</div>
</body>
{{> partials/footer}}
LoginController.kt
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.ui.Model
import org.springframework.ui.set
#Controller
class LoginController {
#GetMapping("/login")
fun loginPage(model: Model): String {
return "login"
}
/*
#PostMapping("/login")
fun loginForm() {
print("post")
}*/
}
It doesn't even trigger the breakpoint in the currently commented part when posting the form.
SecurityConfig.kt
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.userdetails.User
import org.springframework.security.provisioning.InMemoryUserDetailsManager
#Configuration
#EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/test")
.permitAll()
}
override fun configure(auth: AuthenticationManagerBuilder) {
auth.userDetailsService(
InMemoryUserDetailsManager(
User.withUsername("user").password("user").authorities(mutableListOf()).build()
)
)
}
}
I suspect I'm missing some crucial part to receiving and processing the contents of the form correctly. However after going through like 10 tutorials on this issue I've found so many inconsistencies between them that I'm honestly lost.
It seems you are using incorrect mustache syntax for the csrf parameter.
You need the {{ to render the context variable.
<input type="hidden" name="{{_csrf.parameterName}}" value="{{_csrf.token}}"/>
Additionally, you need to copy the "_csrf" object to the MVC model by adding this setting to you application.properties.
spring.mustache.expose-request-attributes=true
Finally, you may see an IllegalArgumentException because you haven't specified a password encoder. If that occurs, you will see the error in your stacktrace. It can be fixed by specifying a PasswordEncoder.

Unable to Run the following Spring Boot Application that uses JDBC Template

I have created a simple Spring Boot Application that adds Dog information into the MySql database.
The controller class for this Application is DogController.java
package com.dog.resue.controller;
import com.dog.resue.dao.DodRepository;
import com.dog.resue.service.DogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Date;
#Controller
#RequestMapping(path="/")
public class DogController {
#Autowired
private DodRepository dodRepository;
#Autowired
private DogService dogService;
#RequestMapping(value ="/home",method = {RequestMethod.POST,RequestMethod.GET})
public String adddog(#RequestParam("name") String name,
#RequestParam("rescued") #DateTimeFormat(pattern = "yyyy-MM-dd") Date rescued,
#RequestParam("vaccinated") Boolean vaccinated, Model model)
{
dogService.addADog(name, rescued, vaccinated);
System.out.println("name = " + name + ",rescued = " + rescued + ", vaccinated = " + vaccinated);
return "index";
}
}
and the corresponding Service class is
package com.dog.resue.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.Date;
#Service
public class DogServiceImpl implements DogService {
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate=new JdbcTemplate(dataSource);
}
#Override
public void addADog(String name, Date rescued, Boolean vaccinated) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO dog(name,rescued,vaccinated) VALUES(?,?,?)",name,rescued,vaccinated );
}
}
And the thymeleaf HTML File is
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<!-- META SECTION -->
<title>Dog Rescue</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- END META SECTION -->
<!-- BEGIN STYLE -->
<style>
table, th, td {
border: 1px solid black;
padding: 1px;
}
</style>
<!-- END STYLE -->
</head>
<body>
<h2>Current Dogs In Rescue</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Rescue Date</th>
<th>Vaccinated</th>
</tr>
</thead>
<tbody>
<tr th:each="dogs : ${dogs}">
<td th:text="${dogs.id}">Text ...</td>
<td th:text="${dogs.name}">Text ...</td>
<td th:text="${dogs.rescued}">Text ...</td>
<td th:text="${dogs.vaccinated}">Text...</td>
</tr>
</tbody>
</table>
</div>
<h2>Add A Dog</h2>
<form action="#" th:action="#{/home}" >
<label>Name<input type="text" name="name" id="name"></input></label>
<label>Vaccinated<input type="text" name="vaccinated" id="vaccinated"></input></label>
<label>Rescued<input type="text" name="rescued" id="rescued"></input></label>
<input type="submit" value="Submit"></input>
</form>
</body>
</html>
While running this code i am getting following Error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Jul 22 21:50:32 IST 2018
There was an unexpected error (type=Bad Request, status=400).
Required String parameter 'name' is not present
url is http://localhost:8080/home
Kindly help me to solve this issues
Your Request parameters are missing in URL( name,rescued,vaccinated)
Your url should be
http://localhost:8080/home?name=ARULSUJU&rescued=2012-12-12&vaccinated=true
because all the parameters are required
Looking at your controller
Why you have rescued as Date Type you can change it as String.
So your controller will be
#RequestMapping(value ="/home",method = {RequestMethod.POST,RequestMethod.GET})
public String adddog(#RequestParam("name") String name,
#RequestParam("rescued") String rescued,
#RequestParam("vaccinated") Boolean vaccinated, Model model)
{
dogService.addADog(name, rescued, vaccinated);
System.out.println("name = " + name + ",rescued = " + rescued + ", vaccinated = " + vaccinated);
return "index";
}
Now try this URL
http://localhost:8080/home?name=test&rescued=2014-12-12&vaccinated=true
in your thymeleaf html file add this xmlns :
xmlns:th="http://www.thymeleaf.org"

inMemoryAuthentication with Spring Boot

I've generated a Spring Boot web application using Spring Initializer, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.
Technologies used:
Spring Boot 1.4.2.RELEASE, Spring 4.3.4.RELEASE, Thymeleaf 2.1.5.RELEASE, Tomcat Embed 8.5.6, Maven 3, Java 8
This is my security config class:
#Configuration
#EnableWebSecurity
#PropertySource("classpath:/com/tdk/iot/config/app-${APP-KEY}.properties")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${securityConfig.formLogin.loginPage}")
private String loginPage;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage(loginPage)
.permitAll()
.loginProcessingUrl("/login")
.failureUrl("/login.html?error=true")
.defaultSuccessUrl("/books/list")
.and()
.exceptionHandling()
.accessDeniedPage("/denied")
.and()
.authorizeRequests()
.antMatchers("/mockup/**").permitAll()
.antMatchers("/books/**").permitAll()
.antMatchers("/welcome/**").authenticated()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/index.html");
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(new StandardPasswordEncoder())
.withUser("test1").password("test1").roles("ADMIN").and()
.withUser("test2").password("test2").roles("USER").and()
.withUser("test3").password("test3").roles("SUPERADMIN");
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertyDefaultConfig() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here the LoginController
#Controller
public class LoginController {
#RequestMapping(value={ "/", "/tdk/login"}, method = { RequestMethod.POST,RequestMethod.GET})
public String welcome(Map<String, Object> model) {
return "tdk/login";
}
}
and the template:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div class="wrap">
<div class="login">
<div class="logo"></div>
<form th:action="#{/login.html}" method="post">
<p th:if="${loginError}" class="error">Wrong user or password</p>
<div class="input_label"><i class="fa fa-user"></i><input type="text" name="user" placeholder="User" /></div>
<div class="input_label"><i class="fa fa-key"></i><input type="password" name="pass" placeholder="Password" /></div>
<input type="submit" value="LOGIN" />
</form>
<div class="forget">
<!-- Do you forgot your password?<br/> -->
<br/>
</div>
</div>
</div>
</body>
</html>
but when I access with test1 / test1 I got this error:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Mar 05 20:16:11 CET 2017
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
Your login page calls /login.html with HTTP POST, but your server doesn't provide such a request mapping.
The configured URL in your Spring Security configuration:
.loginProcessingUrl("/login")
is not matching the URL in your login page:
<form th:action="#{/login.html}" method="post">
See also AbstractAuthenticationFilterConfigurer#loginProcessingUrl:
Specifies the URL to validate the credentials.
try this code
.failureUrl("/tdk/login?error=true")
Controller
#Controller
public class LoginController {
#RequestMapping(value={ "/", "/tdk/login"},params = {"error"},method=RequestMethod.POST)
public String welcome(#RequestParam(value = "error", required = false) int error , ModelMap model) {
if (error == 1) {
model.addAttribute("msg", "Invalid Username or Password");
return "tdk/login";
}
else{
return "redirect:home";
}
}
}
Default method controller for #RequestMapping is GET, not POST.
You need to specify the method on the #requestMapping.
#RequestMapping(value={ "/", "/tdk/login"}, method = RequestMethod.POST)

Why can't I go to other Controller rather than HomeController in Spring framework?

I created a Spring project with STS, using MySql with MyBatis.
I'm trying to make a function for Members to Sign up, but it only keep visiting Homecontroller.java instead of MemberController, when I click the submit button.
This is the arrangement of my files and codes.
This is from index.jsp
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<P> The time on the server is ${serverTime}. </P>
<h1> This is the first page to Log In</h1>
<form id="form1" action="members/logIn">
LogIn <p>
ID: <input type="text" name="id" id="id">
Password: <input type="password" name="pwd" id="pwd">
<input type="button" onclick="logIn()" value="press to LogIn">
</form>
<p><br><br><h3>Move to SignUp page</h3>
<input type="button" onclick="window.location='views/signUp.jsp'" value="Move to Sign Up page">
<h1> This is to Sign Up </h1>
<form id="form2" action="members/join">
ID:
<input type="text" placeholder="Insert ID">
Name:
<input type="text" placeholder="Insert Name"> <p>
PWD:
<input type="text" placeholder="Insert your PWD"> <p>
PWD Check:
<input type="text" placeholder="Confirm your PWD"><p>
Address:
<input type="text" placeholder="Insert Address."> <p>
<input type="submit" value="Sign Up">
</form>
</body>
</html>
and HomeController.java
package kr.co.promptech.controller;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#RequestMapping(value = "", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
System.out.println("Arrived at HomeController.");
return "index";
}
}
MemberController.java
package kr.co.promptech.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import kr.co.promptech.model.Members;
import kr.co.promptech.service.MemberService;
#Controller
#RequestMapping(value="/members")
public class MemberController {
#Autowired
private MemberService memberService;
#RequestMapping(value="join")
public String memberJoin(#RequestParam("id") String id, #RequestParam("pwd") String pwd,
#RequestParam("name") String name, #RequestParam("address") String address){
System.out.println("We have arrived at MemberController");
memberService.memberJoin(new Members(id, pwd, name, address));
return "main";
}
}
Why the Sign Up button keep brings me to HomeController instead MemberController?
also, can anyone help me how to make a button that brings me to main.jsp from index.jsp?
this is not working...
Hope someone can help me here...
OMG, I just found out that all the tags of index.jsp did not have name properties.
It was all my faults... sorry, and Thanks for your answer,

Spring Security 4 : Request method 'POST' not supported

The issue is that when I click on submit I get
Etat HTTP 405 - Request method 'POST' not supported
Here is the controller :
#RequestMapping(value = "/admin/login", method = RequestMethod.GET)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
LOGGER.debug("admin login page");
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("/admin/index");
LOGGER.debug("returning admin login page");
return model;
}
And the form :
<form class="m-t" role="form" th:action="#{/admin/login}" method="POST" autocomplete="off">
<div th:if="${param.error}" class="alert alert-danger">Invalid username and password.</div>
<div th:if="${param.logout}" class="alert alert-success">You have been logged out.</div>
<div class="form-group">
<input type="text" class="form-control" id="username" name="username" placeholder="Username" required="" />
</div>
<div class="form-group">
<input type="password" class="form-control" id="username" name="username" placeholder="Username" required="" />
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn btn-primary block full-width m-b">Login</button>
<small>Forgot password?</small>
<p class="text-muted text-center">
<small>Do not have an account?</small>
</p>
<a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>
</form>
It seems like csrf field not working.
I explain, I have normal users website which I refer here by and admin part which is /admin
The login form is correctly displayed. But I when I click submit I get Etat HTTP 405 - Request method 'POST' not supported
Any idea please ?
Here is my security configuration class :
package com.mintad.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* #author Sofiane HAMMAMI
*/
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").permitAll()
// .antMatchers("/admin/**").access("hasRole('ADMIN')")
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
}
}
In controller , you defined the form as GET:
#RequestMapping(value = "/admin/login", method = RequestMethod.GET)
and in the form, you are calling it as POST:
form class="m-t" role="form" th:action="#{/admin/login}" method="POST"
change one of these, either at controller or in the form.
If you want to change at controller, replace existing line with:
#RequestMapping(value = "/admin/login", method = RequestMethod.POST)
or you can do it by modifying call from form like
form class="m-t" role="form" th:action="#{/admin/login}" method="GET"
Change your security config to use /admin/login as your login page:
...
.formLogin().loginPage("/admin/login")
Reason you get 405 is, you are trying to submit your form with http post method and defined the end point with http get method.
You need to change your request mapping to method as POST like:
#RequestMapping(value = "/admin/login", method = RequestMethod.POST)
OR you could do vice versa (which is not recommend by security reasons) in your form like:
<form class="m-t" role="form" th:action="#{/admin/login}" method="GET" autocomplete="off">

Resources