Customer account management
In order for a customer to place an order, he/she must create an account first. this allow customers to track their orders, and let the store admin had enough information to deliver the order.
Tradenity API offers the Customer
resource which provides all the necessary infrastructure to create and manage user account,
login and logout, safely store sensitive information such as password in encrypted format.
In this section we will learn how to integrate Tradenity Customer
resource and related services within your application
to allow your customers to create and manage their accounts.
Spring Security plugin is the preferred security framework for grails. In order to manage user authentication and authorization in spring security, You must provide at least 3 classes:
UserDetails
class which represents a user.UserDetailsService
implementation, which knows how to get that user based on his credentials (username and password)- Configure security path mapping
Implementing these classes is very simple, as Tradenity already implemented the required infrastructure for you, so the spring specific implementation is just a thin layer around what is already there.
Implement UserDetails
Our implementation of UserDetails
interface is a wrapper around the Customer
class,
delegating all the method calls to their corresponding methods in the customer instance
public class User implements Serializable, UserDetails{
String customerId;
String firstName;
String lastName;
String email;
String username;
String password;
String confirmPassword;
boolean enabled = true;
public User() {
}
public User(Customer customer) {
this.customerId = customer.getId();
this.firstName = customer.getFirstName();
this.lastName = customer.getLastName();
this.email = customer.getEmail();
this.username = customer.getUsername();
this.password = customer.getPassword();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Customer toCustomer() {
Customer c = new Customer(firstName, lastName, email, username, password, "active");
c.setId(this.customerId);
return c;
}
}
Implement UserDetailsService
Next, Implement the UserDetailsService
interface. just a simple wrapper around Tradenity SDK ‘s CustomerService#findByUsername
@Service
public class UserServiceImpl implements UserDetailsService{
@Autowired
CustomerService customerService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Customer customer = customerService.findByUsername(username);
if(customer == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
return new User(customer);
}
}
Configure spring security
grails-app/conf/spring/resources.groovy
import tradenity.grails.sample.CustomerDetailsService
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
beans = {
userDetailsService(CustomerDetailsService)
passwordEncoder(BCryptPasswordEncoder)
}
-
We define a bean of type
CustomerDetailsService
which implementUserDetailsService
. Spring security will pick it automatically and use it to loadUserDetails
instances upon login attempt. -
But, What is
PasswordEncoder
andBCryptPasswordEncoder
? Tradenity never store customer passwords as plain text, this is a best practice for security.
Tradenity stores the encrypted password, Using the bcrypt
algorithm which makes it nearly impossible for hackers to decrypt it.
So, we configure spring security to use the appropriate password encoder.
Now let’s configure spring security to protect our sensitive web paths.
grails-app/conf/application.groovy
grails.plugin.springsecurity.auth.loginFormUrl = '/login'
grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/'
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login?error=1'
grails.plugin.springsecurity.securityConfigType = "InterceptUrlMap"
grails.plugin.springsecurity.interceptUrlMap = [
[pattern: '/', access: ['permitAll']],
[pattern: '/favicon.ico', access: ['permitAll']],
[pattern: '/fonts/**', access: ['permitAll']],
[pattern: '/error', access: ['permitAll']],
[pattern: '/index', access: ['permitAll']],
[pattern: '/products', access: ['permitAll']],
[pattern: '/products/**', access: ['permitAll']],
[pattern: '/categories/**', access: ['permitAll']],
[pattern: '/brands/**', access: ['permitAll']],
[pattern: '/cart/**', access: ['permitAll']],
[pattern: '/login', access: ['permitAll']],
[pattern: '/login/**', access: ['permitAll']],
[pattern: '/logout', access: ['permitAll']],
[pattern: '/logout/**', access: ['permitAll']],
[pattern: '/register', access: ['permitAll']],
[pattern: '/orders/**', access: 'isAuthenticated()'],
[pattern: '/account/create', access: ['permitAll']],
[pattern: '/assets/**', access: ['permitAll']],
[pattern: '/shutdown', access: ['permitAll']]
]
This configuration instruct spring security to protect /orders/**
path, and permit access to all other actions.
The login form
Now, we have everything in place, and we are ready to authenticate our customers using their username and password. Here is the HTML form for user login.
grails-app/views/account/login.gsp
<form action="/login/authenticate" method="post">
<ul>
<li class="text-info">Username: </li>
<li><input type="text" name="username" value=""/></li>
</ul>
<ul>
<li class="text-info">Password: </li>
<li><input type="password" name="password" value=""/></li>
</ul>
<input type="submit" value="LOGIN"/>
</form>
Register New customers
grails-app/views/account/register.gsp
<form action="/account/create" method="post" >
<ul>
<li class="text-info">First Name: </li>
<li><g:textField name="firstName" value="${user.firstName}"/></li>
</ul>
<ul>
<li class="text-info">Last Name: </li>
<li><g:textField name="lastName" value="${user.lastName}"/></li>
</ul>
<ul>
<li class="text-info">Email: </li>
<li><g:textField name="email" value="${user.email}"/></li>
</ul>
<ul>
<li class="text-info">Username: </li>
<li><g:textField name="username" value="${user.username}"/></li>
</ul>
<ul>
<li class="text-info">Password: </li>
<li><g:passwordField name="password" value="${user.password}"/></li>
</ul>
<ul>
<li class="text-info">Re-enter Password:</li>
<li><g:passwordField name="confirmPassword" value="${user.confirmPassword}"/></li>
</ul>
<input type="submit" value="REGISTER NOW"/>
<p class="click">By clicking this button, you are agree to my <a href="#">Policy Terms and Conditions.</a></p>
</form>
and this is the action handler mwthod, it validate the imput and call CustomerService#create
method.
grails-app/controllers/AccountController.groovy
def create() {
def u = new User(firstName: params.firstName, lastName: params.lastName, email: params.email,
username: params.username, password: params.password, confirmPassword: params.confirmPassword
)
if(u.valid()){
def c = customerService.create(u.toCustomer())
redirect action: "login"
}else {
println "invalid"
render view: "register", model: [user: u,
categories: categoryService.findAll(),
cart: shoppingCartService.get()]
}
}