You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Spring授权服务器实现Google/GitHub外部登录对接Angular客户端

Angular + Spring Authorization Server 集成Google/GitHub社交登录实现方案

一、Spring Authorization Server 配置

1. 依赖引入

在pom.xml中添加授权服务器与OAuth2客户端依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
</dependencies>

2. 身份提供商与客户端配置

在application.yml中配置Google、GitHub的身份提供商信息,以及Angular客户端的授权回调地址:

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: 你的Google客户端ID
            client-secret: 你的Google客户端密钥
            scope: openid,email,profile
            redirect-uri: "{baseUrl}/login/oauth2/code/google"
          github:
            client-id: 你的GitHub客户端ID
            client-secret: 你的GitHub客户端密钥
            scope: read:user,user:email
            redirect-uri: "{baseUrl}/login/oauth2/code/github"
        provider:
          google:
            issuer-uri: https://accounts.google.com
          github:
            issuer-uri: https://github.com/login/oauth
      authorization-server:
        client:
          angular-client:
            registration:
              client-id: angular-client
              client-secret: "{noop}angular-secret"
              client-authentication-methods: client_secret_basic
              authorization-grant-types: authorization_code
              redirect-uris: "http://localhost:4200/callback"
              scopes: openid,profile,email

3. 授权服务器核心配置类

创建配置类,处理授权流程与外部登录回调:

@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());

        http.exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));

        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
        )
        .oauth2Login(oauth2 -> oauth2
                .loginPage("/login")
        );

        return http.build();
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient angularClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("angular-client")
                .clientSecret("{noop}angular-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri("http://localhost:4200/callback")
                .scope(OidcScopes.OPENID)
                .scope("profile")
                .scope("email")
                .build();

        return new InMemoryRegisteredClientRepository(angularClient);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    private static RSAKey generateRsa() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
    }

    private static KeyPair generateRsaKey() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            return keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException ex) {
            throw new IllegalStateException(ex);
        }
    }

    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder()
                .issuer("http://localhost:8080")
                .build();
    }
}

二、Angular 客户端配置

1. 安装依赖

安装OAuth2处理库:

npm install angular-oauth2-oidc

2. 模块配置

在app.module.ts中导入并配置OAuth模块:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { OAuthModule } from 'angular-oauth2-oidc';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    OAuthModule.forRoot({
      resourceServer: {
        allowedUrls: ['http://localhost:8080/api'],
        sendAccessToken: true
      }
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

3. 登录服务实现

创建auth.service.ts封装登录逻辑:

import { Injectable } from '@angular/core';
import { OAuthService, AuthConfig } from 'angular-oauth2-oidc';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  private authConfig: AuthConfig = {
    issuer: 'http://localhost:8080',
    clientId: 'angular-client',
    clientSecret: 'angular-secret',
    redirectUri: window.location.origin + '/callback',
    scope: 'openid profile email',
    responseType: 'code',
    showDebugInformation: true
  };

  constructor(private oauthService: OAuthService) {
    this.configureOAuth();
  }

  private configureOAuth(): void {
    this.oauthService.configure(this.authConfig);
    this.oauthService.loadDiscoveryDocumentAndTryLogin().then(() => {});
  }

  loginWithGoogle(): void {
    this.oauthService.initLoginFlow('google');
  }

  loginWithGitHub(): void {
    this.oauthService.initLoginFlow('github');
  }

  logout(): void {
    this.oauthService.logOut();
  }

  getAccessToken(): string | null {
    return this.oauthService.getAccessToken();
  }
}

4. 回调组件与路由

创建callback.component.ts处理授权码回调:

import { Component, OnInit } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { Router } from '@angular/router';

@Component({
  template: '<div>登录校验中...</div>'
})
export class CallbackComponent implements OnInit {

  constructor(private oauthService: OAuthService, private router: Router) { }

  ngOnInit(): void {
    this.oauthService.loadDiscoveryDocument().then(() => {
      this.oauthService.tryLogin().then(() => {
        this.router.navigate(['/']);
      });
    });
  }
}

在app-routing.module.ts配置路由:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CallbackComponent } from './callback.component';
import { HomeComponent } from './home.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'callback', component: CallbackComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

5. 登录页面组件

在home.component.ts与模板中添加登录按钮:

import { Component } from '@angular/core';
import { AuthService } from './auth.service';

@Component({
  template: `
    <div *ngIf="!authService.getAccessToken()">
      <button (click)="authService.loginWithGoogle()">Google登录</button>
      <button (click)="authService.loginWithGitHub()">GitHub登录</button>
    </div>
    <div *ngIf="authService.getAccessToken()">
      <p>已登录,AccessToken: {{ authService.getAccessToken() }}</p>
      <button (click)="authService.logout()">退出登录</button>
    </div>
  `
})
export class HomeComponent {
  constructor(public authService: AuthService) { }
}

关键注意事项

  • Google/GitHub开发者控制台中,回调地址需设置为Spring授权服务器的回调地址(如http://localhost:8080/login/oauth2/code/google)
  • Spring授权服务器中配置的Angular回调地址需与Angular端redirectUri完全一致
  • 确保Spring授权服务器的issuer地址与Angular端配置的issuer匹配

内容的提问来源于stack exchange,提问作者Inzamamul Haque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:25:12