ساخت API امن با Spring Boot و Angular 6 – از صفر تا صد


امنیت وب امروزه بسیار حائز اهمیت است. ایجاد و استفاده از سرویسها بدون در نظر گرفتن بحث امنیت، امروزه امری کاملاً پر ریسک تلقی میشود. اگر میخواهید اطلاعات بسیار حساسی را انتقال دهید، باید از یک لایه امنیتی کامل برای حفاظت از دادههای محرمانه استفاده کنید. چون امن ساختن سیستمها کاملاً مهم است. API امن یکی از مهمترین اجزای این ساختار امنیتی را تشکیل میدهد.
بنابراین هر زمان که از فراخوانی API یا هر چیز دیگری استفاده میکنیم، باید ابتدا امنیت دادههای حساس را در نظر بگریم. انواع بسیار مختلفی از روشهای امنیتی برای امن سازی دادهها وجود دارند. در این نوشته نمونه جامعی از امنیت Spring را بررسی میکنیم. در این راهنما از Spring boot و angular برای نمایش چگونگی امن سازی اپلیکیشنهای انگولار با استفاده از احراز هویت ساده از طریق Spring Security بهره میگیریم. در این راهنما با روش استفاده از Spring Boot برای ساخت سریع API وب آشنا میشویم و به طور خلاصه با استفاده از یک لایه بکاند جاوا، وابستگی maven امنیت Spring را برای امن سازی اپلیکیشن به خدمت میگیریم. همچنین از پلتفرم انگولار برای سهولت ساخت اپلیکیشن وب به کمک پایگاه داده خودمان استفاده میکنیم.
پیش از آغاز توسعه اپلیکیشن نمونهای از آن چه قرار است در بخشهای بعدی بسازیم را نشان میدهیم:

در تصویر فوق نمونهای از اپلیکیشن به نام my heroes میبینید که فهرستی از نامهای قهرمانان را نمایش میدهد. در ابتدا نقطه انتهایی را به صورت غیر امن ارائه میکنیم تا انگولار بتواند فهرست نامها را نمایش دهد. سپس یک نقطه انتهایی REST امن ارائه میکنیم و تغییرات ضروری را در اپلیکیشن انگولار ایجاد میکنیم تا با استفاده از احراز هویت ساده به دادهها دسترسی یابد.
فناوریهای مورد استفاده:
- Angular 6
- Spring Boot 2.0.6
- Java 1.8.0
- Apache Maven 3.5.3
- Visual Studio Code 1.28.2
- Spring Tool Suite 3.9.5
- Mysql 5.7.19
ساخت بکاند جاوا با استفاده از Spring Boot
روشهای مختلفی برای ایجاد یک پروژه Spring Boot وجود دارد.
در ادامه فهرستی از این روشها را میبینید:
- Spring Initializer website
- Spring Boot CLI
- Spring Tool Suite
- Curl Command
ما قصد نداریم تک تک این اجزا را به تفصیل بررسی کنیم و صرفاً از Spring Tool Suite برای ایجاد اپلیکیشن کمک میگیریم.
ایجاد یک پروژه مقدماتی
به مسیر File->New->Spring Starter Project بروید و جزییات مورد نیاز را وارد کرده، وابستگیها را بر اساس تصویر زیر تعیین کنید:


شما باید این وابستگیها را به ترتیب انتخاب کنید تا به فایل maven به نام POM.xml اضافه شوند، سپس دکمه finish را بزند تا پروژه با کتابخانههای مورد نیاز ایجاد شود.
ایجاد یک پایگاه داده
یک جدول در پایگاه داده ایجاد میکنیم و سپس دادههای مورد نیاز را در آن وارد مینماییم. در ادامه کوئری لازم برای ایجاد و درج رکوردها در جدول را مشاهده میکنید:
CREATE TABLE `heroes` ( `id` int(3) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL, KEY `id` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8; insert into `heroes`(`id`,`name`) values (1,'Aung San Suu Kyi'),(2,'Nelson Mandela'),(3,'Tony Blair'),(4,'Dr. Abdul Kalam'),(5,'Srinivas Ramanujan'),(6,'Stephen Hawking'),(7,'Bob Geldof'),(8,'Margaret Thatcher'),(9,'Winston Churchill'),(10,'Bob Dylan'),(11,'Bill Gates'),(12,'Steve Jobs'),(13,'Dalai Lama'),(14,'David Attenborough'),(15,'Mary Robinson'),(16,'Tim Berners-Lee')ک;
افزودن نقطه انتهایی REST غیر امن
در این مرحله کافی است کد زیر را در هر فایل اضافه کنید:
1. فایل Application.properties
server.port=7070 spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root spring.jpa.generate-ddl=true
2. فایل SecureRestServiceApplication.java
package com.example.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EntityScan(basePackages = "com.example.model") @EnableJpaRepositories(basePackages = {"com.example.repository"}) @ComponentScan(basePackages = "com.example.controller") public class SecureRestServiceApplication { public static void main(String[] args) { SpringApplication.run(SecureRestServiceApplication.class, args); } }
3. فایل HeroesController.java
package com.example.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.example.model.Heroes; import com.example.repository.HeroesRepository; @RestController @RequestMapping("/api/") public class HeroesController { @Autowired HeroesRepository heroesRepository; @RequestMapping("/heroes") @CrossOrigin("http://localhost:4200") public List<Heroes> getHeroes(){ return heroesRepository.findAll(); } }
4. فایل Heroes.java
package com.example.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "heroes") public class Heroes { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private int id; @Column(name = "name") private String name; public Heroes() {} public Heroes(int id, String name) { super(); this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
5. فایل HeroesRepository.java
package com.example.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import com.example.model.Heroes; public interface HeroesRepository extends JpaRepository<Heroes, Integer> {}
6. اینک اپلیکیشن را در postman یا هر مرورگر دیگر اجرا کنید
اکنون میتوانید اپلیکیشن را ریاستارت کنید و مرورگر یا postman را باز کرده و به آدرس http://localhost:7070/api/heroes بروید. در این زمان باید اطلاعات قهرمانان را در قالب JSON به صورت زیر ببینید:

ساخت اپلیکیشن فرانتاند با استفاده از انگولار
در این مرحله یک فرانتاند برای اپلیکیشن خود میسازیم. بدین منظور از Angular CLI برای ساخت اپلیکیشن خود استفاده خواهیم کرد.
دستور زیر را در اعلان فرمان خود وارد نمایید:
ng new secure-hero-app
این دستور ممکن است چند دقیقه برای ساخت فایلهای پیکربندی و گرد هم آوردن همه وابستگیها طول بکشد. دستور فوق یک اپلیکیشن بسیار ساده برای شما ایجاد میکند. زمانی که دستور فوق اجرا شد، دستور زیر را اجرا کنید:
ng serve –open
این دستور اپلیکیشن را آغاز میکند و مرورگر پیشفرض شما را به طور خودکار روی آدرس http://localhost:4200 باز میکند تا بتوانید صفحه پیشفرض انگولار را ببینید.
ایجاد موارد الزامی
اینک باید برخی کامپوننتهای لازم، سرویسها و ماژولهای مسیریابی را در اپلیکیشن انگولار خود ایجاد کنیم. ما 1 کامپوننت، 1 ماژول مسیریابی و 1 سرویس با استفاده از دستور زیر ایجاد میکنیم.
1. ایجاد سرویس
ng generate service hero
-
فایل Hero.service.ts
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class HeroService { constructor(private http: HttpClient) { } getHeroes() { const url = 'http://localhost:7070/api/heroes'; return this.http.get(url); } }
2. ایجاد ماژول مسیریابی
ng generate module app-routing --flat --module=app
در ماژول مسیریابی کد زیر را اضافه کنید:
-
فایل App-routing.module.ts
import { HeroesComponent } from './heroes/heroes.component'; import { NgModule } from '@angular/core'; import { RouterModule, Routes} from '@angular/router'; const routes: Routes = [ { path: 'heroes', component: HeroesComponent} ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule] }) export class AppRoutingModule { }
3. ایجاد یک کامپوننت
ng generate component heroes
فایلهای پیکربندی خود را به صورت زیر بهروزرسانی کنید:
-
فایل heroes.component.css
/* HeroesComponent's private CSS styles */ .heroes { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .heroes li { cursor: pointer; position: relative; left: 0; background-color: #EEE; margin: .5em; padding: .3em 0; height: 1.6em; border-radius: 4px; } .heroes li.selected:hover { background-color: #BBD8DC !important; color: white; } .heroes li:hover { color: #607D8B; background-color: #DDD; left: .1em; } .heroes .text { position: relative; top: -3px; } .heroes .badge { display: inline-block; font-size: small; color: white; padding: 0.8em 0.7em 0 0.7em; background-color: #607D8B; line-height: 1em; position: relative; left: -1px; top: -4px; height: 1.8em; margin-right: .8em; border-radius: 4px 0 0 4px; }
-
فایل heroes.component.html
<h2>My Heroes</h2> <ul class="heroes"> <!-- <li> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> --> <li *ngFor="let hero of heroes$" > <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul>
-
فایل heroes.component.ts
import { HeroService } from './../hero.service'; import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'] }) export class HeroesComponent implements OnInit { heroes$: Object; constructor(private data: HeroService) { } ngOnInit() { this.data.getHeroes().subscribe( data => this.heroes$ = data ); } }
4. نگاهی به ماژول اپلیکیشن
برای استفاده از سرویس HTTP در کلاس سرویس باید HttpClientModule را به صورت زیر ایمپورت کنیم:
-
فایل App.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { HeroesComponent } from './heroes/heroes.component'; import { AppRoutingModule } from './/app-routing.module'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [ AppComponent, HeroesComponent ], imports: [ BrowserModule, HttpClientModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
دسترسی به اپلیکیشن
پس از طی مراحل فوق اینک میتوانیم فهرستی از قهرمانها را با دسترسی به آدرس http://localhost:4200/heroes در مرورگر مشاهده کنیم.
امن سازی نقطه انتهایی REST با استفاده از Spring Security
برای فعالسازی spring security در اپلیکیشن کافی است وابستگی زیر را به فایل pom.xml اضافه کنیم:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
اینک تلاش کنید به آدرس http://localhost:7070/api/heroes بروید.
در این زمان باید با صفحهای مواجه شوید که مانند تصویر زیر خواستار اطلاعات احراز هویت است:

نام کاربری پیشفرض user است و رمز عبور اولیه را نیز میتوانید از کنسول STS مانند تصویر زیر دریافت کنید:

جهت استفاده از نام کاربری و رمز عبور خودتان به جای موارد تولیدشده میتوانید مشخصات زیر را در فایل application.properties وارد نمایید:
spring.security.user.name=test spring.security.user.password=test123
بهروزرسانی نقطه انتهایی انگولار برای دسترسی به نقطه انتهایی REST امن
اکنون اگر تلاش کنید به آدرس http://localhost:4200/heroes در مرورگر یا postman بروید، با وضعیت کد 401 مواجه میشوید که به معنی دسترسی غیرمجاز به URL است. در این مرحله باید اطلاعات احراز هویت مقدماتی خود را برای درخواست HTTP وارد کنیم. بنابراین سرویس hero خود را بهروزرسانی میکنیم و متد getHeroes() را به صورت زیر معرفی مینماییم:
getHeroes() { const url = 'http://localhost:7070/api/heroes'; const headers = new HttpHeaders({Authorization: 'Basic ' + btoa('test:test123')}); return this.http.get(url، { headers }); }
اینک مجدداً با مراجعه به آدرس http://localhost:7070/api/heroes در مرورگر تلاش میکنیم؛ اما میبینیم که این تلاش موفق نیست و دلیل آن نیاز به افزودن پشتیبانی از پروتکل CROS در بکاند REST که در بخش بعدی توضیح داده شده است.
اشتراک منابع (Cross-origin (CORS
ما باید پشتیبانی از CORS را به سرویس انگولار خود اضافه کنیم تا بتوانیم یک نقطه انتهایی روی دامنه متفاوت را فراخوانی کنیم. منظور از دامنه متفاوت این است که اپلیکیشن ما روی http://localhost:4200 اجرا میشود و یک منبع را روی دامنه دیگر یعنی http://localhost:7070 درخواست میکند. بنابراین باید CORS را روی سرور پیکربندی کنیم. این کار از طریق تأمین پشتیبانی از پروتکل CORS ممکن است.
بدین منظور باید یک کلاس پیکربندی برای امنیت وب ایجاد کنیم. این کلاس به Spring security نشان میدهد که امکان بررسی از سوی مرورگر وجود دارد. این کار از طریق override کردن روش پیکربندی WebSecurityConfigurerAdapter میسر میشود.
در این مرحله باید دو فایل CustomFilter.java و SecurityConfiguration.java را به صورت زیر ایجاد کنیم:
-
فایل CustomFilter.java
package com.example.config; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.filter.OncePerRequestFilter; public class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token, Cache-Control, remember-me, WWW-Authenticate"); response.addHeader("Access-Control-Expose-Headers", "xsrf-token"); chain.doFilter(request, response); } }
-
فایل SecurityConfiguration.java
package com.example.config; import org.springframework.context.annotation.Configuration; 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.web.access.channel.ChannelProcessingFilter; @EnableWebSecurity @Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.addFilterBefore(new CustomFilter(), ChannelProcessingFilter.class); httpSecurity.authorizeRequests() .antMatchers("/") .permitAll() .anyRequest() //.permitAll() .fullyAuthenticated() .and().httpBasic().and().csrf().disable(); } }
اینک با مراجعه مجدد به آدرس http://localhost:4200 در مرورگر میتوانید فهرست قهرمانها را بار دیگر ببینید.
سخن پایانی
در این نوشته یک REST API با استفاده از Spring Boot ساختیم. ما این API را با استفاده از نوعی احراز هویت مقدماتی با بهرهگیری از Spring Security امن کردیم. در نهایت از پلتفرم محبوب فرانتاند انگولار برای دسترسی به API امن کمک گرفتیم.
اگر این مطلب برایتان مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای طراحی و برنامه نویسی وب
- ساخت رابط کاربری Login با انگولار (Angular) و متریال دیزاین – به زبان ساده
- مجموعه آموزشهای ابزارها و راهکارهای مدیریت وبسایتها
- آموزش فریمورک Spring در جاوا
- آموزش انگولار (Angular): ساخت یک اپلیکیشن در ۲۰ دقیقه – به زبان ساده
- ۱۰ کتابخانه و فریمورک جاوا اسکریپت که باید آنها را بشناسید
==
خیلی ممنون موفق باشید