基于Spring授权服务器实现Google/GitHub外部登录对接Angular客户端
一、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
相关产品推荐
相关产品推荐

