برنامه نویسی

سرور مجوز 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 می چسبیم.

انتخاب CSS

ما 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


![Development vs production](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ws0uqvuz5ubk90wn9rf7.png)


حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون اگر برنامه 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 را در حین توسعه نگه دارید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا