سرور مجوز Angular 19 و Spring به عنوان یک برنامه واحد

خوش آمدید ، توسعه دهندگان دیگر! 🚀 اگر به دنبال ادغام Boot Spring با Security Security هستید و یک سرور مجوز OAUTH2 را تنظیم کنید ، در جای مناسب قرار دارید. در این راهنما ، ما قدم به قدم روند کار را طی خواهیم کرد و از اجرای صاف و ایمن اطمینان می دهیم.
مرحله 1: تنظیم پروژه بوت بهار
ابتدا بیایید یک پروژه جدید بوت بهار با وابستگی های لازم ایجاد کنیم. به Spring Initializr بروید و یک پروژه با:
- وب بهار (برای API های استراحت)
plugins {
java
id("org.springframework.boot") version "3.4.4"
id("io.spring.dependency-management") version "1.1.7"
}
group = "spring.angular"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
tasks.withType {
useJUnitPlatform()
}
پس از تولید ، پروژه را در IDE مورد علاقه خود (IntelliJ ، Eclipse یا Code) باز کنید و برنامه را اجرا کنید.
تأیید راه اندازی برنامه Boot Boot
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.4.4)
2025-03-31T08:37:22.524+11:00 INFO 31772 --- [SpringAngularApp] [ main] s.a.S.SpringAngularAppApplication : No active profile set, falling back to 1 default profile: "default"
2025-03-31T08:37:22.878+11:00 INFO 31772 --- [SpringAngularApp] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2025-03-31T08:37:23.054+11:00 INFO 31772 --- [SpringAngularApp] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path "https://dev.to/"
همانطور که از سیاهههای مربوط می توانیم ببینیم که برنامه ما روی پورت اجرا می شود 8080بشر به مرورگر بروید و وارد آدرس آدرس url http: // localhost: 8080/ما باید صفحه نمایش مانند زیر باشد ، که به معنای برنامه بوت بهار ما خوب است. اگر همه چیز به درستی پیکربندی شده است ، باید یک صفحه فرود را به شرح زیر مشاهده کنید
وقت آن است که برنامه زاویه ای را اضافه کنید
npm install -g @angular/cli
- یک فضای کاری و برنامه اولیه ایجاد کنید
ng new ui
ما می توانیم هر انتخاب را انتخاب کنیم ، اما به SCSS می چسبیم.
ما SSR را نمی خواهیم ، انتخاب ما N خواهد بود
CLI زاویه ای چند وابسته را نصب می کند
ONCES INDEPECIES نصب شده است ، ما می توانیم ساختار پروژه زیر را مشاهده کنیم
cd new ui
ng serve --open
URL http: // localhost: 4200/در مرورگر را وارد کنید و ما برنامه زاویه ای را اجرا خواهیم کرد
گردش کار توسعه
در طول توسعه ، اجرای بوت زاویه ای و بهار به عنوان برنامه های جداگانه کاملاً خوب است (و اغلب ترجیح داده می شود):
Angular CLI: در قسمت جلوی http: // localhost: 4200 با بارگیری گرم برای توسعه سریع.
Boot Spring: API Backend را در http: // localhost: 8080 اجرا می کند.
این جدایی اجازه می دهد:
✔ تکرار سریعتر Frontend (به لطف بارگیری مجدد زنده Angular)
✔ اشکال زدایی مستقل از API های پس زمینه
reaves پاسخهای API مسخره در طول توسعه زودرس
چالش تولید
در تولید ، ما به طور معمول می خواهیم هر دو برنامه را به عنوان یک واحد واحد برای:
✔ استقرار ساده
✔ مسائل متقاطع را کاهش داد (CORS)
✔ عملکرد بهتر (خدمت به دارایی های استاتیک به طور مستقیم از پس زمینه)
پیکربندی Angular برای استقرار به عنوان محتوای استاتیک بوت بهار برای تولید
درک تنظیمات
به طور پیش فرض ، زاویه ای به /dist /ui می پردازد ، اما ما به آن نیاز داریم تا مستقیماً به پوشه منابع استاتیک Boot Spring Boot که در آن می توان به طور خودکار سرو کرد ، خروجی کند. در اینجا نحوه ساخت یکپارچه این کار آورده شده است:
مرحله 1: مسیر خروجی Angular را اصلاح کنید
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/ui",
},
}
}
بیایید تغییر دهیم "outputPath": "dist/ui"
به فهرست منابع Boot Boot
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "../src/main/resources/static", // Changed from "dist/ui"
"index": "src/index.html",
// ... rest of your config
}
}
}
"outputPath": "../src/main/resources/static"
از الان مسیر خروجی با اشاره به منابع بوت بهار ، ما می توانیم دستور Angular CLI را برای ساخت پرونده ها اجرا کنیم ، باید در مورد ما به دیکتاتوری اصلی برنامه زاویه ای اشاره کنیم رابط
مرحله 2: ساخت و تأیید کنید
ng build --configuration production
گزارش های زیر را خواهیم دید
❯ ng build --configuration production
Initial chunk files | Names | Raw size | Estimated transfer size
main-3BERZHFR.js | main | 208.01 kB | 56.74 kB
polyfills-FFHMD2TL.js | polyfills | 34.52 kB | 11.28 kB
styles-5INURTSO.css | styles | 0 bytes | 0 bytes
| Initial total | 242.53 kB | 68.02 kB
Application bundle generation complete. [2.279 seconds]
Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static
به مکان خروجی در اینجا توجه کنید Output location: /Users/san/project/Sample/AngularTest/src/main/resources/static
بشر پرونده ها از فهرست منابع بوت بهار خارج می شوند.
برنامه Boot Spring را اجرا کنید و به مرورگر http: // localhost: 8080 خواهیم دید که برنامه زاویه ای وجود ندارد.
اگر از نزدیک به resources >> static
دایرکتوری ، دایرکتوری مرورگر توسط Angular Build Command ساخته شده است ، و اگر به آن فهرست نگاه کنیم ، می توانیم پرونده index.html و .js را مشاهده کنیم. برای کار زاویه ای که باید در خارج از فهرست مرورگر باشد ، زیرا بهار چیزی در مورد فهرست مرورگر نمی داند.
راه حل 1
ما می توانیم آن پرونده ها را به صورت زیر به خارج از مرورگر منتقل کنیم
اکنون ، اگر برنامه بوت بهار خود را اجرا کنیم و به http: // localhost: 8080 حرکت کنیم ، برنامه زاویه ای را خواهیم دید
خوب این درست کار می کند ، هومم آنقدر مؤثر نیست
مشکل این راه حل این است که ما باید پرونده ها را به صورت دستی جابجا کنیم ، اجازه می دهیم ببینیم چگونه می توانیم این کار را برطرف کنیم.
راه حل بهتر
"outputPath": {
"base": "../src/main/resources/static",
"browser": ""
},
"deleteOutputPath": false
شامل بودن browser =""
برای تولید پرونده در فهرست استاتیک و حذف پرونده های دیگر با استفاده از آن "deleteOutputPath": false
، حالا اگر دستور را اجرا کنیم ng build --configuration production
ما می توانیم ببینیم که همه پرونده ها در استاتیک تولید می شوند
یک مؤلفه جدید اضافه کنید
ng g c Home // This will generate new Home Component
این مؤلفه را در بخش روتر قرار دهید
import { Routes } from '@angular/router';
import {HomeComponent} from './home/home.component';
export const routes: Routes = [{ path: 'home', component: HomeComponent },];
برای توسعه محلی می توانیم پاسخ دهیم ng server
بشر مسیریابی در http: // localhost: 4200/خانه کار خواهد کرد
برای بوت بهار روتر ، ما باید دوباره بسازیم
ng build --configuration production
اگر ما به http://localhost:8080/home
ما به شرح زیر با مسئله ای روبرو خواهیم شد
برای رفع این کار باید پیکربندی را انجام دهیم WebMvcConfigurer
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
@Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable()
? requestedResource
: new ClassPathResource("/static/index.html");
}
});
}
}
## Development vs Production Workflow

اکنون اگر برنامه Boot Spring را اجرا کنیم و به آن حرکت کنیم http://localhost:8080/home
ما مؤلفه خانه خود را خواهیم دید.
برای توسعه ، پروکسی Angular را پیکربندی کنید تا از مشکلات CORS جلوگیری شود:
// src/proxy.conf.json
{
"/api": {
"target": "http://localhost:8080",
"secure": false
}
}
سرور مجوز بهاری
وابستگی را به روز کنید تا شامل سرور مجوز بهاری شود
dependencies {
implementation("org.springframework.boot:spring-boot-starter-oauth2-authorization-server")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-webflux")
developmentOnly("org.springframework.boot:spring-boot-devtools")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
حال اگر سعی کنیم دسترسی پیدا کنیم http://localhost:8080/
صفحه اصلی ورود بهار امنیت بهار نمایش داده می شود.
ما باید تنظیمات اصلی سرور مجوز بهاری را پیکربندی کنیم
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.oidc(Customizer.withDefaults()) // Enable OpenID Connect 1.0
)
.authorizeHttpRequests((authorize) ->
authorize
.anyRequest().authenticated()
)
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("oidc-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
.postLogoutRedirectUri("http://127.0.0.1:8080/")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(oidcClient);
}
@Bean
public JWKSource jwkSource() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
@Bean
public JwtDecoder jwtDecoder(JWKSource jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}
از آنجا که راه اندازی اصلی از کاربر در حافظه استفاده می کند. بنابراین اگر از ورود به سیستم استفاده کنیم ، با استفاده از آن اعتبار استفاده می کنیم.
پایان
بهترین های هر دو جهان
با پیکربندی Angular برای ساختن مستقیم در پوشه منابع استاتیک Boot ، ما یک راه حل قدرتمند تمام پشته ایجاد کرده ایم که:
✔ استقرار را ساده می کند – یک کوزه بوت بهار واحد شامل جلو و پس زمینه است
✔ عملکرد را بهبود می بخشد – دارایی های استاتیک توسط سرور Tomcat تعبیه شده به طور کارآمد ارائه می شود
✔ انعطاف پذیری را حفظ می کند – در حین توسعه برای تولید ، سرورهای جداگانه DEV را در حین توسعه نگه دارید