/SpringBoot-Registration-Login-ThePerfectExample

Login & Signup tutorial for every website ,mixes a lot of microservices together with the latest spring framework api in combined with full security

Primary LanguageCSSMIT LicenseMIT

                                                Build Status star this repo GitHub Issues License GitHub pull requests

Table of Contents ๐Ÿ˜„

Basic Overview

A simple, but at the same time powerful secure login and signup system, which mixes a lot of microservices together and make a unique choice to adopt it. In addition, it comes along with the latest Spring framework Techonologies. Furthemore, this example is composed by a combination of Spring Boot 1.5.6,Spring Security, Mysql,Hibernate and JPA, Bootstrap 3 for the form design as well as jQuery for event handling.

Why should i use it

  Because you will make your life easier. Imagine that you want to create a big website with much complexity for the backend in addition, you must use a lot of microservices architectures in your Web Project. ๐Ÿ˜“ Firstly, relax you have already saved a lot of time. Your login and sign up page of your system is ready, it's secure and full open source. Independently who you are, this is your right choice to start. ๐Ÿ˜‹ ๐Ÿ˜‹ ๐Ÿ˜‹
  You have the ability to expand it as you wish. Basically, you declare your component and the relationship between them and the framework takes care of the lifecycle of your components and wires them together into a useful graph of objects. If you consider how many libraries Spring integrates with, the competition fades away. A good way to convince yourself is to look at the number of guides or how-tos are available on the SpringWebsite. All of them follow one important pattern: specify the correct dependency, initialize a Spring component and autowire library beans into it.

Deployment

In order to deploy this projects, you need to perform some steps:

Installation

Install MySQL

For testing purposes i use the UniformServer, a lightweight solution WAMP Servr solution.So lets go to start. The Uniform Server Zeros base component is the controller application. Download and extract this base component first.

  • Download and save the latest controller file (ZeroXI_Controller_latest_Version.exe) to drive C:
  • The file is a self-extracting archive, double click to run the extractor.
  • The installation (extraction) defaults to creating folder C:\UniServerZ , with the folder structure as shown on the right.

The UniServerZ folder contains file UniController.exe which opens The Uniform Server's control application for running the servers as a standard program. simply run it.

UniController is shown on the above.

  1. Click Start Apache button. Starts the Apache server.
  2. Click Start MySQL button. Starts the MySQL server.

We dont care about to start Apache because spring framework uses Embedded Tomcat which is included in the project so we will not need it. After that, simply click Start Mysql and set Mysql Pasword and mysql will be ready for usage.

Configure MySQL

In order to create, execute, and optimizing SQL queries for this project i am using MySQLWorkbench but you can also use whatever tool you want, as well as mysql command if you feel comfortable with it.

The code below represent the mysql schema that we will need to create for our web project.

CREATE DATABASE db_example; 
CREATE USER 'tutorialuser'@'localhost' IDENTIFIED BY 'tutorialmy5ql';
GRANT ALL PRIVILEGES ON *.* TO 'tutorialuser'@'localhost';
FLUSH PRIVILEGES;

CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `active` int(11) DEFAULT NULL,
  `email` varchar(255) NOT NULL,
  `last_name` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;


CREATE TABLE `role` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `role` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;


CREATE TABLE `user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`),
  CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`),
  CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Don't Forget to insert roles to the users before to continue in the next step. Build&Deploy

INSERT INTO `role` VALUES (1,'ADMIN');
INSERT INTO `role` VALUES (2,'USER');

Build and Deploy


$ cd github-maven-example/example
$ mvn clean install

This is a Spring Boot project, so you can deploy it by simply using the main class: Application.java

The compiled, source, and Javadoc WAR files will be placed in your target folder just simple ran the coomand below if you want to start the project from the war file. (Attention!!! Configure MySQL before continue).

$ cd (Your Path here)\SpringBoot-Registration-Login-ThePerfectExample\target\
$ java -jar original-Spring-Full-Security-1.0-SNAPSHOT.war

If everything is working properly you will able to open the https://127.0.0.1:8443/

you will automatically be redirected in the Loginpage

Usage


Basic Functionality

SpringDataJPA

In order to start leveraging the Spring Data programming model with JPA, a DAO interface needs to extend the JPA specific Repository interface โ€“ JpaRepository. This will enable Spring Data to find this interface and automatically create an implementation for it.By extending the interface we get the most relevant CRUD methods for standard data access available in a standard DAO out of the box.

To define more specific access methods, Spring JPA supports quite a few options โ€“ you can:

  • simply define a new method in the interface.
  • provide the actual JPQ query by using the @Query annotation.
  • use the more advanced Specification and Querydsl support in Spring Data.
  • define custom queries via JPA Named Queries.

When Spring Data creates a new Repository implementation, it analyses all the methods defined by the interfaces and tries to automatically generate queries from the method names. While this has some limitations, it is a very powerful and elegant way of defining new custom access methods with very little effort.

Letโ€™s look at an example: if the managed entity has a name field (and the Java Bean standard getName and setName methods), weโ€™ll define the findByName method as well as findByRole in the DAO interface; this will automatically generate the correct query

@Repository("roleRepository")
public interface RoleRepository extends JpaRepository<Role, Integer> {
   Role findByRole(String role);
   
@Repository("userRepository")
public interface UserRepository extends JpaRepository<User, Long> {
   User findByEmail(String email);
}

}

SecurityPerspective

Here in this content we will define the security matters which resulting and we will make a brief explanation from the security perspective about how we solve these ones.

AuthenticationProviders

An AuthenticationProvider is an abstraction for fetching user information from a specific repository (like a database, LDAP, custom third party source, etc. ). It uses the fetched user information to validate the supplied credentials. Simply put, when multiple authentication providers are defined, the providers will be queried in the order theyโ€™re declared. For a quick demonstration, weโ€™ll configure an in-memory authentication provider.

In our configuration class, letโ€™s now create and add the authentication providers using the AuthenticationManagerBuilder.

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private AuthenticationSuccessHandler successHandler;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;


    @Autowired
    private DataSource dataSource;

    @Value("${spring.queries.users-query}")
    private String usersQuery;

    @Value("${spring.queries.roles-query}")
    private String rolesQuery;

    public SecurityConfig() {
        super();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.
                jdbcAuthentication()
                .usersByUsernameQuery(usersQuery)
                .authoritiesByUsernameQuery(rolesQuery)
                .dataSource(dataSource)
                .passwordEncoder(bCryptPasswordEncoder);

    }
 }

PasswordEncoding

This project discusses use password encoding for registration process because storing the password in plaintext may can lead to critical security issues. There are a few encoding mechanism supported by Spring Security โ€“ and for the article weโ€™ll use BCrypt, as itโ€™s usually the best solution available. Most of the other mechanism, such as the MD5PasswordEncoder and ShaPasswordEncoder use weaker algorithms and are now deprecated.

Weโ€™ll start by defining the simple BCryptPasswordEncoder as a bean in our configuration:

@Bean
public PasswordEncoder passwordEncoder() {
   return new BCryptPasswordEncoder();
}

Older implementations โ€“ such as SHAPasswordEncoder โ€“ would require the client to pass in a salt value when encoding the password.BCrypt, however, will internally generate a random salt instead. This is important to understand because it means that each call will have a different result, and so we need to only encode the password once. Also be aware that the BCrypt algorithm generates a String of length 60, so we need to make sure that the password will be stored in a column that can accommodate it. A common mistake is to create a column of a different length and then get an Invalid Username or Password error at authentication time.

PersistentRememberMe

The classic solution used a cookie called remember-me โ€“ containing the username, expiration time and MD5 hash containing the password. Because it contains a hash of the password, this solution is potentially vulnerable if the cookie is captured. With that in mind โ€“ letโ€™s take a look at the this approach โ€“ using PersistentTokenBasedRememberMeServices to store the persisted login information in a database table between sessions.

First โ€“ we need to have the login information in the database โ€“ we need a table creating to hold the data:

create table if not exists persistent_logins ( 
  username varchar_ignorecase(100) not null, 
  series varchar(64) primary key, 
  token varchar(64) not null, 
  last_used timestamp not null
);

Navigate in the Config class, and for the sake of completeness, here is the way persistence is set up:

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.tokenRepository(persistentTokenRepository())
              
    }
  @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        tokenRepositoryImpl.setDataSource(dataSource);
        return tokenRepositoryImpl;
    }

As we mentioned, the standard TokenBasedRememberMeServices was storing the hashed user password in the cookie.

This solution โ€“ the PersistentTokenBasedRememberMeServices uses a unique series identifier for the user. This identifies the initial login of the user and remains constant each time the user gets logged in automatically during that persistent session. It also contains a random token that is regenerated each time a user logs in via the persisted remember-me functions.This combination of randomly generated series and token are persisted, making a brute force attack very unlikely.

Session

Spring Session has the simple goal of free up session management from the limitations of the HTTP session stored in the server. The solution makes it easy to share session data between services in the cloud without being tied to a single container (i.e. Tomcat). Additionally, it supports multiple sessions in the same browser and sending sessions in a header. Spring Session uses a filter, org.springframework.web.filter.DelegatingFilterProxy, which accepts the HttpRequest and constructs and injects its own Request object down the hierarchy. This way it gains control to the way new sessions are created, since the session object is attached to the HttpRequest Object.

Here we should setup the session.

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.essionManagement()
                .sessionFixation().migrateSession()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .invalidSessionUrl("/invalidSession")
                .maximumSessions(1)
                .expiredUrl("/invalidSession");
              
    }

In addition we should also setup the SessionListener in order to be ready to handle callbacks from the methods sessionCreated(final HttpSessionEvent event) sessionDestroyed(final HttpSessionEvent event)

public class SessionListenerWithMetrics extends HttpSessionEventPublisher {

    private final AtomicInteger activeSessions;
    private final AtomicInteger timeoutSessions;
    private final Counter counterOfActiveSessions;
    private int SessionTimeout = 1 * 50;

    public SessionListenerWithMetrics() {
        super();
        activeSessions = new AtomicInteger();
        timeoutSessions = new AtomicInteger();
        counterOfActiveSessions = MetricRegistrySingleton.metrics.counter("web.sessions.active.count");
//      context.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
    }

    // API

    @Override
    public final void sessionCreated(final HttpSessionEvent event) {
        activeSessions.incrementAndGet();
        System.out.println("Session Created: ");
        event.getSession().setMaxInactiveInterval(SessionTimeout);
        timeoutSessions.set(event.getSession().getMaxInactiveInterval());
        counterOfActiveSessions.inc();
    }

    @Override
    public final void sessionDestroyed(final HttpSessionEvent event) {
        activeSessions.decrementAndGet();
        System.out.println("Session Destroyed: ");
        counterOfActiveSessions.dec();
    }
 }

Support


Please open an issue for support.

Acknowledgments


The Pocket WAR uses the following open source software:

  • SpringFramework for creating a comprehensive programming and configuration model for modern Java-based enterprise applications
  • Bootstrap for creating Build responsive projects with the world's most popular front-end component library
  • jquery for manipulation, event handling, animation that works across a multitude of browsers

License


This project is licensed under the MIT License - see the Licence file for details

Contacts

Don't hesitate to ask me whatever you want. Stay tuned for more awesome projects ๐Ÿ˜˜ . Follow me or Connect me via Linkedin

Contributing


Please contribute using Github Flow. Create a branch, add commits, and open a pull request.

  1. Fork it: git clone https://github.com/PanagiotisDrakatos/SpringBoot-MVC-Hibernate-JPA-Mysql.git
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request
  6. ๐Ÿ˜„ ๐Ÿ˜„ ๐Ÿ˜„

Donate

If you have found this project useful, please consider making a donation
paypal