Merhaba bu yazımızda Spring Security ile yetkilendirme yönetiminin nasıl yapılacağı hakkında konuşacağız. Basit bir Spring uygulaması geliştireceğiz ve Swagger kullanacağız.
Swagger nedir?
Basitçe, Swagger Rest Api için bir arayüz sağlamaktır. Bu makalenin altında Swagger Arayüzü örneğini göreceksiniz.
İlk adım olarak Intellij Idea veya diğer IDE’lerle bir Spring Maven projesi yaratacağız. Eğer ilk defa spring projesi yaratıyorsanız linke göz atmanızı öneririm. (TIKLAYINIZ)
Maven nedir?
Maven bir proje bağımlılığı yönetim aracıdır. Maven şunları sağlar:
- Build işlemini kolaylaştırmak
- Sabit bir build sistemi sağlamak
- Kaliteli proje bilgisi sağlamak
- En iyi uygulamayı geliştirmek için kurallar sağlamak
- Yeni özelliklere geçişi kolaylaştırmak
Maven ile pom.xml dosyasını kullanacağız.
Pom.xml (Project Object Model) dosyası nedir?
Proje Nesne Modeli veya POM, Maven’deki temel çalışma birimidir. Maven tarafından projeyi oluşturmak için kullanılan proje ve yapılandırma ayrıntıları hakkında bilgi içeren bir XML dosyasıdır.
İkinci adım olarak pom.xml dosyamıza aşağıdaki dependecy’leri ekliyoruz.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.caglartelef</groupId>
<artifactId>springsecurity</artifactId>
<version>0.0.1</version>
<name>springsecurity</name>
<description>Spring Security project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Lombok Dependency -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Security Test Dependency -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
<!-- Spring Actuator Dependency for Healtcheck -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Konfigürasyonlarımızı tutmak için “src/main/java/…/resources “ dosyasının altında “application .yaml” veya “application.properties” uzantılı bir dosya oluşturuyoruz ve aşağıdaki bilgilerimizi ekliyoruz.
spring:
application:
name: Spring Security Api
management:
endpoints:
web:
exposure:
include: '*'
base-path: /_monitoring
health:
defaults:
enabled: true
api:
authorization:
users:
caglartelef:
password: "{noop}caglartelef"
config:
timeout: false
roles:
- ROLE_ADMIN
rabiayurdakultelef:
password: "{noop}rabiayurdakultelef"
config:
timeout: false
roles:
- ROLE_ADMIN
bahadirtelef:
password: "{noop}bahadirtelef"
config:
timeout: true
roles:
- ROLE_SECADMIN
Projemizde “src/main/java/…/properties” altındaki dosyanın içerisine “Properties” adında dosya oluşturup içerisine de “ApplicationProperties.java” adında yeni sınıf ekliyoruz.
package com.caglartelef.springsecurity.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.HashMap;
import java.util.Map;
@ConfigurationProperties(prefix = "api.authorization")/** Get authorization infos in application.yaml.*/
public class ApplicationProperties {
private Map<String, User> users = new HashMap<>();
public Map<String, User> getUsers() {
return users;
}
public void setUsers(Map<String, User> users) {
this.users = users;
}
/**
* User class
* */
public static class User {
private String password;
private UserConfig config;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public UserConfig getConfig() {
return config;
}
public void setConfig(UserConfig config) {
this.config = config;
}
}
/**
* User config class
* */
public static class UserConfig {
private boolean timeout;
private String[] roles;
public String[] getRoles() {
return roles;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
public boolean isTimeout() {
return timeout;
}
public void setTimeout(boolean timeout) {
this.timeout = timeout;
}
}
}
Projemizde “src/main/java/…/configuration” altındaki dosyanın içerisine “Configuration” adında dosya oluşturup içerisine de “ConfigurationService.java” adında yeni sınıf ekliyoruz.
package com.caglartelef.springsecurity.config;
import com.caglartelef.springsecurity.properties.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Component
public class ConfigurationService {
@Autowired
private ApplicationProperties properties;
/**
* This method provide Authenticated user class.
* */
public ApplicationProperties.User getCurrentUser() {
String currentUsername = SecurityContextHolder.getContext().getAuthentication().getName();
return properties.getUsers().get(currentUsername);
}
/**
* This method provide Authenticated user name.
* */
public String getCurrentUserName() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}
“Configuration” dosyamızın içerisine “SwaggerConfig.java” adında yeni bir sınıf ekliyoruz. Bu sınıf swagger konfigurasyonumuzu yapmamızı sağlıyor.
package com.caglartelef.springsecurity.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* Swagger configuration
* */
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.caglartelef"))
.paths(PathSelectors.any())
.build();
}
}
“Configuration” dosyamızın içerisine “SecurityConfiguration.java” adında bir sınıf ekliyoruz. Bu sınıf tüm kullanıcılar ve istekler için erişim yetkilendirmesini sağlar.
package com.caglartelef.springsecurity.config;
import com.caglartelef.springsecurity.properties.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
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.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import java.util.Map;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private ApplicationProperties properties;
/**
* This method checks the access information of users.
* */
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/admin/login/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "/api/secAdmin/login/**").hasRole("SECADMIN")
.antMatchers(HttpMethod.GET, "/_monitoring/health/**").permitAll()
.anyRequest().denyAll()
.and()
.csrf().disable()
.formLogin().disable();
}
/**
* This method provide ignore to swagger configuration.
* */
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
/**
* This method allows users to login the system.
* */
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> builder = auth.inMemoryAuthentication();
Map<String, ApplicationProperties.User> users = properties.getUsers();
users.forEach((username, user) -> {
builder.withUser(username).password(user.getPassword()).authorities(user.getConfig().getRoles());
});
}
}
Son olarak projemiz içerisine “Controller” adında bir dosya ekliyoruz ve içerisine “ApiController.java” adında bir sınıf ekliyoruz.
package com.caglartelef.springsecurity.controller;
import com.caglartelef.springsecurity.config.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/api")
public class ApiController {
@Autowired
private ConfigurationService configurationService;
/**
* This method provide to login for admin users.
* */
@GetMapping(value = "/admin/login")
public String adminLogin(){
System.out.println(configurationService.getCurrentUserName());
return configurationService.getCurrentUserName();
}
/**
* This method provide to login for secondary admin users.
* */
@GetMapping(value = "/secAdmin/login")
public String secAdminLogin(){
System.out.println(configurationService.getCurrentUserName());
return configurationService.getCurrentUserName();
}
}
Projemizin tüm adımlarını tamamladık.
Şimdi projemizi çalıştırabiliriz.
Projemizi çalıştırdıktan sonra tarayıcıyı açarak http://localhost:8080/swagger-ui.html adresine gidelim.
Açılan ekrandaki oturum açma isteğini iptal diyerek Swagger’in açılmasını sağlayın. Aşağıdaki gibi bir ekran görüyor olmalısınız.
“/api/admin/login” endpointiyle “application.yaml” üzerine eklediğimiz admin rolüne sahip kullanıcılar giriş yapabilir.
“/api/secAdmin/login” endpointiyle application.yaml” üzerine eklediğimiz secAdmin rolüne sahip kullanıcılar giriş yapabilir.
Admin yetkisi ile secAdmin endpointinden giriş yapmak istediğimiz Spring Security buna izin vermeyecektir.
Projenin kaynak kodlarına erişmek için TIKLAYINIZ.
Okuduğunuz için teşekkür ederim, iyi günler dilerim.