BindingResult always false Spring MVC - spring

I'm trying to implement spring validation framework in my mvc application. My login page has user name and password which are mandatory. I defined them as not null and not empty in the model class. If they are left blank I expect spring to bind the errors to BindingResult object. I'm not sure where I'm doing it wrong.. the hasError() method of BindingResult always return false in my controller class.
Below is my login controller
public class LoginController {
#Autowired
private UserRoleService userRoleService;
#Autowired
private AuthenticationService authenticationService;
#RequestMapping(value = "/login", method=RequestMethod.GET)
public ModelAndView showLoginPage() {
System.out.println("coming into LoginController!!");
ModelAndView mav = new ModelAndView("login");
mav.addObject("userModel", new UserInfo());
return mav;
}
#RequestMapping(value = "/welcome", method=RequestMethod.POST)
public ModelAndView login(#Valid #ModelAttribute("userModel") UserInfo user, BindingResult bindingResult) {
System.out.println("authenticate username and password -- invoke LDAP!!");
System.out.println(user.getUsername() + user.getPassword());
String userId = user.getUsername();
String password = user.getPassword();
if(bindingResult.hasErrors())
{
return new ModelAndView("login");
}
if(authenticationService.athenticate(userId,password)) {
List<String> roles = userRoleService.searchRolesByUserId(userId);
for(String role : roles)
System.out.println("role is: "+role);
ModelAndView mav = new ModelAndView("welcome");
return mav;
}
else {
ModelAndView mav = new ModelAndView("login");
mav.addObject("loginFailed", "User Name or Password is incorrect");
return mav;
}
}
}
Below is my model class..
public class UserInfo{
#NotNull
#NotEmpty(message="User name cannot be empty")
private String username;
#NotNull
#NotEmpty(message="Password cannot be empty")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
My spring xml has these entries...
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"/>
<mvc:annotation-driven />
Below is my login jsp page..
<form:form class="form-signin" commandName="userModel" method="POST" action="welcome.do" id="loginform">
<h2 class="form-signin-heading">Welcome to BPAY</h2>
<div style="color: red">${loginFailed}</div>
<label for="inputEmail" class="sr-only">Username</label>
<form:input type="text" path="username" id="usernameId" class="form-control"
placeholder="Username" required autofocus/>
<form:errors path="username" cssClass="error"/>
<label for="inputPassword" class="sr-only">Password</label>
<form:input type="password" id="passwordId" path="password" class="form-control"
placeholder="Password" required/>
<form:errors path="password" cssClass="error"/>
<button class="btn btn-lg btn-primary btn-block" type="submit" value="OK">Login</button>
</form:form>
The bindingResult.hasErrors() method in my controller class always return false. Can you please let me know, what am I missing?
pom has required entries as below..
<!-- Validation framework -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.3.5.Final</version>
</dependency>

You should use this dependency
`<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.10.Final</version>
</dependency>`
And remove the dependency javax.validation
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>

Try in pom.xml
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

Try using spring-boot-starter-validation:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Related

Why my Spring boot application does not validate form

I have a bit of a problem. So I'm trying to make a form validation. I enter incorrect data and it doesn't show any problems and saves user even it should not do that. If I don't select any country, it crashes becouse it gets null. Binding result method .getAllErrors() shows nothing. The question is why binding result doesn't get any errors and validating all the fields even if they are incorrect?
What I'm missing?
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.0.Final</version>
<type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
#Entity
#Table(name="users")
public class User {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "country_id", referencedColumnName = "id")
#NotBlank(message = "Country is mandatory")
#NotNull
private Country country;
#Column(name = "password")
#Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[##$%^&-+=()])(?=\\\\S+$).{8,20}$",
message = "At least one digit, upper and lower case letters at least once, special character, 8 to 20 characters and no white spaces")
#NotBlank(message = "Password is mandatory")
private String password;
#Column(name = "first_name")
#Size(min = 2, max= 20, message = "Not less then 2")
#NotBlank(message = "First name is mandatory")
private String firstName;
#Column(name = "last_name")
#Size(min = 2, max= 20, message = "Not less then 2")
#NotBlank(message = "Last name is mandatory")
private String lastName;
#Column(name = "mail")
#Email(message = "Enter valid email")
#NotBlank(message = "Email is mandatory")
private String mail;
#Column(name = "phone")
#Pattern(regexp = "^(\\+\\d{1,2}\\s)?\\(?\\d{3}\\)?[\\s.-]\\d{3}[\\s.-]\\d{4}$",
message = "Enter valid phone like:\n123-456-7890\n(123) 456-7890\n123 456 7890\n123.456.7890\n+91 (123) 456-7890")
#NotBlank(message = "Phone is mandatory")
private String phone;
#Column(name = "last_login_date")
private String lastLoginDate;
#Column(name = "register_date")
private String registerDate;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(name = "users_id"),
inverseJoinColumns = #JoinColumn(name = "roles_id")
)
#NotNull
private Set<Roles> roles = new HashSet<>();
#Controller
public class LoginController {
#Autowired
private UserService userService;
#Autowired
CountryRepository countryRepo;
#Autowired
UserRepository userRepo;
#GetMapping("/login")
public String openLogin(Model theModel) {
User user = new User();
List<Country> countryList = new ArrayList<Country>();
countryList = countryRepo.findAll();
theModel.addAttribute("user", user);
theModel.addAttribute("country_list", countryList);
return "login";
}
#PostMapping("/register")
public String registerUser(#Valid #ModelAttribute User user,
HttpServletRequest request, BindingResult result) {
if(result.hasErrors()) {
return "login";
} else {
String userRole = request.getParameter("role_value");
String selectedCountry= request.getParameter("country_value");
userService.setUserCountry(selectedCountry);
userService.setUserRole(userRole);
userService.saveUserWithRole(user);
return "redirect:/";
}
}
}
#Service
public class UserService {
private String userCountry;
private String userRole;
#Autowired
private UserRepository userRepo;
#Autowired
private RoleRepository roleRepo;
#Autowired
private CountryRepository countryRepo;
public void saveUserWithRole(User user) {
Country userCountry = countryRepo.findById(getUserCountry(this.userCountry));
user.addCountry(userCountry);
Roles userRole = roleRepo.findUsingId(getUserRole(this.userRole));
user.addRole(userRole);
userRepo.save(user);
}
public void setUserCountry(String userCountry) {
this.userCountry = userCountry;
}
public Long getUserCountry(String userCountry) {
return Long.parseLong(userCountry);
}
public void setUserRole(String userRole) {
this.userRole = userRole;
}
public Long getUserRole(String userRole) {
return Long.parseLong(userRole);
}
}
<form th:action="#{/register}" th:object="${user}" id="register-form-user"
method="post" style="display: none">
<input type="text" id="firstName" th:field="*{firstName}" name="firstName" th:errorclass="is-invalid" placeholder="Your name">
<span th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">Error</span>
<input type="text" id="last-name" th:field="*{lastName}" name="last-name" th:errorclass="is-invalid" placeholder="Your last name">
<span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">Error</span>
<input type="password" id="password" th:field="*{password}" name="password" th:errorclass="is-invalid" placeholder="Password">
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Error</span>
<input type="password" id="rep-password" name="rep-password" placeholder="Repeat password">
<input type="text" id="mail" th:field="*{mail}" name="mail" th:errorclass="is-invalid" placeholder="Mail">
<span th:if="${#fields.hasErrors('mail')}" th:errors="*{mail}">Error</span>
<input type="text" id="phone" th:field="*{phone}" name="phone" th:errorclass="is-invalid" placeholder="Phone">
<span th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}">Error</span>
<select class="form-control" name="role_value" id="role-name">
<option th:value="0">Select role</option>
<!-- Role options -->
</select>
<span th:if="${#fields.hasErrors('roles')}" th:errors="*{roles}">Error</span>
<select class="form-control" name="country_value" id="country-name">
<option value="0">Select country</option>
<option th:each="country : ${country_list}" th:value="${country.id}" th:text="${country.countryName}"></option>
</select>
<span th:if="${#fields.hasErrors('country')}" th:errors="*{country}">Error</span>
<button class="btn-form">REGISTER</button>
</form>
Thanks for help.
The BindingResult needs to follow immediately after the model attribute you want to validate. Change:
#PostMapping("/register")
public String registerUser(#Valid #ModelAttribute User user,
HttpServletRequest request, BindingResult result)
to
#PostMapping("/register")
public String registerUser(#Valid #ModelAttribute User user,
BindingResult result, HttpServletRequest request)

Spring Boot, Can't reach a different Controller Class

Request is always made to method(/login) in same Controller.
When you press Save button in Register.html, /login method in UserController.java class works automatically although it must run saveRegisterPage method (/registration/saveRegister) in RegisterController.java class.
What must be done in order to run saveRegisterPage method?
Thank you.
UserController.java
#Controller
public class UserController {
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String registerPage(Model model) {
model.addAttribute("user", new User());
return "register";
}
#RequestMapping("/")
public String index() {
return "login";
}
#RequestMapping("/login")
public String login() {
return "login";
}
#RequestMapping("/home")
public String home() {
return "home";
}
}
RegisterController.java
#Controller
#RequestMapping(value="/registration")
public class RegisterController {
#Autowired
UserServiceImpl userServiceImpl;
#RequestMapping(value = "/saveRegister", method = RequestMethod.POST)
public String saveRegisterPage(#ModelAttribute("user") User user, BindingResult result, Model model, RedirectAttributes attributes) {
model.addAttribute("user", user);
if (result.hasErrors()) {
return "register";
} else {
userServiceImpl.save(user);
}
return "home";
}
}
home.html
<html>
<head>...</head>
<body>
<div class="container">
<h2>Welcome</h2>
Logout
</div>
</body>
</html>
login.html
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>...</head>
<body>
<div class="container">
<div class="alert alert-danger" role="alert" th:if="${param.error}">
Wrong Username or Passrod
</div>
<form th:action="#{/login}" method="post">
<h1 class="h3 mb-3 font-weight-normal">Signin</h1>
<label>Username</label>
<input type="text" name="username" class="form-control"/>
<label>Password</label>
<input type="password" id="password" name="password" class="form-control"/>
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">SignIn</button>
<br/>
<div class="margin-top20 text-center"><a th:href="#{/register}">Go Register</a>
</div>
</form>
</div>
</body>
</html>
register.html
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>....</head>
<body>
<div class="container">
<form autocomplete="off" th:action="#{/registration/saveRegister}" th:object="${user}" method="post">
<label>Name </label>
<input type="text" id="firstName" placeholder="First Name" th:field="*{firstName}"
class="form-control"/><br/>
<label>Last name </label>
<input type="text" id="lastName" placeholder="First Name" th:field="*{lastName}" class="form-control"/><br/>
<label>Email </label>
<input type="text" id="email" placeholder="First Name" th:field="*{email}" class="form-control"/><br/>
<label>Username</label>
<input type="text" id="username" placeholder=" Username" th:field="*{username}" class="form-control"/><br/>
<label>Password</label>
<input type="text" id="password" placeholder=" Password" th:field="*{password}" class="form-control"/><br/>
<button type="submit" class="btn btn-success text-center form-control">Save</button>
</form>
</div>
</body>
</html>
SecurityConfiguration.java
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private UserAuthService userAuthService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/", "/register", "/webjars/**", "/h2-console/**","/webjars/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/home", true).permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/home").permitAll()
.and()
.httpBasic();
http.headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userAuthService).passwordEncoder(encoder());
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zorogluresul.springexample1</groupId>
<artifactId>springexample1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springexample1</name>
<description>Spring Boot, Web, Thymeleaf, Jpa, Postgresql, Security</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7-1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1-1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
You should add RegisterController's requestMapping /registration into antMatchers like this:
.antMatchers("/", "/register", "/registration/*", "/webjars/**", "/h2-console/**","/webjars/").permitAll()
It contains /* at the end of the /registration/* in order to allow all requests in RegisterController. If you write it like /registration/register, it also runs saveRegisterPage method in RegisterController.
Try this Resul
import java.io.IOException;
import java.util.List;
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.RequestMapping;
#Controller
#RequestMapping("/")
public class UserController {
#GetMapping("/register")
public String registerPage(Model model) {
model.addAttribute("user", new Fragment3());
return "register";
}
#GetMapping //main page
public String index() {
return "login";
}
#GetMapping("/login") //logging out from home
public String logout() {
return "login";
}
#PostMapping("/home")
public String home() {
return "home";
}
}
---------------------------------------------------------------------------
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.RequestMapping;
#Controller
#RequestMapping("/registration")
public class RegisterController {
#Autowired
UserServiceImpl userServiceImpl;
#PostMapping("/saveRegister")
public String saveRegisterPage(#ModelAttribute("user") User user, BindingResult result, Model model, RedirectAttributes attributes) {
model.addAttribute("user", user);
if (result.hasErrors()) {
return "register";
} else {
userServiceImpl.save(user);
}
return "home";
}
}`
most changes are at the backend
although do replace 2 lines in home.html for the logout.
<h2>Welcome</h2>
<a th:href="#{/login}" class="btn btn-danger">Logout</a>

HTTP Status [404] ? [Not Found] for /login in spring security

I'm having a problem with the customized login form in spring security. I'm new with the spring mvc. As of now I'm having a 404 Not found when clicking the submit button in my login form.
CODE BELOW:
headerindex.jsp
<form class="navbar-form navbar-right" method="post" action="<c:url value="/login" />" id="form_login">
<div class="form-group">
<input type="text" class="form-control" name="username"
placeholder="Username">
</div>
<div class="form-group">
<input type="text" class="form-control" name="password"
placeholder="Password">
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn btn-default">Sign In</button>
</form>
security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<security:authentication-manager>
<security:authentication-provider>
<security:jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username, password, enabled from user where binary username = ?" />
</security:authentication-provider>
</security:authentication-manager>
<security:http use-expressions="true">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/index" access="permitAll" />
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="denyAll" />
<security:form-login authentication-success-forward-url="/home"
login-page="/" password-parameter="password"
username-parameter="username"
authentication-failure-forward-url="/login?error=true" />
<security:logout logout-success-url="/loggedout" />
<security:access-denied-handler error-page="/denied" />
</security:http>
<security:global-method-security secured-annotations="enabled"></security:global-method-security>
</beans>
UsersDao.java
#Component("usersDao")
public class UsersDao {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private SessionFactory sessionFactory;
public Session session() {
return sessionFactory.getCurrentSession();
}
#Transactional
public void create(User user) {
System.out.println(user);
user.setPassword(passwordEncoder.encode(user.getPassword()));
session().save(user);
}
public boolean exists(String username) {
return getUser(username) != null;
}
#SuppressWarnings("unchecked")
public List<User> getAllUsers() {
return session().createQuery("from user").list();
}
public User getUser(String username) {
Criteria criteria = session().createCriteria(User.class);
criteria.add(Restrictions.idEq(username));
return (User)criteria.uniqueResult();
}
#Component("usersDao")
public class UsersDao {
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private SessionFactory sessionFactory;
public Session session() {
return sessionFactory.getCurrentSession();
}
#Transactional
public void create(User user) {
System.out.println(user);
//user.setPassword(passwordEncoder.encode(user.getPassword()));
session().save(user);
}
public boolean exists(String username) {
return getUser(username) != null;
}
#SuppressWarnings("unchecked")
public List<User> getAllUsers() {
return session().createQuery("from user").list();
}
I solved my problem. After checking my DAO codes I found that I was using the PasswordEncoder and I didn't set it in the bean of the security-context.xml. I just commented out the PasswordEncoder and I can now login. :)
UsersDao.java
#Component("usersDao")
public class UsersDao {
//#Autowired
//private PasswordEncoder passwordEncoder;
#Autowired
private SessionFactory sessionFactory;
public Session session() {
return sessionFactory.getCurrentSession();
}
#Transactional
public void create(User user) {
System.out.println(user);
//user.setPassword(passwordEncoder.encode(user.getPassword()));
session().save(user);
}
public boolean exists(String username) {
return getUser(username) != null;
}
#SuppressWarnings("unchecked")
public List<User> getAllUsers() {
return session().createQuery("from user").list();
}
public User getUser(String username) {
Criteria criteria = session().createCriteria(User.class);
criteria.add(Restrictions.idEq(username));
return (User)criteria.uniqueResult();
}

Spring Security with Openid and Database Integration

I am very new to Spring and Spring Security, and hoping someone can help me to solve the following problem.
What I want to achieve is to extract user's username and email address after this user is successfully authenticated by OpenID provider(gmail), then check with the database in order to load the usermodel for this user.
In my spring-security.xml, i have
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<security:authentication-manager alias="openIDAuthenticationManager" />
<bean id="authenticationSuccessHandler" class="org.school.openid.service.YouEatAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/krams/main/common" />
<property name="attributes2UserDetails" ref="openIDAttributes2UserDetails" />
</bean>
<security:http >
<security:anonymous enabled="false" />
<security:logout />
<security:openid-login user-service-ref="userDetailsServiceOpenIDImpl" authentication-success-handler-ref="authenticationSuccessHandler"
login-page="/krams/auth/login" authentication-failure-url="/krams/auth/login?error=true">
<security:attribute-exchange>
<security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />
<security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />
<security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" />
</security:attribute-exchange>
</security:openid-login>
</security:http>
<bean id="openIDAttributes2UserDetails" class="org.school.openid.service.OpenIDAttributes2UserDetailsImpl" />
<bean id="userDetailsServiceOpenIDImpl" class="org.school.openid.service.UserDetailsServiceOpenIDImpl" />
</beans>
my problem is at UserDetailsServiceOpenIDImpl.java is
public class UserDetailsServiceOpenIDImpl implements UserDetailsService {
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
System.out.println(username);
//extract username and email address, HOW?
}
}
The print statement prints out something like
https://www.google.com/accounts/o8/id?id=AItOawlq2C3EdFAuqp-ski_xkgB8jsEKbe-mZE
My questions are
(1) How could I extract the username and the email address from the returned url (also, I am not even sure if the username and email address returned correctly)?
(2) By running the debug on Eclipse, the YouEatAuthenticationSuccessHandler seems not called when the url (https://www.google.com/accounts/o8/id?id=AItOawlq2C3EdFAuqp-ski_xkgB8jsEKbe-mZE) returned.
Thanks.
Edit:
thanks for the link http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html#ns-openid.
It says that "The attribute values are returned as part of the authentication process and can be accessed afterwards using the following code:..."
I have added
OpenIDAuthenticationToken token = (OpenIDAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();
List attributes = token.getAttributes();
into loadUserByUsername method. But the "token" object is null.
Edit 2
By following page https://fisheye.springsource.org/browse/spring-security/samples/openid/src/main/webapp/WEB-INF/applicationContext-security.xml?hb=true, i am able to extra the name and email address for the user.
My spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<security:authentication-manager alias="openIDAuthenticationManager" />
<security:http pattern="/krams/auth/login" security="none"/>
<security:http auto-config="true" access-denied-page="/krams/auth/denied">
<security:intercept-url pattern="/krams/main/*" access="ROLE_USER" />
<security:anonymous enabled="false" />
<security:logout
invalidate-session="true"
logout-success-url="/krams/auth/login"
logout-url="/krams/auth/logout"/>
<security:openid-login
user-service-ref="registeringUserService"
login-page="/krams/auth/login"
authentication-failure-url="/krams/auth/login?error=true"
default-target-url="/krams/main/common">
<security:attribute-exchange identifier-match="https://www.google.com/.*">
<security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />
<security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />
<security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" />
</security:attribute-exchange>
<security:attribute-exchange identifier-match=".*yahoo.com.*">
<security:openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>
<security:openid-attribute name="fullname" type="http://axschema.org/namePerson" required="true" />
</security:attribute-exchange>
</security:openid-login>
<!-- if remember is needed
<security:remember-me token-repository-ref="tokenRepo"/>
-->
</security:http>
<bean id="tokenRepo" class="org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl" />
<!--
A custom UserDetailsService which will allow any user to authenticate and "register" their IDs in an internal map
for use if they return to the site. This is the most common usage pattern for sites which use OpenID.
-->
<bean id="registeringUserService" class="org.school.openid.service.CustomUserDetailsService" />
</beans>
My CustomUserDetailsService.java
public class CustomUserDetailsService implements AuthenticationUserDetailsService {
/*
private final Map registeredUsers = new HashMap();
*/
private static final List DEFAULT_AUTHORITIES = AuthorityUtils.createAuthorityList("ROLE_USER");
protected static Logger logger = Logger.getLogger("service");
/**
* Implementation of {#code AuthenticationUserDetailsService} which allows full access to the submitted
* {#code Authentication} object. Used by the OpenIDAuthenticationProvider.
*/
public UserDetails loadUserDetails(OpenIDAuthenticationToken token) {
String id = token.getIdentityUrl();
String email = null;
String firstName = null;
String lastName = null;
String fullName = null;
List attributes = token.getAttributes();
for (OpenIDAttribute attribute : attributes) {
if (attribute.getName().equals("email")) {
email = attribute.getValues().get(0);
}
if (attribute.getName().equals("firstName")) {
firstName = attribute.getValues().get(0);
}
if (attribute.getName().equals("lastName")) {
lastName = attribute.getValues().get(0);
}
if (attribute.getName().equals("fullname")) {
fullName = attribute.getValues().get(0);
}
}
if (fullName == null) {
StringBuilder fullNameBldr = new StringBuilder();
if (firstName != null) {
fullNameBldr.append(firstName);
}
if (lastName != null) {
fullNameBldr.append(" ").append(lastName);
}
fullName = fullNameBldr.toString();
}
CustomUserDetails user = new CustomUserDetails(id,fullName,email, DEFAULT_AUTHORITIES);
logger.debug("Set username " + fullName + " email " + email);
return user;
}
}
My CustomUserDetails.java
public class CustomUserDetails extends User {
private static final long serialVersionUID = 1L;
private String email;
private String name;
public CustomUserDetails(String id,String name, String email,Collection authorities) {
super(name, "unused", true,true,true,true,authorities);
this.email = email;
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
And
...
<repository>
<id>org.springframework.maven.milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
...
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.1.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.1.0.RC1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.1.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>3.1.0.RC1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-openid</artifactId>
<version>3.1.0.RC1</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
Hope can save you some time.
As I see that question text itself contains the answer. I am just pulling it out and posting it as the answer for the sake of clarity to other developers with the same problem. It took me a while to figure out that question has the answer!
To access user email & name, you need to add below configuration in the security xml file.
<security:attribute-exchange identifier-match="https://www.google.com/.*">
<security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true" />
<security:openid-attribute name="firstName" type="http://axschema.org/namePerson/first" required="true" />
<security:openid-attribute name="lastName" type="http://axschema.org/namePerson/last" required="true" />
</security:attribute-exchange>
<security:attribute-exchange identifier-match=".*yahoo.com.*">
<security:openid-attribute name="email" type="http://axschema.org/contact/email" required="true"/>
<security:openid-attribute name="fullname" type="http://axschema.org/namePerson" required="true" />
</security:attribute-exchange>
Thereafter, it will be accessible in the AuthenticationUserDetailsService class, as shown below.
public UserDetails loadUserDetails(OpenIDAuthenticationToken token) {
String id = token.getIdentityUrl();
:
:
List attributes = token.getAttributes();
for (OpenIDAttribute attribute : attributes) {
if (attribute.getName().equals("email")) {
email = attribute.getValues().get(0);
}
if (attribute.getName().equals("firstName")) {
firstName = attribute.getValues().get(0);
}
if (attribute.getName().equals("lastName")) {
lastName = attribute.getValues().get(0);
}
if (attribute.getName().equals("fullname")) {
fullName = attribute.getValues().get(0);
}
}
:
:
// form and return user object
}
Considering that we use more of Java-based configuration/beans now, translating these XML configuration to Java-based configuration shouldn't be a problem.
Hope it helps.

Problems with Hibernate Validator and Spring Webflow

I'm trying to validate a form using Spring WebFlow and Hibernate Validator Annotations, but I'm doing something wrong.
If I use the Spring validation way (ValidationContext, MessageContext, validate${state}, etc), all works fine, but I want to use the Hibernate Annotations like #Email, #NotEmpty, etc.
I've read a lot of forums, Spring documentation, but I don't know what I'm doing wrong.
At this moment, this is my view-state and next action code:
<var name="user" class="myproject.web.pojo.User"/>
<view-state id="startSignup" model="user">
<binder>
<binding property="email" />
<binding property="name" />
<binding property="lastName" />
<binding property="password" />
</binder>
<on-entry>
<set name="flowScope.user" value="new info.teaming.web.pojo.User()" />
</on-entry>
<transition on="dataEntered" to="lookupUser" />
</view-state>
<action-state id="lookupUser">
<evaluate expression="signupActions.lookupUser(user)" />
<transition to="startSignup" on-exception="myproject.web.service.exception.UserAlreadyExistsException" />
<transition to="saveOrder" />
</action-state>
(...)
This is the startSignup.jsp code:
<!-- sf:form method="POST" modelAttribute="user" -->
<sf:form method="POST" commandName="user" >
<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}" />
<fieldset>
<table cellspacing="0">
<tr>
<th>
<label for="name"><fmt:message key="signUp.name"/></label>
</th>
<td>
<sf:input path="name" size="25" id="name" />
</td>
<sf:errors path="name" />
</tr>
<tr>
<th>
<label for="lastName"><fmt:message key="signUp.lastName"/></label>
</th>
<td>
<sf:input path="lastName" size="45" maxlength="45" id="lastName" />
</td>
</tr>
<tr>
<th>
<label for="password"><fmt:message key="signUp.password"/></label>
</th>
<td>
<sf:password path="password" size="20" showPassword="true" id="password" />
</td>
</tr>
<tr>
<th>
<label for="email"><fmt:message key="signUp.email"/></label>
</th>
<td>
<sf:input path="email" size="30" id="email" />
</td>
</tr>
<tr>
<td>
<input type="submit" name="_eventId_dataEntered" value="<fmt:message key="signUp.register"/>"/>
</td>
</tr>
</table>
</fieldset>
</sf:form>
This is the myproject.web.pojo.User POJO code:
import java.io.Serializable;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#NotEmpty
#Length(min = 2, max = 25)
private String name;
#NotEmpty
#Email
private String email;
#NotEmpty
#Length(min = 2, max = 45)
private String lastName;
#NotEmpty
#Length(min = 6, max = 20)
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return lastName;
}
// public void validateStartSignup(ValidationContext context) {
// MessageContext messages = context.getMessageContext();
//
// messages.addMessage(new MessageBuilder().error().source("name").code("user.name.notEmpty").build());
// }
}
(note: as you can see, when I work with Spring Validation (uncomment the validate method), the form validates successfully)
And finally, this is my SignupAction code:
import myproject.web.model.UsrUser;
import myproject.web.model.dao.UsrUserDAO;
import myproject.web.pojo.User;
import myproject.web.service.exception.UserAlreadyExistsException;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Component;
import org.springframework.webflow.action.MultiAction;
import org.springframework.webflow.execution.Event;
#Component
public class SignupActions extends MultiAction {
#Resource
UsrUserDAO usrUserDAO;
private static final long serialVersionUID = 1L;
public Event lookupUser(#Valid User user) throws UserAlreadyExistsException {
try {
usrUserDAO.findByEmail(user.getEmail());
} catch (javax.persistence.NoResultException e) {
return success();
}
throw new UserAlreadyExistsException();
}
public void saveUser(UsrUser user) {
return;
}
}
When I work with Hibernate Validation, the flow arrives to the saveUser method, without any validation.
I'm using Spring 3:
Spring 3.0.5.RELEASE
Spring WebFlow 2.2.1.RELEASE
Hibernate Validator 4.0.2.GA
What I'm doing wrong?
Thanks a lot for helping me! :)
have you defined your validator in Spring configuration XML file?
According to Spring Webflow reference it shoud be defined like this:
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
<webflow:flow-builder-services id="flowBuilderServices" validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
Try,
Changing:
#Resource
UsrUserDAO usrUserDAO;
By:
#Autowired
private UsrUserManager UsrUserManager;
Using MVC manager.
And:
#Transactional
public Event lookupUser(#Valid User user) throws UserAlreadyExistsException { ...
as a Spring MVC controller, or reusing this MVC code if you already desing it ;)

Resources