Spring Boot - JWT ile OAuth2

Bu bölümde, Spring Boot Security mekanizmaları ve JWT ile OAuth2 hakkında ayrıntılı bilgi edineceksiniz.

Yetkilendirme Sunucusu

Yetkilendirme Sunucusu, Web API Güvenliği için üstün bir mimari bileşendir. Yetkilendirme Sunucusu, uygulamalarınızın ve HTTP uç noktalarınızın uygulamanızın özelliklerini tanımlamasına izin veren bir merkezileştirme yetkilendirme noktası görevi görür.

Kaynak Sunucusu

Kaynak Sunucusu, istemcilere Kaynak Sunucusu HTTP Uç Noktalarına erişmeleri için erişim belirteci sağlayan bir uygulamadır. HTTP Uç Noktalarını, statik kaynakları ve Dinamik web sayfalarını içeren kitaplıklar koleksiyonudur.

OAuth2

OAuth2, Web Güvenliği uygulamasının istemciden kaynaklara erişmesini sağlayan bir yetkilendirme çerçevesidir. Bir OAuth2 uygulaması oluşturmak için Grant Type (Yetkilendirme kodu), İstemci Kimliği ve İstemci sırrına odaklanmamız gerekir.

 

JWT Jetonu

JWT Token, iki taraf arasında teminat altına alınan talepleri temsil etmek için kullanılan bir JSON Web Jetonudur. JWT belirteci hakkında daha fazla bilgiyi www.jwt.io/ adresinde bulabilirsiniz .

Şimdi, bir JWT Token yardımı ile Yetkilendirme Sunucusu, Kaynak Sunucusu kullanımını sağlayan bir OAuth2 uygulaması oluşturacağız.

Veritabanına erişerek JWT belirteci ile Spring Boot Security'yi uygulamak için aşağıdaki adımları kullanabilirsiniz.

Öncelikle, yapı yapılandırma dosyamıza aşağıdaki bağımlılıkları eklememiz gerekiyor.

Maven kullanıcıları pom.xml dosyanıza aşağıdaki bağımlılıkları ekleyebilir.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

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

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

<dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-jwt</artifactId>
</dependency>

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-test</artifactId>
   <scope>test</scope>
</dependency>

Gradle kullanıcıları, build.gradle dosyasına aşağıdaki bağımlılıkları ekleyebilir.

compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')

compile("org.springframework.security.oauth:spring-security-oauth2")
compile('org.springframework.security:spring-security-jwt')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("com.h2database:h2:1.4.191")

nerede,

  • Spring Boot Starter Security - Bahar Güvenliğini uygular

  • Spring Security OAuth2 - Yetkilendirme Sunucusunu ve Kaynak Sunucusunu etkinleştirmek için OAUTH2 yapısını uygular.

  • Spring Security JWT - Web güvenliği için JWT Token oluşturur

  • Spring Boot Starter JDBC - Kullanıcının uygun olup olmadığından emin olmak için veritabanına erişir.

  • Spring Boot Starter Web - HTTP uç noktaları yazar.

  • H2 Database - Kimlik doğrulama ve yetkilendirme için kullanıcı bilgilerini depolar.

Tam derleme yapılandırma dosyası aşağıda verilmiştir.

<?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 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
   
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>websecurityapp</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>websecurityapp</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.RELEASE</version>
      <relativePath /> <!-- lookup parent from repository -->
   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth2</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-jwt</artifactId>
      </dependency>
      
      <dependency>
         <groupId>com.h2database</groupId>
         <artifactId>h2</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

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

Gradle – build.gradle

buildscript {
   ext {
      springBootVersion = '1.5.9.RELEASE'
   }
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
   }
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'com.tutorialspoint'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
   mavenCentral()
}

dependencies {
   compile('org.springframework.boot:spring-boot-starter-security')
   compile('org.springframework.boot:spring-boot-starter-web')
   testCompile('org.springframework.boot:spring-boot-starter-test')
   testCompile('org.springframework.security:spring-security-test')
   compile("org.springframework.security.oauth:spring-security-oauth2")
   compile('org.springframework.security:spring-security-jwt')
   compile("org.springframework.boot:spring-boot-starter-jdbc")
   compile("com.h2database:h2:1.4.191")  
}

Şimdi, ana Spring Boot uygulamasında, aynı uygulamada bir Auth sunucusu ve Resource Server olarak hareket etmek için @EnableAuthorizationServer ve @EnableResourceServer açıklamasını ekleyin.

Ayrıca, JWT Token kullanarak Spring Security ile API'ye erişmek için basit bir HTTP uç noktası yazmak için aşağıdaki kodu kullanabilirsiniz.

package com.tutorialspoint.websecurityapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class WebsecurityappApplication {
   public static void main(String[] args) {
      SpringApplication.run(WebsecurityappApplication.class, args);
   }
   @RequestMapping(value = "/products")
   public String getProductName() {
      return "Honey";   
   }
}

Kimlik doğrulama için Kullanıcı bilgilerini depolamak üzere POJO sınıfını tanımlamak için aşağıdaki kodu kullanın.

package com.tutorialspoint.websecurityapp;

import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public class UserEntity {
   private String username;
   private String password;
   private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
   
   public String getPassword() {
      return password;
   }
   public void setPassword(String password) {
      this.password = password;
   }
   public Collection<GrantedAuthority> getGrantedAuthoritiesList() {
      return grantedAuthoritiesList;
   }
   public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) {
      this.grantedAuthoritiesList = grantedAuthoritiesList;
   }
   public String getUsername() {
      return username;
   }
   public void setUsername(String username) {
      this.username = username;
   }
}

Şimdi, aşağıdaki kodu kullanın ve Spring Boot kimlik doğrulaması için org.springframework.security.core.userdetails.User sınıfını genişleten CustomUser sınıfını tanımlayın.

package com.tutorialspoint.websecurityapp;

import org.springframework.security.core.userdetails.User;

public class CustomUser extends User {
   private static final long serialVersionUID = 1L;
   public CustomUser(UserEntity user) {
      super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList());
   }
}

Veritabanından Kullanıcı bilgilerini okumak ve Özel kullanıcı hizmetine göndermek için @Repository sınıfını oluşturabilir ve ayrıca verilen “ROLE_SYSTEMADMIN” yetkisini ekleyebilirsiniz.

package com.tutorialspoint.websecurityapp;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Repository;

@Repository
public class OAuthDao {
   @Autowired
   private JdbcTemplate jdbcTemplate;

   public UserEntity getUserDetails(String username) {
      Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
      String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?";
      List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username },
         (ResultSet rs, int rowNum) -> {
         
         UserEntity user = new UserEntity();
         user.setUsername(username);
         user.setPassword(rs.getString("PASSWORD"));
         return user;
      });
      if (list.size() > 0) {
         GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN");
         grantedAuthoritiesList.add(grantedAuthority);
         list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList);
         return list.get(0);
      }
      return null;
   }
}

Org.springframework.security.core.userdetails.UserDetailsService öğesini gösterildiği gibi DAO depo sınıfını çağıracak şekilde genişleten bir Özel Kullanıcı ayrıntısı hizmet sınıfı oluşturabilirsiniz.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomDetailsService implements UserDetailsService {
   @Autowired
   OAuthDao oauthDao;

   @Override
   public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException {
      UserEntity userEntity = null;
      try {
         userEntity = oauthDao.getUserDetails(username);
         CustomUser customUser = new CustomUser(userEntity);
         return customUser;
      } catch (Exception e) {
         e.printStackTrace();
         throw new UsernameNotFoundException("User " + username + " was not found in the database");
      }
   }
}

Ardından, Web Güvenliği'ni etkinleştirmek için bir @configuration sınıfı oluşturun, Parola kodlayıcıyı (BCryptPasswordEncoder) ve AuthenticationManager bean'i tanımlayın. Security yapılandırma sınıfı WebSecurityConfigurerAdapter sınıfını genişletmelidir.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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 org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
   @Autowired
   private CustomDetailsService customDetailsService;

   @Bean
   public PasswordEncoder encoder() {
      return new BCryptPasswordEncoder();
   }
   @Override
   @Autowired
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(customDetailsService).passwordEncoder(encoder());
   }
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
         .sessionCreationPolicy(SessionCreationPolicy.NEVER);
   }
   @Override
   public void configure(WebSecurity web) throws Exception {
      web.ignoring();
   }
   @Override
   @Bean
   public AuthenticationManager authenticationManagerBean() throws Exception {
      return super.authenticationManagerBean();
   }
}

Şimdi, İstemci Kimliği, İstemci Sırrı eklemek için OAuth2 Yapılandırma sınıfını tanımlayın, belirteç imzalayan anahtarı ve doğrulayıcı anahtarı için JwtAccessTokenConverter, Özel anahtarı ve Genel anahtarı tanımlayın ve belirteç geçerliliği için ClientDetailsServiceConfigurer'ı kapsamlarla yapılandırın.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
   private String clientid = "tutorialspoint";
   private String clientSecret = "my-secret-key";
   private String privateKey = "private key";
   private String publicKey = "public key";

   @Autowired
   @Qualifier("authenticationManagerBean")
   private AuthenticationManager authenticationManager;
   
   @Bean
   public JwtAccessTokenConverter tokenEnhancer() {
      JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
      converter.setSigningKey(privateKey);
      converter.setVerifierKey(publicKey);
      return converter;
   }
   @Bean
   public JwtTokenStore tokenStore() {
      return new JwtTokenStore(tokenEnhancer());
   }
   @Override
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
      .accessTokenConverter(tokenEnhancer());
   }
   @Override
   public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
      security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
   }
   @Override
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients.inMemory().withClient(clientid).secret(clientSecret).scopes("read", "write")
         .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20000)
         .refreshTokenValiditySeconds(20000);

   }
}

Şimdi, openssl kullanarak bir Özel anahtar ve genel anahtar oluşturun.

Özel anahtar oluşturmak için aşağıdaki komutları kullanabilirsiniz.

openssl genrsa -out jwt.pem 2048
openssl rsa -in jwt.pem

Genel anahtar üretimi için aşağıdaki komutları kullanabilirsiniz.

openssl rsa -in jwt.pem -pubout

Spring Boot'un 1.5 sürümünden sonraki sürümü için, OAuth2 Kaynak filtre sırasını tanımlamak için application.properties dosyanıza aşağıdaki özelliği ekleyin.

security.oauth2.resource.filter-order=3

YAML dosyası kullanıcıları aşağıdaki özelliği YAML dosyasına ekleyebilir.

security:
   oauth2:
      resource:
         filter-order: 3

Şimdi, sınıf yolu kaynakları altında schema.sql ve data.sql dosyası oluşturun src/main/resources/directory uygulamayı H2 veritabanına bağlamak için.

Schema.sql dosyası gösterildiği gibidir -

CREATE TABLE USERS (ID INT PRIMARY KEY, USERNAME VARCHAR(45), PASSWORD VARCHAR(60));

Data.sql dosyası gösterildiği gibidir -

INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES (
   1, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');

INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES (
   2, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');

Note - Şifre, veritabanı tablosunda Bcrypt Encoder formatında saklanmalıdır.

Yürütülebilir bir JAR dosyası oluşturabilir ve aşağıdaki Maven veya Gradle komutlarını kullanarak Spring Boot uygulamasını çalıştırabilirsiniz.

Maven için aşağıda verilen komutu kullanabilirsiniz -

mvn clean install

"BUILD SUCCESS" sonrasında, JAR dosyasını hedef dizinin altında bulabilirsiniz.

Gradle için, komutu gösterildiği gibi kullanabilirsiniz -

gradle clean build

"BUILD SUCCESSFUL" sonrasında, JAR dosyasını build / libs dizini altında bulabilirsiniz.

Şimdi, burada gösterilen komutu kullanarak JAR dosyasını çalıştırın -

java –jar <JARFILE>

Uygulama Tomcat 8080 portunda başlatılır.

Şimdi OAUTH2 belirtecini almak için POSTMAN aracılığıyla POST yöntemi URL'sine basın.

http://localhost:8080/oauth/token

Şimdi, İstek Başlıklarını aşağıdaki gibi ekleyin -

  • Authorization - Müşteri Kimliğiniz ve İstemci sırrınızla Temel Kimlik Doğrulama.

  • Content Type - application / x-www-form-urlencoded

Şimdi, İstek Parametrelerini aşağıdaki gibi ekleyin -

  • Grant_type = şifre
  • kullanıcı adı = kullanıcı adınız
  • şifre = şifreniz

Şimdi, API'ye basın ve access_token'ı gösterildiği gibi alın -

Şimdi, gösterildiği gibi İstek Başlığında Taşıyıcı erişim belirteciyle Kaynak Sunucusu API'sine basın.

Ardından çıktıyı aşağıda gösterildiği gibi görebilirsiniz -