TL;DRï¼ ä»åã®æ稿ã§ã¯ãJava ã®ä¸çãèãã§ããæé·ä¸ã®ããã°ã©ãã³ã°è¨èªãKotlin 㧠RESTful API ãéçºããæ¹æ³ãå¦ãã§ããã¾ããCRUD æä½ãå¦çããå°è¦æ¨¡ãª Spring Boot RESTful API ãä½æãããã¨ããå§ãã¾ãããã®å¾ãAuth0 ã§ãã® API ãã»ãã¥ã¢ ã«ããããã«ãã£ã¦ å¤è¦ç´ èªè¨¼ãã½ã¼ã·ã£ã«ãããã¤ãã¼ã¨ã®çµ±åãªã©ããããã®ã»ãã¥ãªãã£æ©è½ãæä¾ãã¾ããæçµçã«ã¯ãJWT ãèªåãã¡ã§ç®¡çããæ¹æ³ãå¦ã³ãAuth0 ã¨ãç¬èªã®ãã¼ã¯ã³ãçºè¡ãã社å ã½ãªã¥ã¼ã·ã§ã³ã¨ãç½®æãã¾ãã
Kotlin ã¨ã¯ä½ã
Kotlin 㯠JetBrains ãéçºããããã°ã©ãã³ã°è¨èªã§ãJava ä»®æ³ãã·ã³(JVM)ã§å®è¡ããJavaScript ã«ãã³ã³ãã¤ã«ããã¾ãããã®ããã°ã©ãã³ã°è¨èªã¯éçåä»ããããå¤æ°ãé¢æ°ãå¼ã¯ã³ã³ãã¤ã«æéã®ç¢ºèªãã§ããã¿ã¤ãã®äºåå®ç¾©ãããå¤ã»ããã使ç¨ãã¾ãã
ãã®ä¸»è¦ç®çã®ã²ã¨ã¤ã¯ Java ã«ä¼´ã£ã¦èµ·ããåé¡ã解決ãããã¨ã§ããä¾ãã°ãJava ã¨æ¯è¼ã㦠Kotlin ã§æ¸ãããã½ããã¦ã§ã¢ã¯ã³ã¼ãè¡ãããã 40% å°ãªãã¨æ³å®ããã¦ãã¾ãããJava ã«ä½¿ç¨ã§ããå å®ããã©ã¤ãã©ãªã®ã»ããã¨ç¸äºéç¨ã§ãã¾ãã
Kotlin 㨠Java ã¯ã©ã®ããã«éãã
ã¾ãæåã«ãæ§æã§ããKotlin ã®æ§æ㯠Java ã®ãã®ã¨è¥å¹²ä¼¼ã¦ãã¾ãããéããããããããã¾ããJetBrains ã¯ãJava éçºè ã Kotlin ã«ç§»åããã¨ãã«ãªããããªå¦ç¿æ²ç·ã«ãªãã¨èª¬æãã¦ãã¾ããããã¯çå®ã§ãããKotlin éçºè ã«ãªããã¨ããæ°ããè¨èªã§æ £ç¨çã³ã¼ããæ¸ããã¨ã¯ãããªã«å®¹æã§ã¯ããã¾ããã
Kotlin ã®ç¹åã«ã¤ãã¦å¦ç¿ããã°ãKotlin ã¯ç¬èªã®ã°ãããµãªãããé«åº¦ãªããã°ã©ãã³ã°è¨èªã§ãããã¨ã«æ°ã¥ãããã¨æãã¾ããä¾ãã°ãKotlin ã«ã¯ ãã¼ã¿ã¯ã©ã¹ãã·ã¼ã«ã¯ã©ã¹ãã¤ã³ã©ã¤ã³é¢æ°ããã®ä»å¤æ°ã®æ©è½ãããã¾ãããããæ©è½ã®ã»ã¨ãã©ã¯ Java ã§ãã©ã¼åã§ãã¾ãããããªã詳細ãªã³ã¼ããæ¸ããã«ã¯ã§ãã¾ãããããçã«æ £ç¨ç㪠Kotlin ã½ã¼ã¹ã³ã¼ã㯠JetBrains ãè¨ãããã«ç°¡åã§ã¯ããã¾ããã
ãããããå®å¿ãã ãããJetBrains ã¯éçºè ã Java ã½ã¼ã¹ã³ã¼ãã Kotlin ã«ç§»åã§ãããããªãã¼ã«ãéçºãã¾ãããKotlin ã® Web ãµã¤ã ã«ã¯ Java ããå¤æ ã¨ãããã¿ã³ãããã¾ãããã®ãã¿ã³ã¯ãJava ã³ã¼ããè²¼ãä»ãã㦠Kotlin ã®ãã¼ã¸ã§ã³ã«ãããã¨ãã§ãã¾ãããã®ã»ãã«ãIntelliJ IDEA ã«ãéçºè ããããå¤æãå®è¡ããæ©è½ãããã¾ãã
Kotlin ãå¦ç¿ãã
ããã¾ã§ Kotlin ã使ç¨ãããã¨ããªããã°ããã®ããã°è¨äºã«å¾ã£ã¦ãããã§ããããã®åã«ãã®è¨èªã«ã¤ãã¦å¦ç¿ãã¦ããç¹ã«å®³ã«ãªããã®ã§ã¯ããã¾ããã次ã®ãªã¹ã㯠Kotlin ã«ã¤ãã¦å¦ã¶ãã¨ãã§ãããªã½ã¼ã¹ãä¸ãã¦ãã¾ãã
- Kotlin ãªãã¡ã¬ã³ã¹ï¼Kotlin ã®æ§æã詳ãã説æããã¦ãã¾ã
- ãã©ã¤ Kotlinï¼ Kotlin ã«ã¤ãã¦å®è·µçã«å¦ã¶ãã¨ãã§ãã¾ã
- Kotlin ã¤ã³ã»ã¢ã¯ã·ã§ã³ï¼ãã®æ°ããè¨èªãæ·±ãæãä¸ãããæ¹ã¯ãå©ç¨ãã ãã
Kotlin ã«ã¤ãã¦ããç¨åº¦çµé¨ãããæ¹ããããã¯ã·ã³ãã«ãª RESTful API ãéçºãããã¨ã¯ç°¡åãçåã«æããã¦ããæ¹ã¯ç¶ãã¦ãèªã¿ãã ããã
Spring Boot Kotlin ã¢ããªã±ã¼ã·ã§ã³ãå§ãã
Spring Initializr 㯠Spring Boot ã¢ããªã±ã¼ã·ã§ã³ãéå§ããã«ã¯ãã£ã¦ã¤ãã®æ¹æ³ã§ããããæãããããã°ã©ãã³ã°è¨èªã®ã²ã¨ã¤ã®ãªãã·ã§ã³ã¨ã㦠Kotlin ãå ãã¾ããï¼æ¬æ¸ã®å·çæç¹ã§ã¯ãJavaãKotlinãGroovy ã® 3 ã¤ã®ãªãã·ã§ã³ãããã¾ããï¼ããã® Web ãµã¤ãã¯ãã¦ã¼ã¶ã¼ãã¢ããªã±ã¼ã·ã§ã³ã®ã©ã¤ãã©ãªã容æã«é¸æã§ããããã«ãã¾ãããã·ã³ãã«ã«ãããããæ¬æ¸ã®ããã«ç¨æãã ãã® GitHub ã¬ãã¸ã㪠ãè¤è£½ãããã¨ããå§ããããããå±éãã¦ããã¾ãã
git clone https://github.com/auth0-blog/kotlin-spring-boot/ cd kotlin-spring-boot
ãã®ã¹ã¿ã¼ãã¢ãã ããã¸ã§ã¯ãã«ã¯ãã§ã« Spring Data JPA 㨠HSQLDB ãæè¼ããã¦ãã¾ãããããæ©è½ã¯ãAPI ãã¦ã¼ã¶ã¼ã«ãã管çãå¯è½ã«ãã 1 ã»ããã®é¡§å®¢ãä¿çãã¾ããç§ãã¡ã®ä»äºã¯é¡§å®¢ã表ã
Customer
Entity Modelãæ°¸ç¶ã¬ã¤ã¤ã¼ãå¦çãã CustomerRepository
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã RESTful ã¨ã³ããã¤ã³ããå®ç¾©ãã CustomerController
ã¯ã©ã¹ãä½æãããã¨ã§ããKotlin ãã¼ã¿ã¯ã©ã¹ãä½æãã
ãã§ã«è¿°ã¹ã¾ããããKotlin ã®æè¯ã®æ©è½ã®ã²ã¨ã¤ã¯é常ã«ç°¡æ½åãããããã°ã©ã è¨èªã ã¨ãããã¨ã§ããJava éçºè ãã¡ã使ãæ £ãã¦ããå®åå¥ã³ã¼ãã®ã»ã¨ãã©ã¯ gettersãsettersãequalsãhashCode ã®ããã«ç°¡æ½åãããæ§æãæ¯æãã¦ãã¾ããå®éã¯ã dropped ã¨ããç¨èªã¯ããã§ã¯é©åã§ã¯ããã¾ãããequals ã hashCode ã®ãããªã¡ã½ããã¯ã³ã³ãã¤ã©ã«ãã£ã¦èªåçã«æ´¾çããã¾ãããå¿ è¦ã§ããã°ãæ確ã«å®ç¾©ã§ãã¾ãã
RESTful API ã®ã¢ã¤ãã£ã¢ã¯ã¦ã¼ã¶ã¼ã顧客ã»ããã管çã§ããããã«ãããã¨ã§ãKotlin ãã¼ã¿ã¯ã©ã¹ ã¯
Customer
ã¨è¨ãã¾ããmodel
ã¨ããæ°ãããã£ã¬ã¯ããªã src/main/kotlin/com/auth0/samples/kotlinspringboot/
ãã£ã¬ã¯ããªã«ä½æãã¦ããã Customer.kt
ã¨ãããã¡ã¤ã«ã«æ¬¡ã®ã½ã¼ã¹ã³ã¼ãã追å ãã¾ããããpackage com.auth0.samples.kotlinspringboot.model import javax.persistence.Entity import javax.persistence.Id import javax.persistence.GenerationType import javax.persistence.GeneratedValue @Entity class Customer( @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = 0, var firstName: String = "", var lastName: String = "" )
Java ã¨ã¯ç°ãªãã¾ãããã¯ã©ã¹ã®å®£è¨ã®å¾ã«
Customer
ã¯ã©ã¹ã®åºæ¬çãªããããã£ãæ¬å¼§ãå©ç¨ãã¦å®ç¾©ãããã¨ã«ã注æãã¦ãã ããã Kotlin ã§ã¯ãããããã©ã¤ã㪠ã³ã³ã¹ãã©ã¯ã¿ã¼ã¨è¨ãã¾ããã¯ã©ã¹ã®æ¬æã§ãããããããã£ãå®ç¾©ããããä»ã®ã³ã³ã¹ãã©ã¯ã¿ã¼ãå®ç¾©ãããã§ãã¾ããããã®ã±ã¼ã¹ã§ã¯ããã§ååã§ããã¾ãã@Id
㨠@GeneratedValue
ã®æ³¨éã id
ããããã£ã«å ãã¾ããã®ã§ãã注æãã ããããã®æ§æ㯠Java ã®æ§æã¨åãã§ãã顧客ç¨ã¬ãã¸ããªãä½æãã
ããããä½æãã
CustomerRepository
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã¯æ£è¦ã® Java Spring Boot ã¢ããªã±ã¼ã·ã§ã³ã§ãããã¨ã¨é常ã«ä¼¼ã¦ãã¾ããã¾ããæ´çããããã«ãpersistence
ã¨ãããã£ã¬ã¯ããªã src/main/kotlin/com/auth0/samples/kotlinspringboot/
ãã£ã¬ã¯ããªã«ä½æãã¾ãããããã®æ°ãããã£ã¬ã¯ããªã«ãCustomerRepository.kt
ã¨ãããã¡ã¤ã«ãä½æãã次ã®ã³ã¼ãã追å ãã¾ããpackage com.auth0.samples.kotlinspringboot.persistence import com.auth0.samples.kotlinspringboot.model.Customer import org.springframework.data.repository.CrudRepository interface CustomerRepository : CrudRepository<Customer, Long>
ãã®ã¤ã³ã¿ã¼ãã§ã¤ã¹ã«ã¯ãã®ããã¸ã§ã¯ãã«ãã HSQLDB ã¤ã³ã¡ã¢ãªãã¼ã¿ãã¼ã¹ã¨ã¤ã³ã¿ã©ã¯ãããããã«å¿ è¦ãªãã¹ã¦ãããã¾ããããã§
save
ãdelete
ãfindAll
ããã®ä»å¤æ° ãå¯è½ã«ãªãã¾ããæ¡å¼µããã°ããã® CrudRepository
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã«ã¤ãã¦ã®è©³ç´°æ
å ±ãå¿
è¦ãªå ´åã¯ããã®ãªã½ã¼ã¹ãã覧ãã ããã顧客 RESTful ã¨ã³ããã¤ã³ããå®ç¾©ãã
ã¦ã¼ã¶ã¼ã®è¦æ±ãå¦çãã RESTful ã¨ã³ããã¤ã³ã㯠Java ã®å¯¾å¿é¨åã¨åæ§ã§ãããå°ãç°¡æ½ã§ãããJava éçºè ã«ã¨ã£ã¦ã¯ããªãä¼¼ã¦ãããã®ã§ããéè¦ãªã¹ãã¼ãã¡ã³ãã¯å¤ãã£ã¦ããªããã¨ã«æ°ã¥ãããã¨æãã¾ããããªã詳細ã§ãããç§ã«ã¨ã£ã¦ã¯è¯ãäºã§ããã®ãããä¾åé¢ä¿ã®åå ãç°¡åã«ç¹å®ã§ãã¾ãã
ã¯ã©ã¹ãä½æããããã«ã
controller
ãã£ã¬ã¯ããªã src/main/kotlin/com/auth0/samples/kotlinspringboot/
ãã£ã¬ã¯ããªã«ä½æãããã¨ããå§ãã¾ãããããã®å¾ãCustomerController.kt
ã¨ãããã¡ã¤ã«ãæ°ãããã£ã¬ã¯ããªã«ä½æãã次ã®ã³ã¼ãã追å ãã¾ããpackage com.auth0.samples.kotlinspringboot.controller import com.auth0.samples.kotlinspringboot.model.Customer import com.auth0.samples.kotlinspringboot.persistence.CustomerRepository import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/customers") class CustomerController(val repository: CustomerRepository) { @GetMapping fun findAll() = repository.findAll() @PostMapping fun addCustomer(@RequestBody customer: Customer) = repository.save(customer) @PutMapping("/{id}") fun updateCustomer(@PathVariable id: Long, @RequestBody customer: Customer) { assert(customer.id == id) repository.save(customer) } @DeleteMapping("/{id}") fun removeCustomer(@PathVariable id: Long) = repository.delete(id) @GetMapping("/{id}") fun getById(@PathVariable id: Long) = repository.findOne(id) }
ãã®ã¯ã©ã¹ã®ã½ã¼ã¹ã³ã¼ãã¯ããã«ãããã¾ãããå®å ¨ãæãããã«ããã®èª¬æã¯æ¬¡ã®ã¨ããã§ãã
注éã¯ãã®ã¯ã©ã¹ã®ãã¹ã¦ã®ã¨ã³ããã¤ã³ãã«ã¯@RequestMapping("/customers")
ãã¬ãã£ã¯ã¹ããããã¨ã宣è¨ãã¦ãã¾ãã/customers
注éã¯@GetMapping
ã¸ã® HTTP GET è¦æ±ãå¦çããã¡ã½ããã¨ãã¦/customer
ãå®ç¾©ãã¾ããfindAll
注éã¯@PostMapping
ã¸ã® HTTP POST è¦æ±ãå¦çããã¡ã½ããã¨ãã¦/customers
ãå®ç¾©ãã¾ããã¾ãããã®ã¡ã½ããã¯é¡§å®¢ã® JSON ãã¼ã¸ã§ã³ãåçããaddCustomer
ã¯ã©ã¹ã«èªåçã«éã·ãªã¢ã«åãã¾ããCustomer
注éã¯@PutMapping("/{id}")
ã¸ã® HTTP PUT è¦æ±ãå¦çããã¡ã½ããã¨ãã¦/customers
ãå®ç¾©ãã¾ãããã®ã¡ã½ãããè¦æ±ã®æ¬æã¨ãã¦updateCustomer
ãåçãã¾ããPUT ã¡ã½ãã㨠POST ã¡ã½ããã®éã㯠PUT ã¡ã½ããã¯è¦æ±ãã¹ã顧客ã®Customer
ãæ´æ°ãããã¨ãæ±ãããã¨ã§ãã{id}
注éã¯@DeleteMapping("/{id}")
ã¸ã® HTTP DELETE è¦æ±ãå¦çããã¡ã½ããã¨ãã¦/customers
ãå®ç¾©ãã¾ãã{id} ã®å ´åãæ¶å»ããã顧客㮠ID ãå®ç¾©ãã¾ããremoveCustomer
注éã¯@GetMapping("/{id}")
ã¸ã® HTTP GET è¦æ±ãå¦çããã¡ã½ããã¨ãã¦/customer/{id}
ãå®ç¾©ããgetById
ã¯å¿çã¨ãã¦ã·ãªã¢ã«åããã顧客ãå®ç¾©ãã¾ãã{id}
ããã ãã§ãããã㧠Spring Boot ã«ãã£ã¦æ¯æ´ãããæåã® Kotlin RESTful API ãã§ãã¾ãããéãã§ã¿ãããã°ãã¢ããªã±ã¼ã·ã§ã³ã®ã«ã¼ããã£ã¬ã¯ããªã«
mvn spring-boot:run
ã¨ã¿ã¤ãããã¨ãSpring Boot ãèµ·åãã¾ãããã®å¾ã次ã®ã³ãã³ãã使ã£ã¦ API ã¨ã¤ã³ã¿ã©ã¯ããã¾ãã# adds a new customer curl -H "Content-Type: application/json" -X POST -d '{ "firstName": "Bruno", "lastName": "Krebs" }' http://localhost:8080/customers # retrieves all customers curl http://localhost:8080/customers # updates customer with id 1 curl -H "Content-Type: application/json" -X PUT -d '{ "id": 1, "firstName": "Bruno", "lastName": "Simões Krebs" }' http://localhost:8080/customers/1 # deletes customer with id 1 curl -X DELETE http://localhost:8080/customers/1
ä½ãåé¡ãçºçããããGitHub ã¬ãã¸ããªã®é¡§å®¢ãã©ã³ã ã§ã½ã¼ã¹ã³ã¼ããæ¯è¼ãã¦ãã ããã
Kotlin RESTful API ã Auth0 ã§ã»ãã¥ã¢ã«ãã
ã覧ã®ããã«ãAPI ã Auth0 ã§ã»ãã¥ã¢ã«ãããã¨ã¯é常ã«ç°¡åã§ãããããã®æ©è½ãæä¾ãã¾ããAuth0 ã§ã¯ãã·ã³ã°ã«ãµã¤ã³ãªã³ãã¦ã¼ã¶ã¼ç®¡çãã½ã¼ã·ã£ã« ID ãããã¤ãã¼ï¼FacebookãGitHubãTwitterãªã©ï¼ã®ãµãã¼ããã¨ã³ã¿ã¼ãã©ã¤ãºï¼Active DirectoryãLDAPãSAMLãªã©ï¼ãç¬èªã®ã¦ã¼ã¶ã¼ãã¼ã¿ãã¼ã¹ ãå«ãã確ã㪠ã¢ã¤ãã³ãã£ãã£ç®¡çã½ãªã¥ã¼ã·ã§ã³ ãåå¾ããã³ã¼ãè¡ãæ°è¡æ¸ããªããã°ãªãã¾ããã
ã¾ã ãã®ãããªãã¨ããããã¨ããªãåå¿è ã«ã¯ãç¡æ Auth0 ã¢ã«ã¦ã³ã ã®ç»é²ããå§ããã¾ããAuth0 ã¢ã«ã¦ã³ãããæã¡ã®æ¹ã¯ãã¾ã ããã·ã¥ãã¼ãã« API ãä½æ ãã¦ãã ãããAPI ã¯å¤é¨ãªã½ã¼ã¹ã表ãã¨ã³ãã£ãã£ã§ãã¯ã©ã¤ã¢ã³ããè¦æ±ããä¿è·ããã¦ãããªã½ã¼ã¹ã®åçã¨å¿çãå¯è½ã«ãã¾ãããã® API ã¯ãæ§ç¯ããã°ããã® Kotlin ã¢ããªã®æ£ç¢ºãªæ©è½ã§ãã
å é²èªè¨¼ãå§ããããã« Auth0 ã¯å¯å¤§ãªç¡æã¬ãã«ãæä¾ãã¦ãã¾ãã
API ãä½æããã¨ããæ°ããAPI ã®ãã¬ã³ããªåã®
Name
ãaccess_token
ãè¦æ±ããã¨ãã«ä½¿ç¨ãã String
ã® Identifier
ããã® API ã access_token
ãç»é²ãããã㫠対称 ã¾ã㯠é対称ã¢ã«ã´ãªãºã ã使ç¨ããå ´åã«å®ç¾©ãã Signing Algorithm
ã® 3 ã¤ã®åéãå®ç¾©ããªããã°ãªãã¾ããããã®å ´åããããã®åéããããã Kotlin RESTful API
ãkotlin-jwts
ãRS256
ï¼é対称ã¢ã«ã´ãªãºã ã使ç¨ãã¾ãï¼ã«ãã¾ããAuth0 ã¯ç°ãªã OAuth 2.0 ã¯ã¢ã¯ã»ã¹ãã¼ã¯ã³ãè¦æ±ããããã«ããã¼ãã¾ã ããµãã¼ããã¾ãããã®å ´åãä¾ãã·ã³ãã«ã«ããããã«ã API & ä¿¡é ¼ããã¯ã©ã¤ã¢ã³ãã®ããã¼ ã使ç¨ãã¾ãããã®ããã¼ã¯å®è£ ãæãã·ã³ãã«ã§ãããã¯ã©ã¤ã¢ã³ãã¢ããªã±ã¼ã·ã§ã³ã 絶対çã«ä¿¡é ¼ããã å ´å ã ã 使ç¨ãã¹ãã§ãããã¨ã«çæãã¦ãã ãããã»ã¨ãã©ã®å ´åå¥ã®ããã¼ãå¿ è¦ã§ãAuth0 ã® ãã©ã® OAuth 2.0 ããã¼ã使ç¨ãã¹ããï¼ã ã®è¨äºã¯ãã¼ãºã«åã£ãé©åãªã¢ããã¼ããé¸ã¶ã¨ãã«å½¹ç«ã¡ã¾ãã
API & ä¿¡é ¼ãããã¯ã©ã¤ã¢ã³ã ããã¼ã使ç¨ããã«ã¯ãã¾ã Auth0 ã¢ã«ã¦ã³ãã§
Default Directory
ããããã£ãæ§æããªããã°ãªãã¾ããããã®ããã«ã¯ãã¢ã«ã¦ã³ãè¨å® ãã¼ã¸ã«ç§»åãã Default Directory
ããããã£ã®å¤ã¨ã㦠Username-Password-Authentication
ã追å ãã¾ãããã®å¤ã¯ Auth0 ã¢ã«ã¦ã³ãã§æ¢å®ã§è¡¨ç¤ºããã ãã¼ã¿ãã¼ã¹æ¥ç¶ ã®ååã§ããã¾ããã¯ã©ã¤ã¢ã³ã ã§
Password
ä»ä¸ã¿ã¤ããæå¹ã«ããå¿
è¦ãããã¾ããä¸è¨ã§èª¬æããããã« API ãä½æããããAuth0 㯠Kotlin RESTful API (Test Client)
ã¨ããã¯ã©ã¤ã¢ã³ããèªåçã«ä½æãã¾ãããã®è¨å®ã«ã¢ã¯ã»ã¹ã㦠Show Advanced Settings
ãªãã·ã§ã³ãã¯ãªãã¯ããGrant Types
ã¿ãã® Password
ããã§ãã¯ãããã®å¤æ´ãä¿åãã¾ããã³ã¼ããå¤æ´ãã
./src/main/resources
ã®ä¸ã« application.properties
ã¨ãããã¡ã¤ã«ãããã¾ãããã®ãã¡ã¤ã«ã Auth0 ã¢ã«ã¦ã³ãã®ãã¼ã¿ã§èªã¿è¾¼ãå¿
è¦ãããã¾ããæ°è¦ã¢ã«ã¦ã³ããä½æãã¦ããã¨ãã«æ¢å®ã§ããã®å ´åã«ä½¿ç¨ã§ãããæ¢å®ã®ã¢ããªãã表示ããã¾ãããããã¯ãã®æ§æã§éè¦ãªè¦ç´ ãªã®ã§ããã®å¤ã¨æ¬¡ã®ã¢ããªã±ã¼ã·ã§ã³ã®å¤ãç½®æãããã¨ãå¿ããªãã§ãã ããã# this is the identifier of the API that we just created auth0.audience=kotlin-jwts # replace YOUR-DOMAIN to get something like https://bkrebs.auth0.com/ auth0.issuer=https://YOUR-DOMAIN.auth0.com/
ã³ã¼ãã«é²ãåã«ã次ã®ããã« Maven æ§æã« 3 ã¤ã®ä¾åé¢ä¿ã追å ããå¿ è¦ãããã¾ãã
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>auth0</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>auth0-spring-security-api</artifactId> <version>1.0.0-rc.2</version> </dependency>
ãããçµãã£ããã
WebSecurityConfig.kt
ã¨ãããã¡ã¤ã«ã src/main/kotlin/com/auth0/samples/kotlinspringboot/
ãã£ã¬ã¯ããªã«æ¬¡ã®ã½ã¼ã¹ã³ã¼ãã§ä½æãã¾ããpackage com.auth0.samples.kotlinspringboot.security import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import com.auth0.spring.security.api.JwtWebSecurityConfigurer import org.springframework.beans.factory.annotation.Value @Configuration @EnableWebSecurity open class WebSecurity : WebSecurityConfigurerAdapter() { @Value("\${auth0.audience}") private val audience: String? = null @Value("\${auth0.issuer}") private val issuer: String? = null @Throws(Exception::class) override fun configure(http: HttpSecurity) { http.authorizeRequests() .anyRequest().authenticated() JwtWebSecurityConfigurer .forRS256(audience, issuer!!) .configure(http) } }
ããã ãã§ããAuth0 ã Kotlin Spring Boot RESTful API ã§ä½¿ç¨ããããã«å¿ è¦ãªãã¨ã¯ããã ãã§ããã¢ããªã±ã¼ã·ã§ã³ã®ã»ãã¥ãªãã£ããã¹ãããããã«ãã¢ããªã±ã¼ã·ã§ã³ãå度å®è¡ãã¾ãããã
mvn spring-boot:run
è¦æ±ã API ã«çºè¡ããã¢ã¯ã»ã¹ãã¼ã¯ã³ãåå¾ããåã«ãã¾ã Auth0 ã®æ°è¦ã¦ã¼ã¶ã¼ãä½æããå¿ è¦ãããã¾ãããã®ããã«ã¯ã
/dbconnections/signup
ã¨ã³ããã¤ã³ãã« POST
è¦æ±ãçºè¡ããå¿
è¦ãããã¾ãããã®è¦æ±ã¯æ¬¡ã® JSON æ¬æã®å¾ã« Content-Type
ãããã¼ã¨ application/json
ãå¿
è¦ã§ããcurl -H "Content-Type: application/json" -X POST -d '{ "client_id": "hfs2Au7Zka9XYbXs0CRpdmaL33IKy4mA", "email": "[email protected]", "password": "123123", "connection": "Username-Password-Authentication" }' https://bkrebs.auth0.com/dbconnections/signup # response: # {"_id":"xxx","email_verified":false,"email":"[email protected]"}
ãã®å¾ã
POST
è¦æ±ã https://YOUR-DOMAIN.auth0.com/oauth/token
ã«çºè¡ã㦠access_token
ãåå¾ãã¾ãããã®è¦æ±ã«ã次ã®ããã«æ¬æ㨠Content-Type
ãããã¼ã« JSON ãªãã¸ã§ã¯ããå«ãå¿
è¦ãããã¾ããcurl -H "Content-Type: application/json" -X POST -d '{ "grant_type":"password", "username": "[email protected]", "password": "123123", "audience": "kotlin-jwts", "client_id": "hfs2Au7Zka9XYbXs0CRpdmaL33IKy4mA", "client_secret": "Hx4eFNAT8TI2TUVDXhxWDJ8vWpZxt79DQYUl7e178Uw0ASfc7eY42zPf2H-Gv1n1" }' https://bkrebs.auth0.com/oauth/token # response: # {"access_token":"xxx.yyy.zzz","expires_in":86400,"token_type":"Bearer"}
両æ¹ã®è¦æ±ã®
client_id
ããã³ client_secret
ããããã£ã¯ç¶æ³ã«å¿ã㦠å¤æ´ãããªããã°ãªããªã ãã¨ã«æ³¨æãã¦ãã ããããããã®å¤ã¯ Auth0 ãä½æãã Kotlin RESTful API (Test Client) ã¯ã©ã¤ã¢ã³ãã§è¦ã¤ãããã¾ãããããã®å¤ãåå¾ããã«ã¯ ã¯ã©ã¤ã¢ã³ããã¼ã¸ ã«ç§»åãã¦ãã ããããã®æå¾ã®è¦æ±ãçºè¡ããã¨ã
access_token
ãåå¾ã§ãã¾ããããããã¯ãKotlin API ã«éä¿¡ããè¦æ±ã®ãããã¼ã§ãã®ãã¼ã¯ã³ã使ç¨ãã¾ãã®ã§ããã® access_token
ã§ã¨ã³ããã¤ã³ããã¯ã¨ãªããã¨ã顧客ã®ã»ãããå度管çã§ããããã«ãªãã¾ãã# no token = no access: curl http://localhost:8080/customers # token = access curl -H "Authorization: Bearer xxx.yyy.zzz" http://localhost:8080/customers
âKotlin RESTful API ã Auth0 ã§ç°¡åã«ã»ãã¥ã¢ã«ããâ
ããããã¤ã¼ããã
Kotlin ãç¬èªã®ã½ãªã¥ã¼ã·ã§ã³ã§ã»ãã¥ã¢ã«ãã
ä½ããã®çç±ã§ Auth0 ã§ã»ãã¥ã¢ã«ãªã RESTful API ãå¸æããªãå ´åã¯ãæ¬ç« ã§èª¬æããã¹ãããã«å¾ã£ã¦ãã ãããã¾ãã
pom.xml
ãã Auth0 ã®ä¾åé¢ä¿ãåé¤ãã¾ãã<dependency> <groupId>com.auth0</groupId> <artifactId>auth0</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>auth0-spring-security-api</artifactId> <version>1.0.0-rc.2</version> </dependency>
ãã®å¾ã«ã
application.properties
ãã¡ã¤ã«ã«è¿½å ãã 2 ã¤ã®ããããã£ã¯ä½¿ç¨ãã¾ããã®ã§åé¤ãã¾ãããããããJWT ãçºè¡ã»æ¤è¨¼ããã«ã¯ã次㮠Maven ä¾åé¢ä¿ã追å ãã¾ãã<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
ã¦ã¼ã¶ã¼ãå¦çãã
API ã®è¤æ°ã®ã¦ã¼ã¶ã¼ããµãã¼ãããã«ã¯ãã¾ã
ApplicationUser.kt
ãApplicationUserRepository.kt
ãSignUpController.kt
ã® 3 ã¤ã®ã¯ã©ã¹ãä½æãã¾ãããããã¯ã©ã¹ã¯é¡§å®¢ã®ç®¡çããµãã¼ãããã»ã¨ãã©ã¯ã©ã¹ã®ããã«åä½ãã¾ããæåã®ã¯ã©ã¹ãApplicationUser.kt
ã¯ã©ã¹ã¯ model
ããã±ã¼ã¸ã«ä½æããã次ã®ã³ã¼ããå«ã¿ã¾ããpackage com.auth0.samples.kotlinspringboot.model import javax.persistence.Entity import javax.persistence.GeneratedValue import javax.persistence.GenerationType import javax.persistence.Id @Entity class ApplicationUser( @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long = 0, var username: String = "", var password: String = "" )
æ°ãããã®ã¯ä½ãããã¾ãããã¦ã¼ã¶ã¼ã®ããããã£ãä¿çããå¥ã®ãã¼ã¿ã¯ã©ã¹ã ãã§ãããã®å¾ã
ApplicationUserRepository.kt
ã¯ã©ã¹ã persistence
ããã±ã¼ã¸ã«æ¬¡ã®ã³ã¼ãã§ä½æãã¾ããpackage com.auth0.samples.kotlinspringboot.persistence import com.auth0.samples.kotlinspringboot.model.ApplicationUser import org.springframework.data.repository.CrudRepository interface ApplicationUserRepository : CrudRepository<ApplicationUser, Long> { fun findByUsername(username: String): ApplicationUser? }
ãã®å ´åã®
CustomerRepository
ã¨æ¯è¼ããå ´åã®å¯ä¸ã®éã㯠findByUsername
ã¨ããã¡ã½ãããå®ç¾©ãããã¨ã§ãããã®ã¡ã½ããã¯ãã¦ã¼ã¶ã¼ãã¦ã¼ã¶ã¼åãè¦ã¤ããããã«ç¬èªã®ã½ãªã¥ã¼ã·ã§ã³ã§ä½¿ç¨ããã¾ããããã§ãæå¾ã®ã¯ã©ã¹ SignUpController.kt
ã¯æ¬¡ã®ã³ã¼ã㧠controller
ããã±ã¼ã¸ã«ä½æãã¾ããpackage com.auth0.samples.kotlinspringboot.controller import com.auth0.samples.kotlinspringboot.model.ApplicationUser import com.auth0.samples.kotlinspringboot.persistence.ApplicationUserRepository import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/sign-up") class SignUpController(val applicationUserRepository: ApplicationUserRepository, val bCryptPasswordEncoder: BCryptPasswordEncoder) { @PostMapping fun signUp(@RequestBody applicationUser: ApplicationUser) { applicationUser.password = bCryptPasswordEncoder.encode(applicationUser.password) applicationUserRepository.save(applicationUser) } }
ãã®ã³ã³ããã¼ã©ã¼ã§å®ç¾©ãããå¯ä¸ã®ã¨ã³ããã¤ã³ãã¯
signUp
ã§ãæ°è¦ã¦ã¼ã¶ã¼ã«ããã¢ããªã±ã¼ã·ã§ã³ã®ç»é²ãå¯è½ã«ãã¾ãããµã¤ã³ã¤ã³ ããã»ã¹ã¨ãã¼ã¯ã³ã®æ¤è¨¼ã¯å¾ã»ã©èª¬æãã¾ãããå¥ã®é åã§å¦çããã¾ããæçµçãªãã¼ã¿æ¼æ´©ã§ãããã¦ã¼ã¶ã¼ã®ãã¹ã¯ã¼ããã»ãã¥ã¢ã«ããã«ã¯ãSpring Security ã«ä»ãã¦ãã BCryptPasswordEncoder
ã¯ã©ã¹ã使ç¨ãã¦ãã¹ã¦ã®ãã¹ã¯ã¼ããã¨ã³ã³ã¼ããããã¨ã«ãçæãã ãããJWT ã Kotlin ã§çºè¡ã»æ¤è¨¼ãã
ããã§
User
ãã¼ã¿ã¯ã©ã¹ããããããã¨ã³ããã¤ã³ãã«ãã£ã¦æ°è¦ã¦ã¼ã¶ã¼ãèªåã§ç»é²ã§ããã®ã§ãAPI ã¨ã¤ã³ã¿ã©ã¯ãã§ããããã«ããåã«ãããã¦ã¼ã¶ã¼ããµã¤ã³ã¤ã³ããJWT ãæ¤è¨¼ããå¿
è¦ãããã¾ãããããå®ç¾ããã«ã¯ãJWTAuthenticationFilter
ãJWTAuthorizationFilter
ãUserDetailsServiceImpl
ã® 2 ã¤ã®ãã£ã«ã¿ã¨ 1 ã¤ã®ã¯ã©ã¹ãä½æãã¾ãããµã¤ã³ã¤ã³æ©è½ãæ
å½ããæåã®ãã£ã«ã¿ã¯ JWTAuthenticationFilter.kt
ã¨å¼ã°ããæ°è¦ãã£ã¬ã¯ããªãã¡ã¤ã«ã« WebSecurity
ã¯ã©ã¹ã¨ãã¦åãããã±ã¼ã¸ã«ä½æããã¾ãããã®ãã¡ã¤ã«ã«ã¯æ¬¡ã®ã½ã¼ã¹ã³ã¼ããããã¾ããpackage com.auth0.samples.kotlinspringboot import com.auth0.samples.kotlinspringboot.model.ApplicationUser import com.fasterxml.jackson.databind.ObjectMapper import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.Authentication import org.springframework.security.core.AuthenticationException import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.userdetails.User import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import java.io.IOException import java.util.Date import javax.servlet.FilterChain import javax.servlet.ServletException import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class JWTAuthenticationFilter(authManager: AuthenticationManager) : UsernamePasswordAuthenticationFilter() { init { authenticationManager = authManager } @Throws(AuthenticationException::class, IOException::class, ServletException::class) override fun attemptAuthentication( req: HttpServletRequest, res: HttpServletResponse): Authentication { val creds = ObjectMapper() .readValue(req.inputStream, ApplicationUser::class.java) return authenticationManager.authenticate( UsernamePasswordAuthenticationToken( creds.username, creds.password, emptyList<GrantedAuthority>() ) ) } @Throws(IOException::class, ServletException::class) override fun successfulAuthentication( req: HttpServletRequest, res: HttpServletResponse, chain: FilterChain?, auth: Authentication) { val JWT = Jwts.builder() .setSubject((auth.principal as User).username) .setExpiration(Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact() res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT) } }
ãã®ãã£ã«ã¿ã¯ã
- ã¦ã¼ã¶ã¼ããè³æ ¼æ
å ±ã解æããããããèªè¨¼ãã
ãattemptAuthentication
- ã¦ã¼ã¶ã¼ã®èªè¨¼ãæåããã¨ãã« JWT ãä½æãã
ã® 2 ã¤ã®æ©è½ãå®ç¾©ãã¾ããsuccessfulAuthentication
ããã両æ¹ã®ãã£ã«ã¿ã¯
SECRET
ã EXPIRATION_TIME
ã®ããã«ä¸é¨æªå®ç¾©ã®å®æ°ã使ç¨ãããã¨ã«ãçæãã ããããããå®æ°ãå®ç¾©ããã«ã¯ã次ã®ã³ã¼ã㧠SecurityConstants.kt
ã¨ãããã¡ã¤ã«ãåããã£ã¬ã¯ããªã«ä½æãã¾ããpackage com.auth0.samples.kotlinspringboot val SIGN_UP_URL = "/sign-up" val SECRET = "SecretKeyToGenJWTs" val TOKEN_PREFIX = "Bearer " val HEADER_STRING = "Authorization" val EXPIRATION_TIME: Long = 864_000_000 // 10 days
ä¸è¨ã®ãã£ã«ã¿ãä½æãããã¼ã¯ã³ãæ¤è¨¼ããã«ã¯ã2ã¤ãã®ãã£ã«ã¿
JWTAuthorizationFilter
ãå¿
è¦ã§ãããã®ãã£ã«ã¿ã¯æ¬¡ã®ã³ã¼ãã§åããã£ã¬ã¯ããªå
ã«ä½æããã¾ããpackage com.auth0.samples.kotlinspringboot import io.jsonwebtoken.Jwts import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.Authentication import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.web.authentication.www.BasicAuthenticationFilter import java.io.IOException import java.util.Collections.emptyList import javax.servlet.FilterChain import javax.servlet.ServletException import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse class JWTAuthorizationFilter(authManager: AuthenticationManager) : BasicAuthenticationFilter(authManager) { @Throws(IOException::class, ServletException::class) override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) { val header = request.getHeader(HEADER_STRING) if (header == null || !header.startsWith(TOKEN_PREFIX)) { chain.doFilter(request, response) return } val authentication = getAuthentication(request) SecurityContextHolder.getContext().authentication = authentication chain.doFilter(request, response) } fun getAuthentication(request: HttpServletRequest): Authentication? { val token = request.getHeader(HEADER_STRING) if (token != null) { // parse the token. val user = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) .getBody() .getSubject() return if (user != null) UsernamePasswordAuthenticationToken(user, null, emptyList<GrantedAuthority>()) else null } return null } }
ãã®ãã£ã«ã¿ã¯ã»ãã¥ãªãã£ä¿è·ãããã¨ã³ããã¤ã³ããè¦æ±ãããã¨ãã«ä½¿ç¨ããã
Authorization
ãããã¼ã«ãã¼ã¯ã³ãããã°ãã§ãã¯ã«ãã£ã¦éå§ãã¾ããä½ã¨ããã¼ã¯ã³ãè¦ã¤ããã°ããã®æ¤è¨¼ãè¡ããããã®ã¦ã¼ã¶ã¼ã SecurityContext
ã«è¨å®ãã¾ãããã¼ã¯ã³ãè¦ã¤ãããªããã°ããã®è¦æ±ã Spring Security ãã£ã«ã¿ãã§ã¼ã³ã«å¿ãã¦ç§»åããããããããã®è¦æ±ã¯ 401ï¼æ¿èªããã¦ãã¾ããï¼ç¶æ
ã³ã¼ãã®å¿çãåãã¾ããä½æããå¿ è¦ãããæå¾ã®ã¯ã©ã¹ã¯
UserDetailsServiceImpl
ã§ãããã®ã¯ã©ã¹ã¯ Spring Security ãã UserDetailsService
ã¯ã©ã¹ãæ¡å¼µãããã¼ã¿ãã¼ã¹ã§ã¦ã¼ã¶ã¼ãè¦ã¤ããæ
å½ãªã®ã§ãSpring Security ããã®è³æ ¼æ
å ±ããã§ãã¯ã§ãã¾ãããã®ã¯ã©ã¹ã¯ã¡ã¤ã³ kotlinspringboot
ãã£ã¬ã¯ããªã«ä½æããã次ã®ã½ã¼ã¹ã³ã¼ããæ ¼ç´ãã¾ããpackage com.auth0.samples.kotlinspringboot import com.auth0.samples.kotlinspringboot.model.ApplicationUser import com.auth0.samples.kotlinspringboot.persistence.ApplicationUserRepository import org.springframework.security.core.userdetails.User import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.core.userdetails.UsernameNotFoundException import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service open class UserDetailsServiceImpl(val userRepository: ApplicationUserRepository) : UserDetailsService { @Transactional(readOnly = true) @Throws(UsernameNotFoundException::class) override fun loadUserByUsername(username: String): UserDetails { val user = userRepository.findByUsername(username) ?: throw UsernameNotFoundException(username) return User(user.username, user.password, emptyList()) } fun save(user: ApplicationUser) { userRepository.save(user) } }
ãã®ç¬èªã®ã½ãªã¥ã¼ã·ã§ã³ãã¾ã¨ããããã«ã
WebSecurity
ã¯ã©ã¹ã®ã³ã³ãã³ãã¨æ¬¡ãç½®æãã¾ããpackage com.auth0.samples.kotlinspringboot import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.http.HttpMethod import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder @Configuration @EnableWebSecurity open class WebSecurity(val userDetailsService: UserDetailsService) : WebSecurityConfigurerAdapter() { @Bean fun bCryptPasswordEncoder(): BCryptPasswordEncoder { return BCryptPasswordEncoder() } override fun configure(http: HttpSecurity) { http.csrf().disable().authorizeRequests() .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() .anyRequest().authenticated() .and() .addFilter(JWTAuthenticationFilter(authenticationManager())) .addFilter(JWTAuthorizationFilter(authenticationManager())) } override fun configure(auth: AuthenticationManagerBuilder?) { auth!!.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()) } }
ããããå¤æ´ããããå度 API ã¨ã¤ã³ã¿ã©ã¯ãã§ããããã«ãªãã次ã®ããã« JWT ãé©åã«ä½æã»æ¤è¨¼ãã¦ãããããã§ãã¯ãã¾ãã
# run Kotlin app again mvn spring-boot:run # register a new user curl -H "Content-Type: application/json" -X POST -d '{ "username": "admin", "password": "password" }' http://localhost:8080/sign-up # login to get the JWT (in the Authorization header) curl -i -H "Content-Type: application/json" -X POST -d '{ "username": "admin", "password": "password" }' http://localhost:8080/login # get customers passing the JWT contained by the Authorization header curl -H "Authorization: Bearer xxx.yyy.zzz" http://localhost:8080/customers
ã覧ã®ããã«ãJWT ã§ç¬èªã®ã»ãã¥ãªãã£ã½ãªã¥ã¼ã·ã§ã³ãä½æãããã¨ã¯ãããªã«é£ããã¯ããã¾ããããã ããAuth0 ã§çµ±åããããã«å®è¡ããä½æ¥ããããã£ã¨å¤ãã®ä½æ¥ãè¦ãã¾ããå¤è¦ç´ èªè¨¼ãã½ã¼ã·ã£ã« ID ãããã¤ãã¼ãã¨ã³ã¿ã¼ãã©ã¤ãºæ¥ç¶ï¼Active DirectoryãLDAPãSAMLãªã©ï¼ã®ãããªããã«é«åº¦ãªãããã¯ã«ã¯å¯¾å¦ãã¾ããã§ããããã®ãããªã±ã¼ã¹ãå¦çããã«ã¯ããã«å¤ãã®ä½æ¥ãå¿ è¦ã«ãªãã¾ãããããæ©è½ããªãã¨ãç´ æ©ãå®ç¾ããã¨ãã¦ããAuth0 ã使ã£ãã¨ãã¨åããããªã»ãã¥ãªãã£å¯¾çãè¬ãããã¨ã¯ã§ãã¾ããã
ã¾ã¨ã
Java éçºè ã«ã¨ã£ã¦ Kotlin ã§ã³ã¼ããæ¸ããã¨ã¯ã注æãã¹ãå±éºããããªã«ãªãã®ã§ããããªã«é£ãããã¨ã§ã¯ããã¾ãããããããè¨èªã®ãã«ãã¯ã¼ã¨æé«ã®æ©è½ã使ã£ã¦çã® Kotlin éçºè ã«ãªãã«ã¯å®¹æã§ã¯ãªããæ°æéã®å¦ç¿ã¨éçºãå¿ è¦ã§ããKotlin ã¨æ¢åã® Java ã©ã¤ãã©ãªã¨ã®çµ±å㯠Spring Boot ã使ç¨ã§ããã®ã§é常ã«è¯ããã¨ã§ãéçºããã³ã¼ãã¯é常ã«å®çµã§èªã¿ããããã®ã§ãããKotlin ã®æ©è½ã«ã¤ãã¦ãã¾ãåãæ±ãã¾ããã§ãããããã®é©ç¨æ§ãæå¾ã«æ¤è¨¼ãã¾ããã
âããã¯ã¨ã³ãã® Kotlin ã¢ããªã±ã¼ã·ã§ã³ãéçºãããã¨ã¯å ¨ãåé¡ããªãç°¡åã§ããâ
ããããã¤ã¼ããã
ãããã§ããï¼Kotlin ãæ¯æã㦠Java ãæ念ãã¾ãããï¼
About the author
Bruno Krebs
R&D Content Architect (Auth0 Alumni)