Tuesday 17 July 2018

Spring Boot Security using OAuth2 With Password encryption

About OAuth2.0

Question:- What is OAuth2.0 and OAuth?

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service whereas OAuth is simply a secure authorization protocol. It works by delegating user authentication to the service that hosts the user account and authorizing third-party applications to access the user account. OAuth 2 provides authorization flows for web and desktop applications, and mobile devices.


OAuth defines four roles:


  • Resource Owner
  • Client
  • Resource Server
  • Authorization Server




Tokens basically are random strings generated by the authorization server and are issued when the client requests them.

There are 2 types of token being used in OAuth 2.

Access Token: This is the most important because it allows the user data from being accessed by a third-party application. This token is sent by the client as a parameter or as a header in the request to the resource server. It has a limited lifetime, which is defined by the authorization server.

Refresh Token: This token is issued with the access token but it is not sent in each request from the client to the resource server. When access token validity is finished or access token is expired then It could be sent to the authorization server for renewing the access.

Code Example:-

1. Defining the Dependency using Maven

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gaurav</groupId>
    <artifactId>oauth2-security-with-spring-boot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>


2. Configuration for Authorization Server

package com.gaurav.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.provider.token.TokenStore;

/**
 * @author Kumar Gaurav
 *
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigurationDetails extends AuthorizationServerConfigurerAdapter {

private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 5000;
private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 50000;
private static final String CLIEN_ID = "gaurav";
private static final String CLIENT_SECRET = "gaurav123";
private static final String GRANT_TYPE_PASSWORD = "password";
private static final String AUTHORIZATION_CODE = "authorization_code";
private static final String REFRESH_TOKEN = "refresh_token";
private static final String IMPLICIT = "implicit";
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String TRUST = "trust";
@Autowired
private TokenStore tokenStore;

@Autowired
private AuthenticationManager authenticationManager;

@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {

configurer
.inMemory()
.withClient(CLIEN_ID)
.secret(CLIENT_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS);
}

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


3. Configuration for Resource Server

package com.gaurav.config;

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;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;

/**
 * @author Kumar Gaurav
 *
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfigurationDetails extends ResourceServerConfigurerAdapter {

private static final String RESOURCE_ID = "resource_id";

@Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().authorizeRequests().antMatchers("/emps/**").access("hasRole('ADMIN')").and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}

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

}

4. Configuration for Web Security

package com.gaurav.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;
/**
 * @author Kumar Gaurav
 *
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigurationDetails extends WebSecurityConfigurerAdapter {

    @Resource(name = "employeeService")
    private UserDetailsService userDetailsService;

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

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(encoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/api-docs/**").permitAll();
    }

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

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

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

5. REST API for Employee(EmployeeController.java)

package com.gaurav.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.gaurav.model.Employee;
import com.gaurav.service.EmployeeService;
/**
 * @author Kumar Gaurav
 *
 */
@RestController
@RequestMapping("/emps")
public class EmployeeController {

    @Autowired
    private EmployeeService empService;

    @RequestMapping(value="/employee", method = RequestMethod.GET)
    public List<Employee> listUser(){
        return empService.findAll();
    }

    @RequestMapping(value = "/employee", method = RequestMethod.POST)
    public Employee create(@RequestBody Employee user){
        return empService.save(user);
    }

    @RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE)
    public String delete(@PathVariable(value = "id") Long id){
        empService.delete(id);
        return "success";
    }

}

6. Service for Employee

package com.gaurav.service;

import java.util.List;

import com.gaurav.model.Employee;
/**
 * @author Kumar Gaurav
 *
 */
public interface EmployeeService {

Employee save(Employee employee);
    List<Employee> findAll();
    void delete(long id);
}

7. Service Implementation for Employees

package com.gaurav.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.gaurav.dao.EmployeeDao;
import com.gaurav.model.Employee;
import com.gaurav.service.EmployeeService;

/**
 * @author Kumar Gaurav
 *
 */
@Service(value = "employeeService")
public class EmployeeServiceImpl implements UserDetailsService, EmployeeService {
@Autowired
private EmployeeDao employeeDao;

public List<Employee> findAll() {
List<Employee> list = new ArrayList<>();
employeeDao.findAll().iterator().forEachRemaining(list::add);
return list;
}

@Override
public void delete(long id) {
employeeDao.delete(id);
}

@Override
    public Employee save(Employee emp) {
        return employeeDao.save(emp);
    }
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
Employee employee = employeeDao.findByUsername(userId);
if(employee == null){
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(employee.getUsername(), employee.getPassword(), getAuthority());
}
private List<SimpleGrantedAuthority> getAuthority() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
}

8. DAO for Employee

package com.gaurav.dao;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.gaurav.model.Employee;
/**
 * @author Kumar Gaurav
 *
 */
@Repository
public interface EmployeeDao extends CrudRepository<Employee, Long> {
Employee findByUsername(String username);
}

9. Model Class for Employee

package com.gaurav.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

import javax.persistence.*;
/**
 * @author Kumar Gaurav
 *
 */
@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    private long id;
    
    @Column
    private String username;
   
    @Column
    @JsonIgnore
    private String password;
    
    @Column
    private String email;
    
    @Column
    private String designation;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    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;
    }

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getDesignation() {
return designation;
}

public void setDesignation(String designation) {
this.designation = designation;
}
}

10. Spring Boot Initialize Program

package com.gaurav;

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

/**
 * @author Kumar Gaurav
 *
 */
@SpringBootApplication
public class Application {

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

}

11. Property file with DB configuration

application.properties

server.port=9595
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQLDialect
spring.datasource.url=jdbc:mysql://localhost:3306/oauth2
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
security.oauth2.resource.filter-order=3

12. DB.SQL

CREATE TABLE `employee` (
`id` BIGINT (20),
`designation` VARCHAR (765),
`email` VARCHAR (765),
`password` VARCHAR (765),
`username` VARCHAR (765)
); 

INSERT INTO `employee` (`id`, `designation`, `email`, `password`, `username`) VALUES('1','System Analyst','gaurav@gmail.com','$2a$04$HJGJBytFiKq009XsQxxK4e8kKWKOWlm5438F1kfOh9oDDJarRrA92','Gaurav');

INSERT INTO `employee` (`id`, `designation`, `email`, `password`, `username`) VALUES('2','Technical Lead','aaditya1@gmail.com','$2a$04$LGwQg.SGToIO1ocj26kfxOGtu9Tz3jCmQV4.2S5hZgnuZaMEwUC.a','Aaditya');

INSERT INTO `employee` (`id`, `designation`, `email`, `password`, `username`) VALUES('3','Senior Developer','shivam10@gmail.com','$2a$04$i5Jjw.ypcuQR8xNKuFS7/.IQIFdojWw.iiIQ6Cff0QzvOCV.MqfD6','Shivam');

Username
Password
Hash Value(Round 4)
Gaurav
test
$2a$04$HJGJBytFiKq009XsQxxK4e8kKWKOWlm5438F1kfOh9oDDJarRrA92
Aaditya
hello
$2a$04$LGwQg.SGToIO1ocj26kfxOGtu9Tz3jCmQV4.2S5hZgnuZaMEwUC.a
Shivam
welcome
$2a$04$i5Jjw.ypcuQR8xNKuFS7/.IQIFdojWw.iiIQ6Cff0QzvOCV.MqfD6

Executing the Application

On Application.java ->Right Click ->Run As ->Java Application

Using Postman Rest Client we can hit the below URL for getting the access & Refresh token.

http://localhost:9595/oauth/token

Step 1:- In Authorization, First set type = Basic Auth, then in username and password -> pass the client_id and client_secret.


Step -2, Set the POST parameters in Body like below and hit the send button will receive the response like below.



Step-3
Use the access token to get the list of employees which is available in the Database.

URL - http://localhost:9595/emps/employee?access_token=66331215-ab80-4241-acd6-e48971f73b6c






Step-4
Use the refresh token to get the new access token like below, pass the refresh_token and corresponding values received from step 1.

URL - http://localhost:9595/oauth/token



No comments:

Post a Comment