├── .gitignore ├── Book-Authentication ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── src │ ├── main │ ├── kotlin │ │ └── org │ │ │ └── rizki │ │ │ └── mufrizal │ │ │ └── api │ │ │ └── gateway │ │ │ └── BookAuthentication │ │ │ ├── BookAuthenticationApplication.kt │ │ │ ├── configuration │ │ │ ├── DataSourceConfiguration.kt │ │ │ ├── OAuth2Configuration.kt │ │ │ ├── RedisSessionConfiguration.kt │ │ │ └── WebSecurityConfiguration.kt │ │ │ ├── domain │ │ │ ├── OAuth2ClientDetail.kt │ │ │ └── User.kt │ │ │ └── repository │ │ │ ├── OAuth2ClientDetailRepository.kt │ │ │ └── UserRepository.kt │ └── resources │ │ └── application.yml │ └── test │ └── kotlin │ └── org │ └── rizki │ └── mufrizal │ └── api │ └── gateway │ └── BookAuthentication │ └── BookAuthenticationApplicationTests.kt ├── Book-Catalog ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── src │ ├── main │ ├── kotlin │ │ └── org │ │ │ └── rizki │ │ │ └── mufrizal │ │ │ └── api │ │ │ └── gateway │ │ │ └── BookCatalog │ │ │ ├── BookCatalogApplication.kt │ │ │ ├── configuration │ │ │ ├── CorsConfiguration.kt │ │ │ ├── DataSourceConfiguration.kt │ │ │ ├── OAuth2Configuration.kt │ │ │ └── RedisSessionConfiguration.kt │ │ │ ├── controller │ │ │ └── BookController.kt │ │ │ ├── domain │ │ │ └── Book.kt │ │ │ └── repository │ │ │ └── BookRepository.kt │ └── resources │ │ └── application.yml │ └── test │ └── kotlin │ └── org │ └── rizki │ └── mufrizal │ └── api │ └── gateway │ └── BookCatalog │ └── BookCatalogApplicationTests.kt ├── Book-Gateway ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── src │ ├── main │ ├── kotlin │ │ └── org │ │ │ └── rizki │ │ │ └── mufrizal │ │ │ └── api │ │ │ └── gateway │ │ │ └── BookGateway │ │ │ ├── BookGatewayApplication.kt │ │ │ ├── configuration │ │ │ ├── CorsConfiguration.kt │ │ │ ├── OAuth2Configuration.kt │ │ │ ├── RedisSessionConfiguration.kt │ │ │ ├── RestTemplateConfiguration.kt │ │ │ ├── RibbonConfiguration.kt │ │ │ ├── ZuulConfiguration.kt │ │ │ └── ZuulFilterConfiguration.kt │ │ │ ├── controller │ │ │ └── ApiController.kt │ │ │ ├── domain │ │ │ ├── Book.kt │ │ │ ├── BookDetail.kt │ │ │ └── Review.kt │ │ │ └── service │ │ │ ├── catalog │ │ │ └── CatalogService.kt │ │ │ ├── integration │ │ │ └── IntegrationService.kt │ │ │ └── review │ │ │ └── ReviewService.kt │ └── resources │ │ └── application.yml │ └── test │ └── kotlin │ └── org │ └── rizki │ └── mufrizal │ └── api │ └── gateway │ └── BookGateway │ └── BookGatewayApplicationTests.kt ├── Book-Review ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── src │ ├── main │ ├── kotlin │ │ └── org │ │ │ └── rizki │ │ │ └── mufrizal │ │ │ └── api │ │ │ └── gateway │ │ │ └── BookReview │ │ │ ├── BookReviewApplication.kt │ │ │ ├── configuration │ │ │ ├── CorsConfiguration.kt │ │ │ ├── DataSourceConfiguration.kt │ │ │ ├── OAuth2Configuration.kt │ │ │ └── RedisSessionConfiguration.kt │ │ │ ├── controller │ │ │ └── ReviewController.kt │ │ │ ├── domain │ │ │ └── Review.kt │ │ │ └── repository │ │ │ └── ReviewRepository.kt │ └── resources │ │ └── application.yml │ └── test │ └── kotlin │ └── org │ └── rizki │ └── mufrizal │ └── api │ └── gateway │ └── BookReview │ └── BookReviewApplicationTests.kt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Book-Authentication/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /Book-Authentication/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | kotlinVersion = '1.1.2-4' 4 | springBootVersion = '1.5.3.RELEASE' 5 | } 6 | repositories { 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 11 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") 12 | classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") 13 | } 14 | } 15 | 16 | apply plugin: 'kotlin' 17 | apply plugin: 'kotlin-spring' 18 | apply plugin: 'eclipse' 19 | apply plugin: 'org.springframework.boot' 20 | 21 | version = '0.0.1-SNAPSHOT' 22 | sourceCompatibility = 1.8 23 | compileKotlin { 24 | kotlinOptions.jvmTarget = "1.8" 25 | } 26 | compileTestKotlin { 27 | kotlinOptions.jvmTarget = "1.8" 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | } 33 | 34 | dependencies { 35 | compile('org.springframework.boot:spring-boot-starter-security') 36 | compile('org.springframework.security.oauth:spring-security-oauth2') 37 | compile('org.springframework.security:spring-security-jwt') 38 | compile('org.springframework.session:spring-session') 39 | compile('org.springframework.boot:spring-boot-starter-data-redis') 40 | compile('org.springframework.boot:spring-boot-starter-web') 41 | compile('org.springframework.boot:spring-boot-starter-data-jpa') 42 | compile('com.zaxxer:HikariCP') 43 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}") 44 | compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") 45 | runtime('org.mariadb.jdbc:mariadb-java-client') 46 | testCompile('org.springframework.boot:spring-boot-starter-test') 47 | } 48 | -------------------------------------------------------------------------------- /Book-Authentication/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RizkiMufrizal/Simple-API-Gateway/543c1f68ac0a90b9e05bdc65f977959842e2941d/Book-Authentication/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Book-Authentication/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 28 07:32:41 WIB 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip 7 | -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/BookAuthenticationApplication.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication 2 | 3 | import org.rizki.mufrizal.api.gateway.BookAuthentication.domain.OAuth2ClientDetail 4 | import org.rizki.mufrizal.api.gateway.BookAuthentication.domain.User 5 | import org.rizki.mufrizal.api.gateway.BookAuthentication.repository.OAuth2ClientDetailRepository 6 | import org.rizki.mufrizal.api.gateway.BookAuthentication.repository.UserRepository 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.boot.CommandLineRunner 9 | import org.springframework.boot.SpringApplication 10 | import org.springframework.boot.autoconfigure.SpringBootApplication 11 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder 12 | 13 | @SpringBootApplication 14 | class BookAuthenticationApplication : CommandLineRunner { 15 | 16 | @Autowired 17 | private lateinit var userRepository: UserRepository 18 | 19 | @Autowired 20 | private lateinit var oAuth2ClientDetailRepository: OAuth2ClientDetailRepository 21 | 22 | override fun run(vararg args: String?) { 23 | val username = "rizki" 24 | val clientIdBook = "CLIENT_ID_BOOK" 25 | val clientIdReview = "CLIENT_ID_REVIEW" 26 | val clientIdPassword = "CLIENT_ID_PASSWORD" 27 | val clientIdGateway = "CLIENT_ID_GATEWAY" 28 | 29 | val user = userRepository.findOne(username) 30 | if (user == null) { 31 | val roles = listOf("ROLE_USER") 32 | val newUser = User( 33 | username = username, 34 | isActive = true, 35 | password = BCryptPasswordEncoder().encode("mufrizal"), 36 | roles = roles 37 | ) 38 | userRepository.save(newUser) 39 | } 40 | 41 | val book = oAuth2ClientDetailRepository.findOne(clientIdBook) 42 | if (book == null) { 43 | oAuth2ClientDetailRepository 44 | .save(OAuth2ClientDetail( 45 | clientId = clientIdBook, 46 | accessTokenValidity = 3600, 47 | refreshTokenValidity = 3600, 48 | additionalInformation = "{\"additional_param\":\"OAuth Book\"}", 49 | authorizedGrantTypes = "client_credentials", 50 | autoApprove = true, 51 | webServerRedirectUri = " ", 52 | scope = "read,write", 53 | Authorities = " ", 54 | resourceIds = "RESOURCE_ID_REVIEW", 55 | clientSecret = "CLIENT_SECRET_BOOK" 56 | )) 57 | } 58 | 59 | val review = oAuth2ClientDetailRepository.findOne(clientIdReview) 60 | if (review == null) { 61 | oAuth2ClientDetailRepository 62 | .save(OAuth2ClientDetail( 63 | clientId = clientIdReview, 64 | accessTokenValidity = 3600, 65 | refreshTokenValidity = 3600, 66 | additionalInformation = "{\"additional_param\":\"OAuth Review\"}", 67 | authorizedGrantTypes = "client_credentials", 68 | autoApprove = true, 69 | webServerRedirectUri = " ", 70 | scope = "read,write", 71 | Authorities = " ", 72 | resourceIds = "RESOURCE_ID_BOOK", 73 | clientSecret = "CLIENT_SECRET_REVIEW" 74 | )) 75 | } 76 | 77 | val password = oAuth2ClientDetailRepository.findOne(clientIdPassword) 78 | if (password == null) { 79 | oAuth2ClientDetailRepository 80 | .save(OAuth2ClientDetail( 81 | clientId = clientIdPassword, 82 | accessTokenValidity = 3600, 83 | refreshTokenValidity = 3600, 84 | additionalInformation = "{\"additional_param\":\"OAuth Password\"}", 85 | authorizedGrantTypes = "password,refresh_token", 86 | autoApprove = true, 87 | webServerRedirectUri = " ", 88 | scope = "read,write", 89 | Authorities = " ", 90 | resourceIds = "RESOURCE_ID_GATEWAY", 91 | clientSecret = "CLIENT_SECRET_PASSWORD" 92 | )) 93 | } 94 | 95 | val gateway = oAuth2ClientDetailRepository.findOne(clientIdGateway) 96 | if (gateway == null) { 97 | oAuth2ClientDetailRepository 98 | .save(OAuth2ClientDetail( 99 | clientId = clientIdGateway, 100 | accessTokenValidity = 3600, 101 | refreshTokenValidity = 3600, 102 | additionalInformation = "{\"additional_param\":\"OAuth Gateway\"}", 103 | authorizedGrantTypes = "client_credentials", 104 | autoApprove = true, 105 | webServerRedirectUri = " ", 106 | scope = "read,write", 107 | Authorities = " ", 108 | resourceIds = "RESOURCE_ID_BOOK,RESOURCE_ID_REVIEW", 109 | clientSecret = "CLIENT_ID_GATEWAY" 110 | )) 111 | } 112 | } 113 | 114 | } 115 | 116 | fun main(args: Array) { 117 | SpringApplication.run(BookAuthenticationApplication::class.java, *args) 118 | } 119 | -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/configuration/DataSourceConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.configuration 2 | 3 | import com.zaxxer.hikari.HikariConfig 4 | import com.zaxxer.hikari.HikariDataSource 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.context.annotation.Bean 7 | import org.springframework.context.annotation.Configuration 8 | import org.springframework.context.annotation.PropertySource 9 | import org.springframework.core.env.Environment 10 | import javax.sql.DataSource 11 | 12 | /** 13 | * Created by rizkimufrizal on 5/28/17. 14 | */ 15 | @Configuration 16 | @PropertySource("classpath:application.yml") 17 | class DataSourceConfiguration @Autowired constructor(val environment: Environment) { 18 | 19 | @Bean(destroyMethod = "close") 20 | fun dataSource(): DataSource { 21 | val dataSourceConfig = HikariConfig() 22 | dataSourceConfig.driverClassName = environment.getRequiredProperty("spring.datasource.driver-class-name") 23 | dataSourceConfig.jdbcUrl = environment.getRequiredProperty("spring.datasource.url") 24 | dataSourceConfig.username = environment.getRequiredProperty("spring.datasource.username") 25 | dataSourceConfig.password = environment.getRequiredProperty("spring.datasource.password") 26 | dataSourceConfig.maximumPoolSize = environment.getRequiredProperty("spring.datasource.maximumPoolSize").toInt() 27 | dataSourceConfig.minimumIdle = environment.getRequiredProperty("spring.datasource.minimumIdle").toInt() 28 | dataSourceConfig.connectionTimeout = environment.getRequiredProperty("spring.datasource.connectionTimeout").toLong() 29 | dataSourceConfig.idleTimeout = environment.getRequiredProperty("spring.datasource.idleTimeout").toLong() 30 | dataSourceConfig.addDataSourceProperty("poolName", environment.getRequiredProperty("spring.datasource.poolName")) 31 | dataSourceConfig.addDataSourceProperty("cachePrepStmts", true) 32 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSize", 250) 33 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSqlLimit", 2048) 34 | return HikariDataSource(dataSourceConfig) 35 | } 36 | } -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/configuration/OAuth2Configuration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.beans.factory.annotation.Qualifier 5 | import org.springframework.context.annotation.Bean 6 | import org.springframework.context.annotation.Configuration 7 | import org.springframework.security.authentication.AuthenticationManager 8 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer 10 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter 11 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 12 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer 13 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer 14 | import javax.sql.DataSource 15 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore 16 | import org.springframework.security.oauth2.provider.token.TokenStore 17 | 18 | 19 | /** 20 | * Created by rizkimufrizal on 5/28/17. 21 | */ 22 | @Configuration 23 | open class OAuth2Configuration { 24 | 25 | @Configuration 26 | @EnableAuthorizationServer 27 | protected class AuthorizationServerConfiguration : AuthorizationServerConfigurerAdapter() { 28 | 29 | @Autowired 30 | @Qualifier("authenticationManagerBean") 31 | private lateinit var authenticationManager: AuthenticationManager 32 | 33 | @Autowired 34 | private lateinit var dataSource: DataSource 35 | 36 | @Autowired 37 | private lateinit var jedisConnectionFactory: JedisConnectionFactory 38 | 39 | @Bean 40 | fun jwtAccessTokenConverter(): JwtAccessTokenConverter { 41 | return JwtAccessTokenConverter() 42 | } 43 | 44 | @Bean 45 | fun tokenStore(): TokenStore { 46 | return RedisTokenStore(jedisConnectionFactory) 47 | } 48 | 49 | @Throws(Exception::class) 50 | override fun configure(authorizationServerEndpointsConfigurer: AuthorizationServerEndpointsConfigurer?) { 51 | authorizationServerEndpointsConfigurer 52 | ?.accessTokenConverter(jwtAccessTokenConverter()) 53 | ?.tokenStore(tokenStore()) 54 | ?.authenticationManager(authenticationManager) 55 | } 56 | 57 | @Throws(Exception::class) 58 | override fun configure(clientDetailsServiceConfigurer: ClientDetailsServiceConfigurer?) { 59 | clientDetailsServiceConfigurer 60 | ?.jdbc(dataSource) 61 | } 62 | 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/configuration/RedisSessionConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.context.annotation.PropertySource 7 | import org.springframework.core.env.Environment 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 9 | import org.springframework.data.redis.core.RedisTemplate 10 | import org.springframework.data.redis.serializer.GenericToStringSerializer 11 | import org.springframework.data.redis.serializer.StringRedisSerializer 12 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 13 | import redis.clients.jedis.JedisPoolConfig 14 | import java.time.Duration 15 | 16 | /** 17 | * Created by rizkimufrizal on 5/28/17. 18 | */ 19 | @Configuration 20 | @PropertySource("classpath:application.yml") 21 | @EnableRedisHttpSession 22 | class RedisSessionConfiguration @Autowired constructor(val environment: Environment) { 23 | 24 | private fun jedisPoolConfig(): JedisPoolConfig { 25 | val jedisPoolConfig = JedisPoolConfig() 26 | jedisPoolConfig.maxTotal = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-total")) 27 | jedisPoolConfig.maxIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-idle")) 28 | jedisPoolConfig.minIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.min-idle")) 29 | jedisPoolConfig.minEvictableIdleTimeMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.min-evictable-idle-time-millis").toLong()).toMillis() 30 | jedisPoolConfig.timeBetweenEvictionRunsMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.time-between-eviction-runs-millis").toLong()).toMillis() 31 | jedisPoolConfig.blockWhenExhausted = environment.getRequiredProperty("spring.redis.block-when-exhausted").toBoolean() 32 | return jedisPoolConfig 33 | } 34 | 35 | @Bean 36 | fun jedisConnectionFactory(): JedisConnectionFactory { 37 | val jedisConnectionFactory = JedisConnectionFactory(jedisPoolConfig()) 38 | jedisConnectionFactory.port = environment.getRequiredProperty("spring.redis.port").toInt() 39 | jedisConnectionFactory.hostName = environment.getRequiredProperty("spring.redis.host") 40 | jedisConnectionFactory.usePool = environment.getRequiredProperty("spring.redis.use-pool").toBoolean() 41 | return jedisConnectionFactory 42 | } 43 | 44 | @Bean 45 | fun redisTemplate(): RedisTemplate { 46 | val redisTemplate = RedisTemplate() 47 | redisTemplate.connectionFactory = jedisConnectionFactory() 48 | redisTemplate.keySerializer = StringRedisSerializer() 49 | redisTemplate.hashValueSerializer = GenericToStringSerializer(Any::class.java) 50 | redisTemplate.valueSerializer = GenericToStringSerializer(Any::class.java) 51 | return redisTemplate 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/configuration/WebSecurityConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.configuration 2 | 3 | import org.rizki.mufrizal.api.gateway.BookAuthentication.repository.UserRepository 4 | import org.springframework.beans.factory.annotation.Autowired 5 | import org.springframework.context.annotation.Bean 6 | import org.springframework.context.annotation.Configuration 7 | import org.springframework.security.authentication.AuthenticationManager 8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity 10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 11 | import org.springframework.security.core.GrantedAuthority 12 | import org.springframework.security.core.authority.SimpleGrantedAuthority 13 | import org.springframework.security.core.userdetails.User 14 | import org.springframework.security.core.userdetails.UserDetailsService 15 | import org.springframework.security.core.userdetails.UsernameNotFoundException 16 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder 17 | import org.springframework.security.crypto.password.PasswordEncoder 18 | import java.util.* 19 | 20 | 21 | /** 22 | * Created by rizkimufrizal on 5/28/17. 23 | */ 24 | @Configuration 25 | @EnableWebSecurity(debug = true) 26 | class WebSecurityConfiguration @Autowired constructor(val userRepository: UserRepository) : WebSecurityConfigurerAdapter() { 27 | 28 | 29 | @Bean 30 | fun passwordEncoder(): PasswordEncoder { 31 | return BCryptPasswordEncoder() 32 | } 33 | 34 | override fun userDetailsService() = UserDetailsService { 35 | username -> 36 | userRepository.findByUsername(username) 37 | .map { user -> 38 | buildUserForAuthentication(user, buildUserAuthority(userRoles = user.roles as List)) 39 | } 40 | .orElseThrow { 41 | UsernameNotFoundException("username not found $username") 42 | } 43 | } 44 | 45 | private fun buildUserForAuthentication(user: org.rizki.mufrizal.api.gateway.BookAuthentication.domain.User, grantedAuthorities: List): User { 46 | return User(user.username, user.password, user.isActive as Boolean, true, true, true, grantedAuthorities) 47 | } 48 | 49 | private fun buildUserAuthority(userRoles: List): List { 50 | val grantedAuthorities = HashSet() 51 | userRoles.forEach { grantedAuthorities.add(SimpleGrantedAuthority(it)) } 52 | return ArrayList(grantedAuthorities) 53 | } 54 | 55 | override fun configure(authenticationManagerBuilder: AuthenticationManagerBuilder?) { 56 | authenticationManagerBuilder 57 | ?.userDetailsService(this.userDetailsService()) 58 | ?.passwordEncoder(passwordEncoder()) 59 | } 60 | 61 | @Bean 62 | override fun authenticationManagerBean(): AuthenticationManager { 63 | return super.authenticationManagerBean() 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/domain/OAuth2ClientDetail.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.domain 2 | 3 | import javax.persistence.* 4 | 5 | /** 6 | * Created by rizkimufrizal on 5/28/17. 7 | */ 8 | 9 | @Entity 10 | @Table(name = "oauth_client_details") 11 | data class OAuth2ClientDetail( 12 | @Id 13 | @Column(name = "client_id", nullable = false) 14 | val clientId: String? = null, 15 | 16 | @Column(name = "resource_ids", nullable = false) 17 | val resourceIds: String? = null, 18 | 19 | @Column(name = "client_secret", nullable = false) 20 | val clientSecret: String? = null, 21 | 22 | @Column(name = "scope", nullable = false) 23 | val scope: String? = null, 24 | 25 | @Column(name = "authorized_grant_types", nullable = false) 26 | val authorizedGrantTypes: String? = null, 27 | 28 | @Column(name = "web_server_redirect_uri", nullable = false) 29 | val webServerRedirectUri: String? = null, 30 | 31 | @Column(name = "authorities", nullable = false) 32 | val Authorities: String? = null, 33 | 34 | @Column(name = "access_token_validity", nullable = false) 35 | val accessTokenValidity: Int? = null, 36 | 37 | @Column(name = "refresh_token_validity", nullable = false) 38 | val refreshTokenValidity: Int? = null, 39 | 40 | @Lob 41 | @Column(name = "additional_information", nullable = false) 42 | val additionalInformation: String? = null, 43 | 44 | @Column(name = "autoapprove", nullable = false) 45 | val autoApprove: Boolean? = null 46 | ) -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/domain/User.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.domain 2 | 3 | import javax.persistence.* 4 | 5 | /** 6 | * Created by rizkimufrizal on 5/28/17. 7 | */ 8 | 9 | @Entity 10 | @Table(name = "tb_user") 11 | data class User( 12 | @Id 13 | @Column(length = 50, nullable = false) 14 | val username: String? = null, 15 | @Column(length = 100, nullable = false) 16 | val password: String? = null, 17 | @Column(name = "is_active", length = 100, nullable = false) 18 | val isActive: Boolean? = null, 19 | @ElementCollection(fetch = FetchType.EAGER) 20 | @CollectionTable(name = "roles", joinColumns = arrayOf(JoinColumn(name = "username"))) 21 | @Column(name = "role") 22 | val roles: List? = null 23 | ) -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/repository/OAuth2ClientDetailRepository.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.repository 2 | 3 | import org.rizki.mufrizal.api.gateway.BookAuthentication.domain.OAuth2ClientDetail 4 | import org.springframework.data.repository.PagingAndSortingRepository 5 | 6 | /** 7 | * Created by rizkimufrizal on 5/28/17. 8 | */ 9 | interface OAuth2ClientDetailRepository : PagingAndSortingRepository -------------------------------------------------------------------------------- /Book-Authentication/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/repository/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication.repository 2 | 3 | import org.rizki.mufrizal.api.gateway.BookAuthentication.domain.User 4 | import org.springframework.data.repository.PagingAndSortingRepository 5 | import java.util.* 6 | 7 | /** 8 | * Created by rizkimufrizal on 5/28/17. 9 | */ 10 | interface UserRepository : PagingAndSortingRepository { 11 | fun findByUsername(username: String): Optional 12 | } -------------------------------------------------------------------------------- /Book-Authentication/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # konfigurasi oauth2 di spring 1.5.1 2 | security: 3 | oauth2: 4 | resource: 5 | filter-order: 3 6 | 7 | spring: 8 | 9 | #konfigurasi redis 10 | redis: 11 | host: 127.0.0.1 12 | port: 6379 13 | max-total: 128 14 | max-idle: 128 15 | min-idle: 16 16 | min-evictable-idle-time-millis: 60 17 | time-between-eviction-runs-millis: 30 18 | block-when-exhausted: true 19 | use-pool: true 20 | 21 | #konfigurasi jackson 22 | jackson: 23 | serialization: 24 | indent-output: true 25 | 26 | # spring datasource jpa 27 | datasource: 28 | driver-class-name: org.mariadb.jdbc.Driver 29 | url: jdbc:mariadb://127.0.0.1:3306/database_book?autoReconnect=true 30 | username: root 31 | password: 32 | poolName: SpringBootHikariCP 33 | maximumPoolSize: 5 34 | minimumIdle: 3 35 | maxLifetime: 2000000 36 | connectionTimeout: 30000 37 | idleTimeout: 30000 38 | 39 | # spring hibernate 40 | jpa: 41 | generate-ddl: true 42 | show-sql: true 43 | hibernate: 44 | ddl-auto: update 45 | properties: 46 | hibernate: 47 | format_sql: true 48 | 49 | server: 50 | port: 8083 51 | -------------------------------------------------------------------------------- /Book-Authentication/src/test/kotlin/org/rizki/mufrizal/api/gateway/BookAuthentication/BookAuthenticationApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookAuthentication 2 | 3 | import org.junit.Test 4 | import org.junit.runner.RunWith 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.test.context.junit4.SpringRunner 7 | 8 | @RunWith(SpringRunner::class) 9 | @SpringBootTest 10 | class BookAuthenticationApplicationTests { 11 | 12 | @Test 13 | fun contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Book-Catalog/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /Book-Catalog/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | kotlinVersion = '1.1.2-4' 4 | springBootVersion = '1.5.3.RELEASE' 5 | } 6 | repositories { 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 11 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") 12 | classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") 13 | } 14 | } 15 | 16 | apply plugin: 'kotlin' 17 | apply plugin: 'kotlin-spring' 18 | apply plugin: 'eclipse' 19 | apply plugin: 'org.springframework.boot' 20 | 21 | version = '0.0.1-SNAPSHOT' 22 | sourceCompatibility = 1.8 23 | compileKotlin { 24 | kotlinOptions.jvmTarget = "1.8" 25 | } 26 | compileTestKotlin { 27 | kotlinOptions.jvmTarget = "1.8" 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | } 33 | 34 | dependencies { 35 | compile('org.springframework.boot:spring-boot-starter-security') 36 | compile('org.springframework.security.oauth:spring-security-oauth2') 37 | compile('org.springframework.session:spring-session') 38 | compile('org.springframework.boot:spring-boot-starter-data-redis') 39 | compile('org.springframework.boot:spring-boot-starter-web') 40 | compile('org.springframework.boot:spring-boot-starter-data-jpa') 41 | compile('com.zaxxer:HikariCP') 42 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}") 43 | compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") 44 | runtime('org.mariadb.jdbc:mariadb-java-client') 45 | 46 | testCompile('org.springframework.boot:spring-boot-starter-test') 47 | } 48 | -------------------------------------------------------------------------------- /Book-Catalog/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RizkiMufrizal/Simple-API-Gateway/543c1f68ac0a90b9e05bdc65f977959842e2941d/Book-Catalog/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Book-Catalog/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 27 21:09:41 WIB 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip 7 | -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/BookCatalogApplication.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog 2 | 3 | import org.rizki.mufrizal.api.gateway.BookCatalog.domain.Book 4 | import org.rizki.mufrizal.api.gateway.BookCatalog.repository.BookRepository 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.boot.CommandLineRunner 7 | import org.springframework.boot.SpringApplication 8 | import org.springframework.boot.autoconfigure.SpringBootApplication 9 | 10 | @SpringBootApplication 11 | class BookCatalogApplication @Autowired constructor(val bookRepository: BookRepository) : CommandLineRunner { 12 | 13 | override fun run(vararg args: String?) { 14 | (1..10) 15 | .filter { bookRepository.findOne(it.toLong()) == null } 16 | .forEach { 17 | bookRepository.save(Book( 18 | title = "Judul Buku $it", 19 | description = "deskripsi buku ini adalah $it" 20 | )) 21 | } 22 | } 23 | } 24 | 25 | fun main(args: Array) { 26 | SpringApplication.run(BookCatalogApplication::class.java, *args) 27 | } 28 | -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/configuration/CorsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.configuration 2 | 3 | import org.springframework.stereotype.Component 4 | import java.io.IOException 5 | import javax.servlet.* 6 | import javax.servlet.http.HttpServletRequest 7 | import javax.servlet.http.HttpServletResponse 8 | 9 | /** 10 | * Created by rizkimufrizal on 5/28/17. 11 | */ 12 | @Component 13 | class CorsConfiguration : Filter { 14 | 15 | @Throws(ServletException::class) 16 | override fun init(filterConfig: FilterConfig) { 17 | 18 | } 19 | 20 | @Throws(IOException::class, ServletException::class) 21 | override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { 22 | val httpServletResponse = response as HttpServletResponse 23 | val httpServletRequest = request as HttpServletRequest 24 | 25 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*") 26 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT") 27 | httpServletResponse.setHeader("Access-Control-Max-Age", "3600") 28 | httpServletResponse.setHeader("Access-Control-Allow-Headers", "accept, authorization, x-requested-with, content-type") 29 | 30 | if (httpServletRequest.method.equals("OPTIONS", ignoreCase = true)) { 31 | try { 32 | httpServletResponse.status = 200 33 | httpServletResponse.writer.print("OK") 34 | httpServletResponse.writer.flush() 35 | } catch (e: IOException) { 36 | } 37 | 38 | } else { 39 | chain.doFilter(request, response) 40 | } 41 | } 42 | 43 | override fun destroy() { 44 | 45 | } 46 | } -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/configuration/DataSourceConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.configuration 2 | 3 | import com.zaxxer.hikari.HikariConfig 4 | import com.zaxxer.hikari.HikariDataSource 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.context.annotation.Bean 7 | import org.springframework.context.annotation.Configuration 8 | import org.springframework.context.annotation.PropertySource 9 | import org.springframework.core.env.Environment 10 | import javax.sql.DataSource 11 | 12 | /** 13 | * Created by rizkimufrizal on 5/28/17. 14 | */ 15 | @Configuration 16 | @PropertySource("classpath:application.yml") 17 | class DataSourceConfiguration @Autowired constructor(val environment: Environment) { 18 | 19 | @Bean(destroyMethod = "close") 20 | fun dataSource(): DataSource { 21 | val dataSourceConfig = HikariConfig() 22 | dataSourceConfig.driverClassName = environment.getRequiredProperty("spring.datasource.driver-class-name") 23 | dataSourceConfig.jdbcUrl = environment.getRequiredProperty("spring.datasource.url") 24 | dataSourceConfig.username = environment.getRequiredProperty("spring.datasource.username") 25 | dataSourceConfig.password = environment.getRequiredProperty("spring.datasource.password") 26 | dataSourceConfig.maximumPoolSize = environment.getRequiredProperty("spring.datasource.maximumPoolSize").toInt() 27 | dataSourceConfig.minimumIdle = environment.getRequiredProperty("spring.datasource.minimumIdle").toInt() 28 | dataSourceConfig.connectionTimeout = environment.getRequiredProperty("spring.datasource.connectionTimeout").toLong() 29 | dataSourceConfig.idleTimeout = environment.getRequiredProperty("spring.datasource.idleTimeout").toLong() 30 | dataSourceConfig.addDataSourceProperty("poolName", environment.getRequiredProperty("spring.datasource.poolName")) 31 | dataSourceConfig.addDataSourceProperty("cachePrepStmts", true) 32 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSize", 250) 33 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSqlLimit", 2048) 34 | return HikariDataSource(dataSourceConfig) 35 | } 36 | } -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/configuration/OAuth2Configuration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 8 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter 10 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer 11 | import org.springframework.security.oauth2.provider.token.TokenStore 12 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore 13 | import org.springframework.security.web.access.channel.ChannelProcessingFilter 14 | 15 | /** 16 | * Created by rizkimufrizal on 5/28/17. 17 | */ 18 | @Configuration 19 | open class OAuth2Configuration { 20 | 21 | @Configuration 22 | @EnableResourceServer 23 | protected class ResourceServerConfiguration @Autowired constructor(val jedisConnectionFactory: JedisConnectionFactory) : ResourceServerConfigurerAdapter() { 24 | 25 | private val RESOURCE_ID = "RESOURCE_ID_BOOK" 26 | 27 | @Bean 28 | fun tokenStore(): TokenStore { 29 | return RedisTokenStore(jedisConnectionFactory) 30 | } 31 | 32 | override fun configure(resourceServerSecurityConfigurer: ResourceServerSecurityConfigurer?) { 33 | resourceServerSecurityConfigurer 34 | ?.tokenStore(tokenStore()) 35 | ?.resourceId(RESOURCE_ID) 36 | } 37 | 38 | override fun configure(httpSecurity: HttpSecurity?) { 39 | httpSecurity 40 | ?.authorizeRequests() 41 | ?.antMatchers("/**") 42 | ?.fullyAuthenticated() 43 | ?.and() 44 | ?.addFilterBefore(CorsConfiguration(), ChannelProcessingFilter::class.java) 45 | } 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/configuration/RedisSessionConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.context.annotation.PropertySource 7 | import org.springframework.core.env.Environment 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 9 | import org.springframework.data.redis.core.RedisTemplate 10 | import org.springframework.data.redis.serializer.GenericToStringSerializer 11 | import org.springframework.data.redis.serializer.StringRedisSerializer 12 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 13 | import redis.clients.jedis.JedisPoolConfig 14 | import java.time.Duration 15 | 16 | /** 17 | * Created by rizkimufrizal on 5/28/17. 18 | */ 19 | @Configuration 20 | @PropertySource("classpath:application.yml") 21 | @EnableRedisHttpSession 22 | class RedisSessionConfiguration @Autowired constructor(val environment: Environment) { 23 | 24 | private fun jedisPoolConfig(): JedisPoolConfig { 25 | val jedisPoolConfig = JedisPoolConfig() 26 | jedisPoolConfig.maxTotal = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-total")) 27 | jedisPoolConfig.maxIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-idle")) 28 | jedisPoolConfig.minIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.min-idle")) 29 | jedisPoolConfig.minEvictableIdleTimeMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.min-evictable-idle-time-millis").toLong()).toMillis() 30 | jedisPoolConfig.timeBetweenEvictionRunsMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.time-between-eviction-runs-millis").toLong()).toMillis() 31 | jedisPoolConfig.blockWhenExhausted = environment.getRequiredProperty("spring.redis.block-when-exhausted").toBoolean() 32 | return jedisPoolConfig 33 | } 34 | 35 | @Bean 36 | fun jedisConnectionFactory(): JedisConnectionFactory { 37 | val jedisConnectionFactory = JedisConnectionFactory(jedisPoolConfig()) 38 | jedisConnectionFactory.port = environment.getRequiredProperty("spring.redis.port").toInt() 39 | jedisConnectionFactory.hostName = environment.getRequiredProperty("spring.redis.host") 40 | jedisConnectionFactory.usePool = environment.getRequiredProperty("spring.redis.use-pool").toBoolean() 41 | return jedisConnectionFactory 42 | } 43 | 44 | @Bean 45 | fun redisTemplate(): RedisTemplate { 46 | val redisTemplate = RedisTemplate() 47 | redisTemplate.connectionFactory = jedisConnectionFactory() 48 | redisTemplate.keySerializer = StringRedisSerializer() 49 | redisTemplate.hashValueSerializer = GenericToStringSerializer(Any::class.java) 50 | redisTemplate.valueSerializer = GenericToStringSerializer(Any::class.java) 51 | return redisTemplate 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/controller/BookController.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.controller 2 | 3 | import org.rizki.mufrizal.api.gateway.BookCatalog.repository.BookRepository 4 | import org.springframework.beans.factory.annotation.Autowired 5 | import org.springframework.data.domain.Pageable 6 | import org.springframework.http.HttpStatus 7 | import org.springframework.http.ResponseEntity 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.RequestMapping 11 | import org.springframework.web.bind.annotation.RestController 12 | 13 | /** 14 | * Created by rizkimufrizal on 5/28/17. 15 | */ 16 | @RestController 17 | @RequestMapping(value = "/api") 18 | class BookController @Autowired constructor(val bookRepository: BookRepository) { 19 | 20 | @GetMapping(value = "/books") 21 | fun books(pageable: Pageable): ResponseEntity<*> = ResponseEntity(bookRepository.findAll(pageable), HttpStatus.OK) 22 | 23 | @GetMapping(value = "/books/{id}") 24 | fun book(@PathVariable("id") id: Long): ResponseEntity<*> = ResponseEntity(bookRepository.findOne(id), HttpStatus.OK) 25 | 26 | } -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/domain/Book.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.domain 2 | 3 | import javax.persistence.* 4 | 5 | /** 6 | * Created by rizkimufrizal on 5/28/17. 7 | */ 8 | @Entity 9 | @Table(name = "tb_book") 10 | data class Book( 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | val id: Long? = null, 14 | @Column(length = 50, nullable = false) 15 | val title: String? = null, 16 | @Lob 17 | @Column(nullable = false) 18 | val description: String? = null 19 | ) -------------------------------------------------------------------------------- /Book-Catalog/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/repository/BookRepository.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog.repository 2 | 3 | import org.rizki.mufrizal.api.gateway.BookCatalog.domain.Book 4 | import org.springframework.data.repository.PagingAndSortingRepository 5 | 6 | /** 7 | * Created by rizkimufrizal on 5/28/17. 8 | */ 9 | interface BookRepository : PagingAndSortingRepository -------------------------------------------------------------------------------- /Book-Catalog/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # konfigurasi oauth2 di spring 1.5.1 2 | security: 3 | oauth2: 4 | resource: 5 | filter-order: 3 6 | 7 | spring: 8 | profiles: 9 | active: development 10 | 11 | #konfigurasi redis 12 | redis: 13 | host: 127.0.0.1 14 | port: 6379 15 | max-total: 128 16 | max-idle: 128 17 | min-idle: 16 18 | min-evictable-idle-time-millis: 60 19 | time-between-eviction-runs-millis : 30 20 | block-when-exhausted : true 21 | use-pool: true 22 | 23 | # spring datasource jpa 24 | datasource: 25 | driver-class-name: org.mariadb.jdbc.Driver 26 | url: jdbc:mariadb://127.0.0.1:3306/database_book?autoReconnect=true 27 | username: root 28 | password: 29 | poolName: SpringBootHikariCP 30 | maximumPoolSize: 5 31 | minimumIdle: 3 32 | maxLifetime: 2000000 33 | connectionTimeout: 30000 34 | idleTimeout: 30000 35 | 36 | # spring hibernate 37 | jpa: 38 | generate-ddl: true 39 | show-sql: true 40 | hibernate: 41 | ddl-auto: update 42 | properties: 43 | hibernate: 44 | format_sql: true 45 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 46 | 47 | # jackson config 48 | jackson: 49 | serialization: 50 | indent_output: true 51 | server: 52 | port: 8081 53 | -------------------------------------------------------------------------------- /Book-Catalog/src/test/kotlin/org/rizki/mufrizal/api/gateway/BookCatalog/BookCatalogApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookCatalog 2 | 3 | import org.junit.Test 4 | import org.junit.runner.RunWith 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.test.context.junit4.SpringRunner 7 | 8 | @RunWith(SpringRunner::class) 9 | @SpringBootTest 10 | class BookCatalogApplicationTests { 11 | 12 | @Test 13 | fun contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Book-Gateway/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /Book-Gateway/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | kotlinVersion = '1.1.2-4' 4 | springBootVersion = '1.5.3.RELEASE' 5 | } 6 | repositories { 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 11 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") 12 | classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") 13 | } 14 | } 15 | 16 | apply plugin: 'kotlin' 17 | apply plugin: 'kotlin-spring' 18 | apply plugin: 'eclipse' 19 | apply plugin: 'org.springframework.boot' 20 | 21 | version = '0.0.1-SNAPSHOT' 22 | sourceCompatibility = 1.8 23 | compileKotlin { 24 | kotlinOptions.jvmTarget = "1.8" 25 | } 26 | compileTestKotlin { 27 | kotlinOptions.jvmTarget = "1.8" 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | } 33 | 34 | ext { 35 | springCloudVersion = 'Dalston.SR1' 36 | } 37 | 38 | dependencies { 39 | compile('org.springframework.cloud:spring-cloud-starter-zuul') 40 | compile('org.springframework.cloud:spring-cloud-starter-hystrix') 41 | compile('org.springframework.cloud:spring-cloud-starter-ribbon') 42 | compile('org.springframework.boot:spring-boot-starter-security') 43 | compile('org.springframework.security.oauth:spring-security-oauth2') 44 | compile('org.springframework.session:spring-session') 45 | compile('org.springframework.boot:spring-boot-starter-data-redis') 46 | compile('org.springframework.boot:spring-boot-starter-web') 47 | compile('io.reactivex.rxjava2:rxjava:2.1.0') 48 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}") 49 | compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") 50 | testCompile('org.springframework.boot:spring-boot-starter-test') 51 | } 52 | 53 | dependencyManagement { 54 | imports { 55 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 56 | } 57 | } -------------------------------------------------------------------------------- /Book-Gateway/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RizkiMufrizal/Simple-API-Gateway/543c1f68ac0a90b9e05bdc65f977959842e2941d/Book-Gateway/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Book-Gateway/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 28 11:56:38 WIB 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip 7 | -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/BookGatewayApplication.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway 2 | 3 | import org.springframework.boot.SpringApplication 4 | import org.springframework.boot.autoconfigure.SpringBootApplication 5 | 6 | @SpringBootApplication 7 | class BookGatewayApplication 8 | 9 | fun main(args: Array) { 10 | SpringApplication.run(BookGatewayApplication::class.java, *args) 11 | } 12 | -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/CorsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import org.springframework.stereotype.Component 4 | import java.io.IOException 5 | import javax.servlet.* 6 | import javax.servlet.http.HttpServletRequest 7 | import javax.servlet.http.HttpServletResponse 8 | 9 | /** 10 | * Created by rizkimufrizal on 5/28/17. 11 | */ 12 | @Component 13 | class CorsConfiguration : Filter { 14 | 15 | @Throws(ServletException::class) 16 | override fun init(filterConfig: FilterConfig) { 17 | 18 | } 19 | 20 | @Throws(IOException::class, ServletException::class) 21 | override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { 22 | val httpServletResponse = response as HttpServletResponse 23 | val httpServletRequest = request as HttpServletRequest 24 | 25 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*") 26 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT") 27 | httpServletResponse.setHeader("Access-Control-Max-Age", "3600") 28 | httpServletResponse.setHeader("Access-Control-Allow-Headers", "accept, authorization, x-requested-with, content-type") 29 | 30 | if (httpServletRequest.method.equals("OPTIONS", ignoreCase = true)) { 31 | try { 32 | httpServletResponse.status = 200 33 | httpServletResponse.writer.print("OK") 34 | httpServletResponse.writer.flush() 35 | } catch (e: IOException) { 36 | } 37 | 38 | } else { 39 | chain.doFilter(request, response) 40 | } 41 | } 42 | 43 | override fun destroy() { 44 | 45 | } 46 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/OAuth2Configuration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 8 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter 10 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer 11 | import org.springframework.security.oauth2.provider.token.TokenStore 12 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore 13 | import org.springframework.security.web.access.channel.ChannelProcessingFilter 14 | 15 | /** 16 | * Created by rizkimufrizal on 5/28/17. 17 | */ 18 | @Configuration 19 | open class OAuth2Configuration { 20 | 21 | @Configuration 22 | @EnableResourceServer 23 | protected class ResourceServerConfiguration @Autowired constructor(val jedisConnectionFactory: JedisConnectionFactory) : ResourceServerConfigurerAdapter() { 24 | 25 | private val RESOURCE_ID = "RESOURCE_ID_GATEWAY" 26 | 27 | @Bean 28 | fun tokenStore(): TokenStore { 29 | return RedisTokenStore(jedisConnectionFactory) 30 | } 31 | 32 | override fun configure(resourceServerSecurityConfigurer: ResourceServerSecurityConfigurer?) { 33 | resourceServerSecurityConfigurer 34 | ?.tokenStore(tokenStore()) 35 | ?.resourceId(RESOURCE_ID) 36 | } 37 | 38 | override fun configure(httpSecurity: HttpSecurity?) { 39 | httpSecurity 40 | ?.authorizeRequests() 41 | ?.antMatchers("/api/**") 42 | ?.fullyAuthenticated() 43 | ?.and() 44 | ?.addFilterBefore(CorsConfiguration(), ChannelProcessingFilter::class.java) 45 | } 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/RedisSessionConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.context.annotation.PropertySource 7 | import org.springframework.core.env.Environment 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 9 | import org.springframework.data.redis.core.RedisTemplate 10 | import org.springframework.data.redis.serializer.GenericToStringSerializer 11 | import org.springframework.data.redis.serializer.StringRedisSerializer 12 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 13 | import redis.clients.jedis.JedisPoolConfig 14 | import java.time.Duration 15 | 16 | /** 17 | * Created by rizkimufrizal on 5/28/17. 18 | */ 19 | @Configuration 20 | @PropertySource("classpath:application.yml") 21 | @EnableRedisHttpSession 22 | class RedisSessionConfiguration @Autowired constructor(val environment: Environment) { 23 | 24 | private fun jedisPoolConfig(): JedisPoolConfig { 25 | val jedisPoolConfig = JedisPoolConfig() 26 | jedisPoolConfig.maxTotal = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-total")) 27 | jedisPoolConfig.maxIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-idle")) 28 | jedisPoolConfig.minIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.min-idle")) 29 | jedisPoolConfig.minEvictableIdleTimeMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.min-evictable-idle-time-millis").toLong()).toMillis() 30 | jedisPoolConfig.timeBetweenEvictionRunsMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.time-between-eviction-runs-millis").toLong()).toMillis() 31 | jedisPoolConfig.blockWhenExhausted = environment.getRequiredProperty("spring.redis.block-when-exhausted").toBoolean() 32 | return jedisPoolConfig 33 | } 34 | 35 | @Bean 36 | fun jedisConnectionFactory(): JedisConnectionFactory { 37 | val jedisConnectionFactory = JedisConnectionFactory(jedisPoolConfig()) 38 | jedisConnectionFactory.port = environment.getRequiredProperty("spring.redis.port").toInt() 39 | jedisConnectionFactory.hostName = environment.getRequiredProperty("spring.redis.host") 40 | jedisConnectionFactory.usePool = environment.getRequiredProperty("spring.redis.use-pool").toBoolean() 41 | return jedisConnectionFactory 42 | } 43 | 44 | @Bean 45 | fun redisTemplate(): RedisTemplate { 46 | val redisTemplate = RedisTemplate() 47 | redisTemplate.connectionFactory = jedisConnectionFactory() 48 | redisTemplate.keySerializer = StringRedisSerializer() 49 | redisTemplate.hashValueSerializer = GenericToStringSerializer(Any::class.java) 50 | redisTemplate.valueSerializer = GenericToStringSerializer(Any::class.java) 51 | return redisTemplate 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/RestTemplateConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import org.springframework.cloud.client.loadbalancer.LoadBalanced 4 | import org.springframework.cloud.netflix.ribbon.RibbonClient 5 | import org.springframework.context.annotation.Bean 6 | import org.springframework.context.annotation.Configuration 7 | import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext 8 | import org.springframework.security.oauth2.client.OAuth2RestTemplate 9 | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails 10 | import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest 11 | import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails 12 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client 13 | import org.springframework.web.client.RestTemplate 14 | 15 | /** 16 | * Created by rizkimufrizal on 5/28/17. 17 | */ 18 | 19 | @Configuration 20 | @RibbonClient(name = RibbonConfiguration.name, configuration = arrayOf(RibbonConfiguration::class)) 21 | @EnableOAuth2Client 22 | class RestTemplateConfiguration { 23 | 24 | @LoadBalanced 25 | @Bean 26 | fun getRestTemplate(): RestTemplate { 27 | return RestTemplate() 28 | } 29 | 30 | @Bean 31 | fun resource(): OAuth2ProtectedResourceDetails { 32 | val clientCredentials = ClientCredentialsResourceDetails() 33 | clientCredentials.clientId = "CLIENT_ID_GATEWAY" 34 | clientCredentials.clientSecret = "CLIENT_SECRET_GATEWAY" 35 | clientCredentials.grantType = "client_credentials" 36 | clientCredentials.accessTokenUri = "http://localhost:8083/oauth/token" 37 | return clientCredentials 38 | } 39 | 40 | @Bean 41 | fun restTemplate(): OAuth2RestTemplate { 42 | val accessTokenRequest = DefaultAccessTokenRequest() 43 | return OAuth2RestTemplate(resource(), DefaultOAuth2ClientContext(accessTokenRequest)) 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/RibbonConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import com.netflix.client.config.DefaultClientConfigImpl 4 | import com.netflix.client.config.IClientConfig 5 | import com.netflix.loadbalancer.IPing 6 | import com.netflix.loadbalancer.IRule 7 | import com.netflix.loadbalancer.PingUrl 8 | import com.netflix.loadbalancer.WeightedResponseTimeRule 9 | import org.springframework.context.annotation.Bean 10 | import org.springframework.context.annotation.Configuration 11 | import org.springframework.context.annotation.Lazy 12 | 13 | 14 | /** 15 | * Created by rizkimufrizal on 5/28/17. 16 | */ 17 | @Configuration 18 | class RibbonConfiguration { 19 | 20 | companion object { 21 | const val name = "SPRING-CLOUD-RIBBON" 22 | } 23 | 24 | @Bean 25 | fun ribbonPing(@Lazy config: IClientConfig): IPing { 26 | return PingUrl() 27 | } 28 | 29 | @Bean 30 | fun ribbonRule(@Lazy config: IClientConfig): IRule { 31 | return WeightedResponseTimeRule() 32 | } 33 | 34 | @Bean 35 | fun ribbonClientConfig(): IClientConfig { 36 | val config = DefaultClientConfigImpl() 37 | config.loadProperties(name) 38 | return config 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/ZuulConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | 7 | /** 8 | * Created by rizkimufrizal on 6/2/17. 9 | */ 10 | 11 | @Configuration 12 | @EnableZuulProxy 13 | class ZuulConfiguration { 14 | 15 | @Bean 16 | fun zuulFilterConfiguration(): ZuulFilterConfiguration { 17 | return ZuulFilterConfiguration() 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/configuration/ZuulFilterConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.configuration 2 | 3 | import com.netflix.zuul.ZuulFilter 4 | import com.netflix.zuul.context.RequestContext 5 | import org.slf4j.LoggerFactory 6 | 7 | /** 8 | * Created by rizkimufrizal on 6/2/17. 9 | */ 10 | class ZuulFilterConfiguration : ZuulFilter() { 11 | 12 | private val logger = LoggerFactory.getLogger(ZuulFilterConfiguration::class.java) 13 | 14 | override fun run(): Any? { 15 | val requestContext = RequestContext.getCurrentContext() 16 | val httpServlerRequest = requestContext.request 17 | requestContext.addZuulRequestHeader("Authorization", httpServlerRequest.getHeader("Authorization")) 18 | 19 | logger.info(String.format("${httpServlerRequest.method} request to ${httpServlerRequest.requestURL}")) 20 | logger.info(String.format("http header Authorization ${httpServlerRequest.getHeader("Authorization")}")) 21 | return null 22 | } 23 | 24 | override fun shouldFilter(): Boolean { 25 | return true 26 | } 27 | 28 | override fun filterType(): String { 29 | return "pre" 30 | } 31 | 32 | override fun filterOrder(): Int { 33 | return 1 34 | } 35 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/controller/ApiController.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.controller 2 | 3 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.BookDetail 4 | import org.rizki.mufrizal.api.gateway.BookGateway.service.integration.IntegrationService 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.web.bind.annotation.GetMapping 7 | import org.springframework.web.bind.annotation.PathVariable 8 | import org.springframework.web.bind.annotation.RequestMapping 9 | import org.springframework.web.bind.annotation.RestController 10 | import org.springframework.web.context.request.async.DeferredResult 11 | import rx.Observable 12 | import rx.Observer 13 | 14 | /** 15 | * Created by rizkimufrizal on 5/28/17. 16 | */ 17 | 18 | @RestController 19 | @RequestMapping(value = "/api") 20 | class ApiController @Autowired constructor(val integrationService: IntegrationService) { 21 | 22 | @GetMapping(value = "/books/{id}") 23 | fun getBookDetails(@PathVariable("id") id: Long): DeferredResult { 24 | return toDeferredResult(integrationService.getBookDetails(id)) 25 | } 26 | 27 | private fun toDeferredResult(details: Observable): DeferredResult { 28 | val result = DeferredResult() 29 | details.subscribe(object : Observer { 30 | override fun onCompleted() {} 31 | 32 | override fun onError(throwable: Throwable) {} 33 | 34 | override fun onNext(bookDetail: BookDetail) { 35 | result.setResult(bookDetail) 36 | } 37 | }) 38 | return result 39 | } 40 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/domain/Book.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.domain 2 | 3 | /** 4 | * Created by rizkimufrizal on 5/28/17. 5 | */ 6 | 7 | data class Book(val id: Long? = null, val title: String? = null, val description: String? = null) -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/domain/BookDetail.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.domain 2 | 3 | /** 4 | * Created by rizkimufrizal on 5/28/17. 5 | */ 6 | 7 | data class BookDetail(val id: Long? = null, val title: String? = null, val description: String? = null, val reviews: Iterable? = null) -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/domain/Review.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.domain 2 | 3 | /** 4 | * Created by rizkimufrizal on 5/28/17. 5 | */ 6 | 7 | data class Review(val id: Long? = null, val bookId: Long? = null, val reviewText: String? = null) -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/service/catalog/CatalogService.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.service.catalog 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand 4 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.Book 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.security.oauth2.client.OAuth2RestTemplate 7 | import org.springframework.stereotype.Component 8 | import rx.Observable 9 | 10 | /** 11 | * Created by rizkimufrizal on 5/28/17. 12 | */ 13 | @Component 14 | class CatalogService @Autowired constructor(val oAuth2RestTemplate: OAuth2RestTemplate) { 15 | 16 | @HystrixCommand(fallbackMethod = "fallbackBook") 17 | fun getBook(id: Long): Observable { 18 | return Observable.create { observer -> 19 | try { 20 | observer.onNext(oAuth2RestTemplate.getForObject("http://localhost:8081/api/books/{id}", Book::class.java, id)) 21 | observer.onCompleted() 22 | } catch (e: Exception) { 23 | observer.onError(e) 24 | } 25 | } 26 | } 27 | 28 | private fun fallbackBook(id: Long): Book { 29 | return Book( 30 | id = id, 31 | description = "Service Book Lagi error", 32 | title = "Microservice" 33 | ) 34 | } 35 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/service/integration/IntegrationService.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.service.integration 2 | 3 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.Book 4 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.BookDetail 5 | import org.rizki.mufrizal.api.gateway.BookGateway.service.catalog.CatalogService 6 | import org.rizki.mufrizal.api.gateway.BookGateway.service.review.ReviewService 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.stereotype.Component 9 | import rx.Observable 10 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.Review 11 | 12 | 13 | /** 14 | * Created by rizkimufrizal on 5/28/17. 15 | */ 16 | 17 | @Component 18 | class IntegrationService { 19 | 20 | @Autowired 21 | private lateinit var catalogService: CatalogService 22 | 23 | @Autowired 24 | private lateinit var reviewService: ReviewService 25 | 26 | fun getBookDetails(bookId: Long): Observable { 27 | return Observable.zip( 28 | catalogService.getBook(bookId), 29 | reviewService.getReviews(bookId), 30 | this::buildBookDetails 31 | ) 32 | } 33 | 34 | private fun buildBookDetails(book: Book, reviews: Iterable): BookDetail { 35 | return BookDetail( 36 | id = book.id, 37 | title = book.title, 38 | description = book.description, 39 | reviews = reviews 40 | ) 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/service/review/ReviewService.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway.service.review 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand 4 | import org.rizki.mufrizal.api.gateway.BookGateway.domain.Review 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.core.ParameterizedTypeReference 7 | import org.springframework.http.HttpMethod 8 | import org.springframework.security.oauth2.client.OAuth2RestTemplate 9 | import org.springframework.stereotype.Component 10 | import rx.Observable 11 | 12 | 13 | /** 14 | * Created by rizkimufrizal on 5/28/17. 15 | */ 16 | @Component 17 | class ReviewService @Autowired constructor(val restTemplate: OAuth2RestTemplate) { 18 | 19 | @HystrixCommand(fallbackMethod = "fallbackReview") 20 | fun getReviews(id: Long): Observable> { 21 | return Observable.create { observer -> 22 | try { 23 | val responseType = object : ParameterizedTypeReference>() {} 24 | observer.onNext(restTemplate.exchange("http://localhost:8082/api/book/reviews/{id}", HttpMethod.GET, null, responseType, id).body) 25 | observer.onCompleted() 26 | } catch (e: Exception) { 27 | observer.onError(e) 28 | } 29 | } 30 | } 31 | 32 | private fun fallbackReview(id: Long): Iterable { 33 | return listOf() 34 | } 35 | } -------------------------------------------------------------------------------- /Book-Gateway/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # konfigurasi oauth2 di spring 1.5.1 2 | security: 3 | oauth2: 4 | resource: 5 | filter-order: 3 6 | 7 | spring: 8 | profiles: 9 | active: development 10 | 11 | #konfigurasi redis 12 | redis: 13 | host: 127.0.0.1 14 | port: 6379 15 | max-total: 128 16 | max-idle: 128 17 | min-idle: 16 18 | min-evictable-idle-time-millis: 60 19 | time-between-eviction-runs-millis : 30 20 | block-when-exhausted : true 21 | use-pool: true 22 | 23 | # jackson config 24 | jackson: 25 | serialization: 26 | indent_output: true 27 | server: 28 | port: 8080 29 | 30 | ping-server: 31 | ribbon: 32 | eureka: 33 | enabled: false 34 | listOfServers: localhost:8081,localhost:8082 35 | ServerListRefreshInterval: 15000 36 | zuul: 37 | routes: 38 | authentications: 39 | path: /** 40 | url: http://localhost:8083/ 41 | 42 | ribbon: 43 | eureka: 44 | enabled: false -------------------------------------------------------------------------------- /Book-Gateway/src/test/kotlin/org/rizki/mufrizal/api/gateway/BookGateway/BookGatewayApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookGateway 2 | 3 | import org.junit.Test 4 | import org.junit.runner.RunWith 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.test.context.junit4.SpringRunner 7 | 8 | @RunWith(SpringRunner::class) 9 | @SpringBootTest 10 | class BookGatewayApplicationTests { 11 | 12 | @Test 13 | fun contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Book-Review/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ -------------------------------------------------------------------------------- /Book-Review/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | kotlinVersion = '1.1.2-4' 4 | springBootVersion = '1.5.3.RELEASE' 5 | } 6 | repositories { 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 11 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") 12 | classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") 13 | } 14 | } 15 | 16 | apply plugin: 'kotlin' 17 | apply plugin: 'kotlin-spring' 18 | apply plugin: 'eclipse' 19 | apply plugin: 'org.springframework.boot' 20 | 21 | version = '0.0.1-SNAPSHOT' 22 | sourceCompatibility = 1.8 23 | compileKotlin { 24 | kotlinOptions.jvmTarget = "1.8" 25 | } 26 | compileTestKotlin { 27 | kotlinOptions.jvmTarget = "1.8" 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | } 33 | 34 | dependencies { 35 | compile('org.springframework.boot:spring-boot-starter-security') 36 | compile('org.springframework.security.oauth:spring-security-oauth2') 37 | compile('org.springframework.session:spring-session') 38 | compile('org.springframework.boot:spring-boot-starter-data-redis') 39 | compile('org.springframework.boot:spring-boot-starter-web') 40 | compile('org.springframework.boot:spring-boot-starter-data-jpa') 41 | compile('com.zaxxer:HikariCP') 42 | compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}") 43 | compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}") 44 | runtime('org.mariadb.jdbc:mariadb-java-client') 45 | 46 | testCompile('org.springframework.boot:spring-boot-starter-test') 47 | } 48 | -------------------------------------------------------------------------------- /Book-Review/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RizkiMufrizal/Simple-API-Gateway/543c1f68ac0a90b9e05bdc65f977959842e2941d/Book-Review/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Book-Review/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 28 07:18:43 WIB 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip 7 | -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/BookReviewApplication.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview 2 | 3 | import org.rizki.mufrizal.api.gateway.BookReview.domain.Review 4 | import org.rizki.mufrizal.api.gateway.BookReview.repository.ReviewRepository 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.boot.CommandLineRunner 7 | import org.springframework.boot.SpringApplication 8 | import org.springframework.boot.autoconfigure.SpringBootApplication 9 | 10 | @SpringBootApplication 11 | class BookReviewApplication @Autowired constructor(val reviewRepository: ReviewRepository) : CommandLineRunner { 12 | 13 | override fun run(vararg args: String?) { 14 | (1..10) 15 | .filter { reviewRepository.findOne(it.toLong()) == null } 16 | .forEach { 17 | for (j in 1..10) { 18 | reviewRepository.save(Review( 19 | bookId = j.toLong(), 20 | reviewText = "ini adalah contoh review no $it-$j" 21 | )) 22 | } 23 | } 24 | } 25 | 26 | } 27 | 28 | fun main(args: Array) { 29 | SpringApplication.run(BookReviewApplication::class.java, *args) 30 | } 31 | -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/configuration/CorsConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.configuration 2 | 3 | import org.springframework.stereotype.Component 4 | import java.io.IOException 5 | import javax.servlet.* 6 | import javax.servlet.http.HttpServletRequest 7 | import javax.servlet.http.HttpServletResponse 8 | 9 | 10 | /** 11 | * Created by rizkimufrizal on 5/28/17. 12 | */ 13 | @Component 14 | class CorsConfiguration : Filter { 15 | 16 | @Throws(ServletException::class) 17 | override fun init(filterConfig: FilterConfig) { 18 | 19 | } 20 | 21 | @Throws(IOException::class, ServletException::class) 22 | override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) { 23 | val httpServletResponse = response as HttpServletResponse 24 | val httpServletRequest = request as HttpServletRequest 25 | 26 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*") 27 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT") 28 | httpServletResponse.setHeader("Access-Control-Max-Age", "3600") 29 | httpServletResponse.setHeader("Access-Control-Allow-Headers", "accept, authorization, x-requested-with, content-type") 30 | 31 | if (httpServletRequest.method.equals("OPTIONS", ignoreCase = true)) { 32 | try { 33 | httpServletResponse.status = 200 34 | httpServletResponse.writer.print("OK") 35 | httpServletResponse.writer.flush() 36 | } catch (e: IOException) { 37 | } 38 | 39 | } else { 40 | chain.doFilter(request, response) 41 | } 42 | } 43 | 44 | override fun destroy() { 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/configuration/DataSourceConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.configuration 2 | 3 | import com.zaxxer.hikari.HikariConfig 4 | import com.zaxxer.hikari.HikariDataSource 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.context.annotation.Bean 7 | import org.springframework.context.annotation.Configuration 8 | import org.springframework.context.annotation.PropertySource 9 | import org.springframework.core.env.Environment 10 | import javax.sql.DataSource 11 | 12 | /** 13 | * Created by rizkimufrizal on 5/28/17. 14 | */ 15 | @Configuration 16 | @PropertySource("classpath:application.yml") 17 | class DataSourceConfiguration @Autowired constructor(val environment: Environment) { 18 | 19 | @Bean(destroyMethod = "close") 20 | fun dataSource(): DataSource { 21 | val dataSourceConfig = HikariConfig() 22 | dataSourceConfig.driverClassName = environment.getRequiredProperty("spring.datasource.driver-class-name") 23 | dataSourceConfig.jdbcUrl = environment.getRequiredProperty("spring.datasource.url") 24 | dataSourceConfig.username = environment.getRequiredProperty("spring.datasource.username") 25 | dataSourceConfig.password = environment.getRequiredProperty("spring.datasource.password") 26 | dataSourceConfig.maximumPoolSize = environment.getRequiredProperty("spring.datasource.maximumPoolSize").toInt() 27 | dataSourceConfig.minimumIdle = environment.getRequiredProperty("spring.datasource.minimumIdle").toInt() 28 | dataSourceConfig.connectionTimeout = environment.getRequiredProperty("spring.datasource.connectionTimeout").toLong() 29 | dataSourceConfig.idleTimeout = environment.getRequiredProperty("spring.datasource.idleTimeout").toLong() 30 | dataSourceConfig.addDataSourceProperty("poolName", environment.getRequiredProperty("spring.datasource.poolName")) 31 | dataSourceConfig.addDataSourceProperty("cachePrepStmts", true) 32 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSize", 250) 33 | dataSourceConfig.addDataSourceProperty("prepStmtCacheSqlLimit", 2048) 34 | return HikariDataSource(dataSourceConfig) 35 | } 36 | } -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/configuration/OAuth2Configuration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity 8 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter 10 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer 11 | import org.springframework.security.oauth2.provider.token.TokenStore 12 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore 13 | import org.springframework.security.web.access.channel.ChannelProcessingFilter 14 | 15 | 16 | /** 17 | * Created by rizkimufrizal on 5/28/17. 18 | */ 19 | @Configuration 20 | open class OAuth2Configuration { 21 | 22 | @Configuration 23 | @EnableResourceServer 24 | protected class ResourceServerConfiguration @Autowired constructor(val jedisConnectionFactory: JedisConnectionFactory) : ResourceServerConfigurerAdapter() { 25 | 26 | private val RESOURCE_ID = "RESOURCE_ID_REVIEW" 27 | 28 | @Bean 29 | fun tokenStore(): TokenStore { 30 | return RedisTokenStore(jedisConnectionFactory) 31 | } 32 | 33 | override fun configure(resourceServerSecurityConfigurer: ResourceServerSecurityConfigurer?) { 34 | resourceServerSecurityConfigurer 35 | ?.tokenStore(tokenStore()) 36 | ?.resourceId(RESOURCE_ID) 37 | } 38 | 39 | override fun configure(httpSecurity: HttpSecurity?) { 40 | httpSecurity 41 | ?.authorizeRequests() 42 | ?.antMatchers("/**") 43 | ?.fullyAuthenticated() 44 | ?.and() 45 | ?.addFilterBefore(CorsConfiguration(), ChannelProcessingFilter::class.java) 46 | } 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/configuration/RedisSessionConfiguration.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.configuration 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.context.annotation.Bean 5 | import org.springframework.context.annotation.Configuration 6 | import org.springframework.context.annotation.PropertySource 7 | import org.springframework.core.env.Environment 8 | import org.springframework.data.redis.connection.jedis.JedisConnectionFactory 9 | import org.springframework.data.redis.core.RedisTemplate 10 | import org.springframework.data.redis.serializer.GenericToStringSerializer 11 | import org.springframework.data.redis.serializer.StringRedisSerializer 12 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession 13 | import redis.clients.jedis.JedisPoolConfig 14 | import java.time.Duration 15 | 16 | /** 17 | * Created by rizkimufrizal on 5/28/17. 18 | */ 19 | @Configuration 20 | @PropertySource("classpath:application.yml") 21 | @EnableRedisHttpSession 22 | class RedisSessionConfiguration @Autowired constructor(val environment: Environment) { 23 | 24 | private fun jedisPoolConfig(): JedisPoolConfig { 25 | val jedisPoolConfig = JedisPoolConfig() 26 | jedisPoolConfig.maxTotal = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-total")) 27 | jedisPoolConfig.maxIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.max-idle")) 28 | jedisPoolConfig.minIdle = Integer.parseInt(environment.getRequiredProperty("spring.redis.min-idle")) 29 | jedisPoolConfig.minEvictableIdleTimeMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.min-evictable-idle-time-millis").toLong()).toMillis() 30 | jedisPoolConfig.timeBetweenEvictionRunsMillis = Duration.ofSeconds(environment.getRequiredProperty("spring.redis.time-between-eviction-runs-millis").toLong()).toMillis() 31 | jedisPoolConfig.blockWhenExhausted = environment.getRequiredProperty("spring.redis.block-when-exhausted").toBoolean() 32 | return jedisPoolConfig 33 | } 34 | 35 | @Bean 36 | fun jedisConnectionFactory(): JedisConnectionFactory { 37 | val jedisConnectionFactory = JedisConnectionFactory(jedisPoolConfig()) 38 | jedisConnectionFactory.port = environment.getRequiredProperty("spring.redis.port").toInt() 39 | jedisConnectionFactory.hostName = environment.getRequiredProperty("spring.redis.host") 40 | jedisConnectionFactory.usePool = environment.getRequiredProperty("spring.redis.use-pool").toBoolean() 41 | return jedisConnectionFactory 42 | } 43 | 44 | @Bean 45 | fun redisTemplate(): RedisTemplate { 46 | val redisTemplate = RedisTemplate() 47 | redisTemplate.connectionFactory = jedisConnectionFactory() 48 | redisTemplate.keySerializer = StringRedisSerializer() 49 | redisTemplate.hashValueSerializer = GenericToStringSerializer(Any::class.java) 50 | redisTemplate.valueSerializer = GenericToStringSerializer(Any::class.java) 51 | return redisTemplate 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/controller/ReviewController.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.controller 2 | 3 | import org.rizki.mufrizal.api.gateway.BookReview.repository.ReviewRepository 4 | import org.springframework.beans.factory.annotation.Autowired 5 | import org.springframework.data.domain.Pageable 6 | import org.springframework.http.HttpStatus 7 | import org.springframework.http.ResponseEntity 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.PathVariable 10 | import org.springframework.web.bind.annotation.RequestMapping 11 | import org.springframework.web.bind.annotation.RestController 12 | 13 | /** 14 | * Created by rizkimufrizal on 5/28/17. 15 | */ 16 | @RestController 17 | @RequestMapping(value = "/api") 18 | class ReviewController @Autowired constructor(val reviewRepository: ReviewRepository) { 19 | 20 | @GetMapping(value = "/reviews") 21 | fun reviews(pageable: Pageable): ResponseEntity<*> = ResponseEntity(reviewRepository.findAll(pageable), HttpStatus.OK) 22 | 23 | @GetMapping(value = "/reviews/{id}") 24 | fun review(@PathVariable("id") id: Long): ResponseEntity<*> = ResponseEntity(reviewRepository.findOne(id), HttpStatus.OK) 25 | 26 | @GetMapping(value = "/book/reviews/{id}") 27 | fun reviewBook(@PathVariable("id") id: Long): ResponseEntity<*> = ResponseEntity(reviewRepository.findByBookId(id), HttpStatus.OK) 28 | } -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/domain/Review.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.domain 2 | 3 | import javax.persistence.* 4 | 5 | /** 6 | * Created by rizkimufrizal on 5/28/17. 7 | */ 8 | 9 | @Entity 10 | @Table(name = "tb_review") 11 | data class Review( 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.AUTO) 14 | val id: Long? = null, 15 | @Column(nullable = false) 16 | val bookId: Long? = null, 17 | @Lob 18 | @Column(nullable = false) 19 | val reviewText: String? = null 20 | ) -------------------------------------------------------------------------------- /Book-Review/src/main/kotlin/org/rizki/mufrizal/api/gateway/BookReview/repository/ReviewRepository.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview.repository 2 | 3 | import org.rizki.mufrizal.api.gateway.BookReview.domain.Review 4 | import org.springframework.data.repository.PagingAndSortingRepository 5 | 6 | 7 | /** 8 | * Created by rizkimufrizal on 5/28/17. 9 | */ 10 | interface ReviewRepository : PagingAndSortingRepository { 11 | fun findByBookId(bookId: Long?): Iterable 12 | } -------------------------------------------------------------------------------- /Book-Review/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # konfigurasi oauth2 di spring 1.5.1 2 | security: 3 | oauth2: 4 | resource: 5 | filter-order: 3 6 | 7 | spring: 8 | profiles: 9 | active: development 10 | 11 | #konfigurasi redis 12 | redis: 13 | host: 127.0.0.1 14 | port: 6379 15 | max-total: 128 16 | max-idle: 128 17 | min-idle: 16 18 | min-evictable-idle-time-millis: 60 19 | time-between-eviction-runs-millis : 30 20 | block-when-exhausted : true 21 | use-pool: true 22 | 23 | # spring datasource jpa 24 | datasource: 25 | driver-class-name: org.mariadb.jdbc.Driver 26 | url: jdbc:mariadb://127.0.0.1:3306/database_book?autoReconnect=true 27 | username: root 28 | password: 29 | poolName: SpringBootHikariCP 30 | maximumPoolSize: 5 31 | minimumIdle: 3 32 | maxLifetime: 2000000 33 | connectionTimeout: 30000 34 | idleTimeout: 30000 35 | 36 | # spring hibernate 37 | jpa: 38 | generate-ddl: true 39 | show-sql: true 40 | hibernate: 41 | ddl-auto: update 42 | properties: 43 | hibernate: 44 | format_sql: true 45 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 46 | 47 | # jackson config 48 | jackson: 49 | serialization: 50 | indent_output: true 51 | server: 52 | port: 8082 -------------------------------------------------------------------------------- /Book-Review/src/test/kotlin/org/rizki/mufrizal/api/gateway/BookReview/BookReviewApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package org.rizki.mufrizal.api.gateway.BookReview 2 | 3 | import org.junit.Test 4 | import org.junit.runner.RunWith 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.test.context.junit4.SpringRunner 7 | 8 | @RunWith(SpringRunner::class) 9 | @SpringBootTest 10 | class BookReviewApplicationTests { 11 | 12 | @Test 13 | fun contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple-API-Gateway 2 | 3 | Project ini dibuat dalam rangka belajar membuat API Gateway 4 | 5 | Cara Menjalankan : 6 | 7 | * Jalankan Redis 8 | * Jalankan database MySQL dan buat sebuah database dengan nama `database_book` 9 | * Silahkan akses keempat project, lalu jalankan dengan perintah `gradle bootRun` 10 | 11 | Spesifikasi Port : 12 | 13 | * Book Gateway : 8080 14 | * Book Catalog : 8081 15 | * Book Review : 8082 16 | * Book Authentication : 8083 17 | 18 | Untuk mendapatkan token, silahkan jalankan perintah berikut : 19 | 20 | ```bash 21 | curl -X POST -vu CLIENT_ID_PASSWORD:CLIENT_SECRET_PASSWORD \ 22 | http://localhost:8080/oauth/token \ 23 | -H "Accept: application/json" \ 24 | -d "password=mufrizal&username=rizki&grant_type=password&client_secret=CLIENT_SECRET_PASSWORD&client_id=CLIENT_ID_PASSWORD" 25 | ``` 26 | 27 | Jika Berhasil maka akan muncul output seperti berikut : 28 | 29 | ```json 30 | { 31 | "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiUkVTT1VSQ0VfSURfR0FURVdBWSJdLCJ1c2VyX25hbWUiOiJyaXpraSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE0OTYzMDcyOTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI2OWJkMzY0YS0wODJiLTQ0ZGItOWQzYS01ZTBlODQyZjYwNDUiLCJjbGllbnRfaWQiOiJDTElFTlRfSURfUEFTU1dPUkQifQ.ultPrI5PQNWxPChfL4G7Ja0NhvJcjWnXWYOgts9HLXQ", 32 | "token_type" : "bearer", 33 | "refresh_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiUkVTT1VSQ0VfSURfR0FURVdBWSJdLCJ1c2VyX25hbWUiOiJyaXpraSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI2OWJkMzY0YS0wODJiLTQ0ZGItOWQzYS01ZTBlODQyZjYwNDUiLCJleHAiOjE0OTYzMDcyOTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI3MzcwMTYyNC1jYTQ0LTRhZjItYjZlYy1jNGYzNTljOTcxZTAiLCJjbGllbnRfaWQiOiJDTElFTlRfSURfUEFTU1dPUkQifQ.1wPUJy2S7ONLzPKDhY2IvZrCiX8PSwCy6r7hxkWnczE", 34 | "expires_in" : 1442, 35 | "scope" : "read write", 36 | "jti" : "69bd364a-082b-44db-9d3a-5e0e842f6045" 37 | } 38 | ``` 39 | 40 | Contoh Cara Akses API : 41 | 42 | ```bash 43 | curl "http://localhost:8080/api/books/1" \ 44 | -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiUkVTT1VSQ0VfSURfR0FURVdBWSJdLCJ1c2VyX25hbWUiOiJyaXpraSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE0OTYzMDcyOTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI2OWJkMzY0YS0wODJiLTQ0ZGItOWQzYS01ZTBlODQyZjYwNDUiLCJjbGllbnRfaWQiOiJDTElFTlRfSURfUEFTU1dPUkQifQ.ultPrI5PQNWxPChfL4G7Ja0NhvJcjWnXWYOgts9HLXQ" \ 45 | -H "Content-Type: application/json" 46 | ``` 47 | 48 | Jika Berhasil maka akan muncul output seperti berikut : 49 | 50 | ```json 51 | { 52 | "id": 1, 53 | "title": "Judul Buku 1", 54 | "description": "deskripsi buku ini adalah 1", 55 | "reviews": [ 56 | { 57 | "id": 1, 58 | "bookId": 1, 59 | "reviewText": "ini adalah contoh review no 1-1" 60 | }, 61 | { 62 | "id": 11, 63 | "bookId": 1, 64 | "reviewText": "ini adalah contoh review no 2-1" 65 | }, 66 | { 67 | "id": 21, 68 | "bookId": 1, 69 | "reviewText": "ini adalah contoh review no 3-1" 70 | }, 71 | { 72 | "id": 31, 73 | "bookId": 1, 74 | "reviewText": "ini adalah contoh review no 4-1" 75 | }, 76 | { 77 | "id": 41, 78 | "bookId": 1, 79 | "reviewText": "ini adalah contoh review no 5-1" 80 | }, 81 | { 82 | "id": 51, 83 | "bookId": 1, 84 | "reviewText": "ini adalah contoh review no 6-1" 85 | }, 86 | { 87 | "id": 61, 88 | "bookId": 1, 89 | "reviewText": "ini adalah contoh review no 7-1" 90 | }, 91 | { 92 | "id": 71, 93 | "bookId": 1, 94 | "reviewText": "ini adalah contoh review no 8-1" 95 | }, 96 | { 97 | "id": 81, 98 | "bookId": 1, 99 | "reviewText": "ini adalah contoh review no 9-1" 100 | }, 101 | { 102 | "id": 91, 103 | "bookId": 1, 104 | "reviewText": "ini adalah contoh review no 10-1" 105 | } 106 | ] 107 | } 108 | ``` 109 | 110 | Jika client id tidak memiliki hak akses untuk resource tertentu maka akan ditolak, silahkan jalankan perintah berikut : 111 | 112 | ```bash 113 | curl "http://localhost:8081/api/books" \ 114 | -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiUkVTT1VSQ0VfSURfR0FURVdBWSJdLCJ1c2VyX25hbWUiOiJyaXpraSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJleHAiOjE0OTYzMDcyOTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiI2OWJkMzY0YS0wODJiLTQ0ZGItOWQzYS01ZTBlODQyZjYwNDUiLCJjbGllbnRfaWQiOiJDTElFTlRfSURfUEFTU1dPUkQifQ.ultPrI5PQNWxPChfL4G7Ja0NhvJcjWnXWYOgts9HLXQ" \ 115 | -H "Content-Type: application/json" 116 | ``` 117 | 118 | Karena tidak memiliki akses suatu resource maka akan muncul output seperti berikut. 119 | 120 | ```json 121 | { 122 | "error":"access_denied", 123 | "error_description":"Invalid token does not contain resource id (RESOURCE_ID_BOOK)" 124 | } 125 | ``` 126 | --------------------------------------------------------------------------------