Spring Boot Oauth2 login and signup implementation using IN-Memory DB

  Spring Boot Oauth2 login and signup Implementation using In-memory Database

Purpose: In this post, we will learn how we can implement Oauth2. The steps are given below.

1. Setup and Create Project: Visit hereSpring Boot Rest API Hello World Examples.

Next Step. Spring Boot Parent Dependencies.

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
Next Step. INmemory Database Dependencies.

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>       

Next Step. Oauth2 dependency Dependencies.
      <dependency>
        <groupId>org.springframework.security.oauth</groupId>
          <artifactId>spring-security-oauth2</artifactId>
          <version>2.3.6.RELEASE</version>
      </dependency>
       

Next Step. Spring Boot main class.

Class: Application.java

Note: @SpringBootApplication=@Configuration+ @EnableAutoConfiguration+ @ComponentScan.



package com.bce;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}



Next Step. Add below line in application.properties for In-memory DB and Oauth2 configuration and change yellow highted line if port changed.

server.port=8080
server.servlet.context-path=/api
spring.jackson.default-property-inclusion=NON_NULL

spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
#enabling the H2 console
spring.h2.console.enabled=true
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:h2:C:/data/oauth2

# Custom H2 Console URL
spring.h2.console.path=/h2

 

#Application specific
authentication.oauth.clientid=springbootrest
authentication.oauth.secret=restservice
authentication.oauth.tokenValidityInSeconds=6000
#2592000
authentication.oauth.refreshTokenValidityInSeconds=10000
authentication.oauth.grant.type=password
authentication.oauth.grant.refresh.type=refresh_token
#2592000
authentication.oauth.token.url=http://localhost:8080/api
auth.server.schem=http

logging.level.org.springframework.boot.autoconfigure=ERROR


#spring.main.allow-circular-references: true

Next Step. OAuth Configuration add below java file (OAuthConfiguration.java)


package com.bce.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
@EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {

    @Value("${authentication.oauth.clientid:clientid}")
    private String PROP_CLIENTID;
   
    @Value("${authentication.oauth.secret:secret}")
    private String PROP_SECRET;
   
    @Value("${authentication.oauth.tokenValidityInSeconds:60}")
    private Integer PROP_TOKEN_VALIDITY_SECONDS;
   
    @Value("${authentication.oauth.refreshTokenValidityInSeconds:120}")
    private Integer PROP_REFRESH_TOKEN_VALIDITY_SECONDS;

   
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
   
    @Autowired
    UserDetailsService userDetailsService;

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
    }
   
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
        .withClient(PROP_CLIENTID).secret(passwordEncoder().encode(PROP_SECRET))
        .authorizedGrantTypes("password", "authorization_code", "refresh_token").scopes("read","write")
        .authorities("USER","ADMIN")
        .autoApprove(true)
        .accessTokenValiditySeconds(PROP_TOKEN_VALIDITY_SECONDS)
        .refreshTokenValiditySeconds(PROP_REFRESH_TOKEN_VALIDITY_SECONDS);
    }

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).accessTokenConverter(defaultAccessTokenConverter())
        .userDetailsService(userDetailsService);
    }

    @Bean
    public TokenStore tokenStore(){
        return new JwtTokenStore(defaultAccessTokenConverter());    
    }

    @Bean
    public JwtAccessTokenConverter defaultAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }
}


Next Step. OAuth Configuration add below java file (SecurityConfig.java)

package com.bce.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.bce.service.UserDetailsServiceImpl;

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/pages/");

    }
   
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder());
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }
}
Next Step: add  ResourceServerConfiguration.java file.

package com.bce.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    private static final String RESOURCE_ID = "oauth2-api";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/", "/user", "/oauth/token").permitAll();
        http.csrf().disable();
        http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();

    }
}


Next Step: Signup process.
Open URL as given in below image and signup: 


Given below method will be call after singup call.

A. Add signup method in LoginController.java
    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public ResponseEntity<CustomResponse<Login>> signup(@RequestBody LoginBO login) {
        log.info("user enter in signup()..");
        Login loginDB = loginService.signup(login);

        if (loginDB != null && loginDB.getLoginStatus() != StatusCode.ALREADY_EXIST) {
            ResponseEntity<Object> result = loginService.generateAuthToken(login.getEmailId(), login.getPassword());
            if (result != null && result.getStatusCode() == HttpStatus.OK) {

                CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
                loginDB.setPassword(null);
                customResponse.setData(loginDB);
                customResponse.setMessage("User created successfully");
                customResponse.setStatus("CREATED");
                customResponse.setToken(result.getBody());
                return new ResponseEntity<>(customResponse, HttpStatus.CREATED);
            } else {
                CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
                customResponse.setMessage("Authentication failed");
                customResponse.setStatus("UNAUTHORIZED");
                return new ResponseEntity<>(customResponse, HttpStatus.UNAUTHORIZED);

            }
        } else {
            CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
            customResponse.setMessage("Provided details al-ready exist");
            customResponse.setStatus("ALREADY");
            customResponse.setData(null);
            return new ResponseEntity<>(customResponse, HttpStatus.CONFLICT);
        }
    }

B. In LoginServiceImpl.java added signup method implementation. In given below method we are checking existing using is present in DB.

    @Override
    public Login signup(LoginBO signupLogin) {
        Login login = checkExistingUserByEmail(signupLogin);
        if (login != null) {
            login.setLoginStatus(StatusCode.ALREADY_EXIST);
            return login;
        }
        {
            login = new Login();

            login.setPassword(new BCryptPasswordEncoder().encode(signupLogin.getPassword()));
            login.setLoginStatus(StatusCode.VERIFICATION_PENDING);
            if (signupLogin.getEmailId() != null) {
                login.setVerifyEmail(StatusCode.VERIFICATION_PENDING);
                login.setEmailId(signupLogin.getEmailId());
            }
            if (signupLogin.getMobile() != null) {
                login.setVerifyMobile(StatusCode.VERIFICATION_PENDING);
                login.setMobile(signupLogin.getMobile());
            }
            login.setLoginTypeEnum(signupLogin.getLoginTypeEnum());
            login.setCreatedTime(new Date());
            Login lDB = this.loginRepository.save(login);

            return lDB;
        }
    }

Below are the signup response: 



Next Step: After Signup. Below are given login process.


A. Add login method in LoginController.java. In given below method first we are checking the using in DB with username and password. If your is valid the we call generate token method.
   
    @RequestMapping(value = "/loginuser", method = RequestMethod.POST)
    public ResponseEntity<CustomResponse<Login>> login(@RequestBody LoginBO login) {
        log.info("user enter in login()..");
        Login loginDB = loginService.login(login);

        if (loginDB != null) {
            ResponseEntity<Object> result = loginService.generateAuthToken(login.getEmailId(), login.getPassword());
            if (result != null && result.getStatusCode() == HttpStatus.OK) {
                CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
                customResponse.setMessage("Login Successfully");
                customResponse.setStatus("SUCCESS");
                customResponse.setToken(result.getBody());
                loginDB.setPassword(null);
                customResponse.setData(loginDB);
                return new ResponseEntity<>(customResponse, HttpStatus.OK);
            } else {
                CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
                customResponse.setMessage("Authentication failed");
                customResponse.setStatus("UNAUTHORIZED");
                customResponse.setData(null);
                return new ResponseEntity<>(customResponse, HttpStatus.UNAUTHORIZED);

            }
        } else {
            CustomResponse<Login> customResponse = new CustomResponse<Login>(loginDB);
            customResponse.setMessage("Detail does not match");
            customResponse.setStatus("FAIL");
            return new ResponseEntity<>(customResponse, HttpStatus.NOT_FOUND);
        }
    }

B. In LoginServiceImpl.java added login method implementation. In given below method we are checking is user valid in DB.

    @Override
    public Login login(LoginBO login) {
        Login loginDB = this.loginRepository.findByEmailId(login.getEmailId());
        if (loginDB != null && checkPassword(loginDB.getPassword(), login.getPassword())) {
            return loginDB;
        } else {
            return null;
        }

    }

C: Below are the method is using for generate authentication token to call API.

        @Override
    public ResponseEntity<Object> generateAuthToken(String username, String password) {
        try {
            String authentication = PROP_CLIENTID + ":" + PROP_SECRET;
            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", "Basic " + Base64.getEncoder().encodeToString(authentication.getBytes()));
            headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
            RestTemplate restTemplate = new RestTemplate();

            HttpEntity<String> requestEnty = new HttpEntity<>(headers);
            String URL = OAUTH_TOKEN_URL + "/oauth/token";
            URL += "?username=" + username;
            URL += "&grant_type=" + OAUTH_GRANT_TYPE;
            URL += "&password=" + password;

            ResponseEntity<Object> result = restTemplate.exchange(URL, HttpMethod.POST, requestEnty, Object.class);
            return result;
        } catch (RestClientException e) {

            e.printStackTrace();
            return null;
        }

    }

Login Response: 


DB Details: User details are shown below. 


Download Code from GitHub.  Download

No comments:

Post a Comment