Spring OAuth 2.0 Authorization Server (Authorization Code)

Ferhat Aykan
6 min readJun 29, 2020

--

Merhaba Arkadaşlar bir önceki yazımızda Spring OAuth 2.0 tanımlamaya çalıştık. Bu yazımızda projemizi oluşturup daha önce bahsettiğimiz grant type’lardan Authorization Code kullanarak token alacağız.

Hemen projemizi oluşturmaya başlayalım. Projeyi tek bir repo altında tutucağım için maven module’den faydalanacağım. Bir empty maven projesi oluşturup packaging type’nı pom olarak seçiyoruz.

Spring projelerinde Spring OAuth 2.0 kullanabilmek için

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>

dependencies’lerine ihtiyacımız var.

Daha sonrasında projemize sağ tıkladıktan sonra yeni module oluştur diyip bir maven projesi oluşturuyoruz. Oluşturduğumuz module’n parent’i ‘oauth2-application-medium’ olduğuna dikkat ediyoruz. Parent pom.xml dosyasında da oluşturduğumuz yeni module tanımlanmadıysa tanımlama yapıyoruz.

‘auth-service’mizin hangi porta çalışacağını ve ‘app-name’inin ne olacağına application.yml dosyasında belirtiyoruz.

Bir önceki Spring OAuth 2.0 yazımızda jks dosyası oluşturmuştuk. Projemizde kullanmak için ekliyoruz.

‘auth-service’mize AuthApplication ekleyerek projemizde herhangi bir hata olup olmadığını kontrol etmek için çalıştırıyoruz.

Authorization Server konfigürasyonu yapmaya başlayalım. İlk olarak token type olarak JWT kullanacağımız için

JWTConfiguration ekliyoruz. JKS dosyasını okuyarak KeyPair bean’ını oluşturuyoruz. ‘password’ ve ‘alias’ kısmına jks dosyasını oluşturken girdiğimiz değerleri veriyoruz.

JwtAccessTokenConverter
Token oluşturmada ve gelen token’u çözüp verify etmede yardımcı olacak bean’ımız.

JwtTokenStore
Authorization Server’ımızın iletişim kuracağı bean’dır. Bu bean ile token oluşturma ve verify etme işlemleri sağlanacaktır.

PasswordEncoder
Kullanıcının kayıt olduğunda girdiği password’u veya clientSecret’lerini crypt’ed şeklinde saklıyoruz. Burada BCryptPasswordEncoder kullanacağız.

UserDetails
Projemizde login olan kullanıcın bilgileri repository’den okuyup Spring Security Context’ine yüklemek ve validation işlemleri yapabilmek için Spring’in bizim için oluşturduğu UserDetails interface’dan yararlanarak bir AccountDetails class’ını oluşturuyoruz.

Spring Security’de login olan kullanıcın yetkilerini GrantedAuthority interface’ini kullanarak Spring Context’ine yükleyeceğiz. UserDetails implement ettiğimizde ‘override getAuthorities()’ method’u repository’den okuduğumuz role veya permission bilgilerini bu method aracılığıyla Spring Security’iye vereceğiz.

UserDetailsService
Kullanıcı sisteme giriş yaptığında kullanıcı adı ile arayıp UserDetails bilgilerini dönmemizi sağlayan ve gerektiğinde Spring Security Context’ine kullanıcı bilgilerini yüklememizi sağlayan sınıftır.

CustomUserDetailsService class’ına Spring’in tanımladığı service interface’ini implement ederek repository’den okuduğumuz account ve role bilgilerini convert edip AccountDetails nesnesini dönüyoruz.

AuthenticationManager
Kullanıcı login olmak veya token almak için kullanıcı adı ve şifresini girdiğinde validation işlemleri yapmamızı sağlayan class’tır. Burayı istediğimiz gibi özelleştirerek repository’den veya ldap’dan kullanıcıyı valide edebiliriz.

AuthorizationServerConfigurerAdapter
Client tanımlamalarımızı ve Authorization Server konfigürasyonlarını bu class yardımıyla yapacağız.

ClientDetailsServiceConfigurer ile client’larımızı kaydedeceğiz.
Client’larımızı in memory tutabileceğimiz gibi repository’de de tutabiliriz. Bu sayede bir panel’den yeni client ekleyip silebiliriz. Bize ne kazandırır diye düşünürsek her yeni bir client oluşturulduğunda auth-service girip client’ı ekleyip yeniden deployment etmek zorunda kalmayız.

clientId
Token almaya gelirken hangi client’dan geldiğini clientId ile anlıyoruz.

clientSecret
Token alırken clientSecret’ide gönderiyoruz.

authorities
Client’in yetkilerini belirtiyoruz. Herhangi bir service verdiğimiz yetkiler kadar erişim sağlıyor.

scope
Client token alırken izin verilen scope kadar erişim sağlayabilmesini sağlıyoruz. Örneğin Admin paneline sahip bir client’a write scope yetkisi verirken info client’ınada read scope yetkisi verebiliriz.

WebSecurityConfigurerAdapter
‘auth-service’mizde kullanıcı token almak için geldiğinde login sayfasına yönlendirilmesi gibi birçok konfigürasyonu WebSecurityConfigurerAdapter ile yapacağız. Default filter sıralaması 100. sıradadır. 1.sırada AuthorizationServerConfigurerAdapter bulunmaktadır. 2.sırayada WebSecurityConfigurerAdapter’i koyuyoruz.

Authorization Code ile token almak için neredeyse herşey tamam.
Uygulamamızı çalıştırdıktan sonra

http://localhost:9999/oauth/authorize?response_type=code&client_id=clientX&redirect_uri=http://localhost:8080/token&scope=read

browser’ımızdan yukarıdaki link’e tıklıyoruz. ‘/oauth/authorize’ endpoint’ne sadece authenticated kullanıcılar erişebileceği için bizi login sayfasına yönlendirecektir.
Spring bizim için oluşturduğu login sayfasını kullanabileceğimiz gibi kendi login sayfamızıda yazabiliriz.

Kullanıcı adı ve şifremizi girdikten sonra Sign in butonuna tıklıyoruz. Özel olarak yazdığım CustomAuthenticationManager class’ına akışı görebilmek için debug koyuyorum.

Şifre doğrulamasını yapmadım. UserDetailsService’de oluşturduğum şifre crypt’ed olmadığı için PasswordEncoder ile eşleşme sağlanamayacaktır.

Debug’ı kapatırsak Spring Context’ine Authentication bilgisi oluşturacak ve client’ımızı yetkilendirme sayfasına yönlendirecektir. Bu sayfaya geldiğimizde authenticated olduğumuzu anlamak için Cookies bilgisinden faydalanacak.

İstersek bu sayfayı özelleştirebiliriz. Default ayarlarıyla kullanacağım. Özelleştirmek isteyen StackOverFlow dan faydalanabilir

Eğer Deny butonuna tıklarsak bizi

bu şekilde access denied olarak yönlendirecek. Eğer izin verirsek

bizi bir code ile redirect uri’mize yönlendirecek.

localhost:8080 portunda bir client’ımız çalışsaydı gelen code alıp token alma isteğinde bulunacaktık. Client’ımızı oluşturmadan önce bu code ile Postman’den token alabiliyor muyuz kontrol edelim.

Code ile token alabilmek için ‘/oauth/token’ enpoint’ini kullanacağız ama buna herhangi bir client’dan erişebilmek için authenticated olmamızı istiyor. AuthService üzerinden login olduk ama client bu bilgiyi alamadığı için ‘/oauth/token’ endpoint’ini herkese açık hale getiriyorum. Bu şekilde client’tan erişim sağlayabileceğiz.

Herkese açık hale getirdikten sonra Postman’den altığımız code ile token alma isteği yapalım.

İlk olarak clientId ve clientSecret değerlerini Base64 şeklinde header eklememiz gerekiyor. Bunun için Postman’ın Authorization sekmesinden yararlanabiliriz. PreviewRequest buton’una tıklayıp header’ımıza Authorization bilgisini ekleyebiliriz.

Daha sonra body olarak aldığımız code ve client bilgilerini gönderiyoruz.

JWT Debugger kullanarak token’un içerdiği bilgilere bakabiliriz. Ben JWT Debugger olarak Chrome Extension kullanıyorum.

Client uygulamamızı oluşturmadan AuthorizationServerConfiguration’da autoApprove özelliğini true yaparak yeniden uygulamamızı başlattığımızda Authorize sayfası gelmeden direk code dönecektir.

Client’dan uygulamaya gelirken ‘Cors’a takılmamak için ‘auth-service’mize CorsFilter ekliyoruz ve uygulamamızı yeniden başlatıyoruz.

Client uygulamamızı Javascript’in Vue.js framework’unu kullanacağız. Kullandığım node ve npm version’u

resimdeki gibidir.

Projeyi github’dan çektikten sonra client-app dizininde ‘npm install’ diyerek dependencies’leri yükleyip daha sonra ‘npm run serve’ diyerek uygulamayı çalıştırabilirsiniz.

localhost:8080'a gelen isteği Home sayfamıza yönlendireceğiz.

Login buton’una tıkladıktan sona uygulama bizi ‘auth-service’e yönlendirecek. Login olduktan sonra bizi ‘redirect_uri’ye yönlendirecek.

http://localhost:8080/token redirect ettiğimizde Token page açılıyor ve burada parametre olarak gelen code alıp ‘getTokenWithCode’ methodudan gönderiyoruz. Burada işlem başarılı olursa ‘profile’ page açılıyor.

Token alırken Postman’den yaptığımız gibi code ve client bilgileriyle birlikte ‘auth-service’e post isteğinde bulunuyoruz. Gelen token’ı local storage’mıza kaydediyoruz.

Authorization Code ile token alma konusunu inceledik. Bir sonraki yazımızda grant type’ı password ile token nasıl alınır onu inceleyeceğiz.

İyi çalışmalar.

--

--