Merhabalar arkadaşlar.

Bu yazıda Spring Rest ile oluşturulmuş basit bir web servisin Spring Boot tabanlı bir maven projesinde Spring Security ile koruma altına alınmasını temel olarak anlatacağım. Bu yazıda başlıkta da belirttiğim üzere inmemory user kullanarak doğrulama ve yetkilendirme işine bakacağız. Bir sonraki yazıda ise veritabanında tanımlı user ile bu işlemi örnekleyeceğiz.

Bu yazıdaki uygulamanın kodlarına https://github.com/ilkgunel/SpringSecurityWithSpringBoot/tree/master adresinden erişebilirsiniz.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.ilkaygunel</groupId>
	<artifactId>SpringSecurityWithSpringBoot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>SpringSecurityWithSpringBoot</name>
	<description>Example usage project for Spring Security with Spring Boot</description>

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

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
	</dependencies>

	<properties>
		<java.version>9</java.version>
	</properties>

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

	<repositories>
		<repository>
			<id>spring-releases</id>
			<url>https://repo.spring.io/libs-release</url>
		</repository>
	</repositories>
	<pluginRepositories>
		<pluginRepository>
			<id>spring-releases</id>
			<url>https://repo.spring.io/libs-release</url>
		</pluginRepository>
	</pluginRepositories>
</project>

pom.xml dosyamız içerisinde parent tag’inin altında Spring Boot kütüphanelerinin yer aldığı projeyi tanıtmış olduk. Projemize bağımlılık olarak Spring’in web ve security kütüphanelerini ekledik.

application.properties

server.contextPath=/SpringSecurityWithSpringBoot
server.port=8080

application.properties dosyamız içinde projenin deploy edileceği context path ayarını ve çalışacağı portu belirliyoruz.

Address.java

package com.ilkaygunel.pojo;

public class Address {
	
	public Address(){
		
	}
	
	public Address(String neighborhood,String borough,String country){
		this.neighborhood = neighborhood;
		this.borough = borough;
		this.country = country;
	}
	
	private String neighborhood;
	private String borough;
	private String country;

	public String getNeighborhood() {
		return neighborhood;
	}

	public void setNeighborhood(String neighborhood) {
		this.neighborhood = neighborhood;
	}

	public String getBorough() {
		return borough;
	}

	public void setBorough(String borough) {
		this.borough = borough;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

}

Address sınıfımız içerisinde bir üyenin adresine ait bazı bilgileri tutacak alanlar ve onların get-set metotları yer alıyor. Sınıf içerisinde hiç parametre almayan ve tüm değerleri parametre alan iki constructor bulunuyor.

Member.java

package com.ilkaygunel.pojo;

public class Member {
	public Member() {

	}

	public Member(String name, String surname, String city, String company,Address address) {
		this.name = name;
		this.surname = surname;
		this.city = city;
		this.company = company;
		this.address = address;
	}

	private String name;
	private String surname;
	private String city;
	private String company;
	private Address address;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSurname() {
		return surname;
	}

	public void setSurname(String surname) {
		this.surname = surname;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCompany() {
		return company;
	}

	public void setCompany(String company) {
		this.company = company;
	}
	
	public Address getAddress() {
		return address;
	}
	
	public void setAddress(Address address) {
		this.address = address;
	}

}

Member sınıfı içerisinde üyeye ait bilgileri tutan name, surname, city, company ve address alanları ve onların get-set metotları yer alıyor. Address sınıfı ile aynı şekilde burada da iki yapılandırıcı mevcut.

MemberListWebService.java

package com.ilkaygunel.webservice;

import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.ilkaygunel.pojo.Address;
import com.ilkaygunel.pojo.Member;

@RestController
public class MemberListWebService {
	@RequestMapping(value = "/memberList", method = RequestMethod.GET)
	public List<Member> getExample(@RequestParam(value = "MemberId", defaultValue = "0") String id) {
		List<Member> memberList = new ArrayList<>();
		Member member1 = new Member("İsmail", "Ceylan", "İstanbul", "Saha",
				new Address("Haramidere", "Beylikdüzü", "TÜRKİYE"));
		memberList.add(member1);

		Member member2 = new Member("Serkan", "Akbaba", "İstanbul", "Saha",
				new Address("Cennet Mahallesi", "K.Çekmece", "TÜRKİYE"));
		memberList.add(member2);

		Member member3 = new Member("Ertan", "Şahin", "İstanbul", "Saha",
				new Address("Maslak", "Sarıyer", "TÜRKİYE"));
		memberList.add(member3);

		if (id.equals("0")) {
			return memberList;
		} else {
			return memberList.subList(Integer.parseInt(id) - 1, Integer.parseInt(id));
		}
	}
}

MemberListWebService sınıfı bizim get isteklerimize yanıt verecek olan ve bize üye listesini JSON formatında sunacak bir web servis sınıfıdır. Sınıf içerisinde 3 adet member nesnesi oluşturuyoruz ve ve bunları memberList listesine koyuyoruz. Akabinde eğer gelen istek içerisinde id bilgisi yok ise ya da 0 gelmiş ise tüm listeyi aksi takdirde gelen id bilgisindeki user’ı döndürüyoruz.

Daha önce Spring Rest GET Örneği yazımda Spring ile GET hizmeti sunan bir Testful web servis yazmayı anlatmıştım. İlgilenen arkadaşlar oraya da göz atabilirler.

15.11.2018 Güncelleme

Linkedin'den yaşadığı sorun ile ilgili mesaj atan bir hanım arkadaş vesilesi ile öğrendiğim kadarı ile Spring Boot 2.0 ile beraber InMemoryUser tanımı ve kullanımında değişikliğe gidilmiş. InMemoryUser'ların tanımı ve kullanımı için bir InMemoryUserDetailsManager instance'ı oluşturmalı ve ilgili user'larımızı aşağıdaki gibi kayda geçirmeliyiz. Spring Boot 2.0 öncesi için ***auth.inMemoryAuthentication().withUser("adminUser").password("adminUserPassword").roles("ADMIN");*** şeklinde kullanmaya devam edebiliriz.

WebSecurityConfig.java

package com.ilkaygunel.security;

import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.csrf().disable().authorizeRequests().antMatchers("/").permitAll()
				.antMatchers("/memberList").hasAnyRole("ADMIN").anyRequest().authenticated()
				.and().httpBasic();
	}

	@Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("{noop}password").roles("ADMIN").build());
        manager.createUser(User.withUsername("admin").password("{noop}admin").roles("USER").build());
        auth.userDetailsService(manager);
    }
}

Yazımızın ana noktasını oluşturan olayı konuştuğumuz WebSecurityConfig sınıfına geldi sıra. WebSecurityConfig sınıfını @Configuration ve @EnableWebSecurity notasyonları ile işaretliyoruz. @Configuration notasyonu XML ile de yapılabilen konfigürasyon ayarlarının Java kodunda yapılabilmesini sağlayan notasyondur. @EnableWebSecurity notasyonu da web projemiz üzerinde güvenlik katmanını aktif hale getiren notasyondur.

WebSecurityConfig sınıfımızı WebSecurityConfigurerAdapter sınıfımızdan kalıtıyoruz ve içerisinde configure(HttpSecurity http) ve configure(AuthenticationManagerBuilder auth) metotlarını Override ediyoruz. configure(HttpSecurity http) metodu ile hangi URL path’inin herkese açık olacağını hangisinin hangi yetki sahibi kişilerin erişimine açık olacağını kararlaştırıyoruz. Biz burada / path’i yani kök path için herkesin erişimine açık olsun, memberList path’ine sadece ADMIN yetkisi olan kişiler erişebilsin ve httpBasic ile Basic Authentication kullanılsın dedik.

configure(AuthenticationManagerBuilder auth) metodu ile de user tanımlarımızı yapıyoruz. inMemoryAuthentication() ifadesi user’ların runtime’da tutulduğu ve bir yerlerden çekilip alınmadığı manasına gelir. withUser() metodu ile userName’i, password() metodu ile user’ın parolasını, roles() metodu ile de user’ın rolünü tanımlıyoruz. Biz biri ADMIN biri de USER olacak şekilde iki user tanımladık.

Application.java

package com.ilkaygunel.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = { "com.ilkaygunel.security" })
@ComponentScan(basePackages = { "com.ilkaygunel.pojo" })
@ComponentScan(basePackages = { "com.ilkaygunel.webservice" })
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

Application sınıfımız Spring Boot projemizi çok kolay bir şekilde ayağa kaldırmamızı sağlayan sınıfımızıdır. Application sınıfına sağ tıklayıp Run As -> Java Application dediğimiz zaman projemiz ayağa kalkacaktır.

Projemiz ayağa kalktıktan sonra ben Postman uygulmamı açıyorum ve http://localhost:8080/SpringSecurityWithSpringBoot/memberList adresine hiçbir username password vermeden bir GET isteği yolluyorum.

No Auth bir şekilde yollamış olduğum bu GET isteği ekran görüntüsünde de göreceğiniz gibi Unauthorized ve Full authentication is required to access this resource mesajları ile reddedildi. Şimdi USER rolüne sahip standartUser ve onun standartUserPassowrd parolası ile bir istek gönderiyorum.

Bu kez de Access Denied mesajı ile gönderdiğim istek reddedildi çünkü memberList path’ine sadece ADMIN yetkisi olan user ulaşabilir. Şimdi ADMIN yetkisi olan user’ın bilgileri ile GET isteğini gönderiyorum.

Gördüğünüz gibi web servis ilgili yetkiye sakip kişi bilgisi ile istek gelince bana data dönüşünü yaptı.

Bu yazıda anlatacaklarım bu kadar arkadaşlar. Bir sonraki yazıda yetkilendirme işlemi için database kullanımını anlatacağım.

Görüşene kadar sağlıcakla kalın.

Selam ve Sevgilerimle