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:
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
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);
}
}
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";
}
}
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