前端 Spring Security OAuth 應用 – 授權碼流程

Spring Security
Remote
0
02:04 AM · Nov 30 ,2025

1. 概述

在本教程中,我們將繼續我們的 Spring Security OAuth 系列,通過構建 Authorization Code 流的簡單前端來完成。

請注意,重點在於客户端;請參考 Spring REST API + OAuth2 + AngularJS 的文章,以瞭解 Authorization 和 Resource Server 的詳細配置。

2. 授權服務器

在處理前端之前,我們需要在授權服務器配置中添加客户端詳情:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
               .withClient("fooClientId")
               .secret(passwordEncoder().encode("secret"))
               .authorizedGrantTypes("authorization_code")
               .scopes("foo", "read", "write")
               .redirectUris("http://localhost:8089/")
...

注意,我們現在啓用了授權碼 grant 類型,幷包含以下簡單詳情:

  • 我們的客户端 ID 是 fooClientId 
  • 我們的權限範圍是 foo, read, 以及 write 
  • 重定向 URI 是 http://localhost:8089/(我們將使用端口 8089 用於我們的前端應用)

3. 前端

現在,讓我們開始構建我們的簡單前端應用程序。

由於我們將使用 Angular 6 用於我們的應用程序,因此我們需要在 Spring Boot 應用程序中使用 frontend-maven-plugin 插件:


    
        com.github.eirslett
        frontend-maven-plugin
        1.6

        
            v8.11.3
            6.1.0
            src/main/resources
        

        
            
                安裝 node 和 npm
                
                    install-node-and-npm
                
            

            
                npm install
                
                    npm
                
            

            
                npm run build
                
                    npm
                

                
                    run build
                
            
        
    

請注意,自然地,我們需要首先在我們的機器上安裝 Node.js;我們將使用 Angular CLI 來為我們的應用程序生成基礎:

ng new authCode

4. Angular Module

Now, let’s discuss our Angular Module in detail.

Here’s our simple AppModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule   from '@angular/router';
import { AppComponent } from './app.component';
import { HomeComponent } from './home.component';
import { FooComponent } from './foo.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    FooComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    RouterModule.forRoot([
     { path: '', component: HomeComponent, pathMatch: 'full' }], {onSameUrlNavigation: 'reload'})
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Our Module consists of three Components and one service, we’ll discuss them in the following sections

4.1. App Component

Let’s start with our AppComponent which is the root component:

import {Component} from '@angular/core';
 
@Component({
    selector: 'app-root',
    template: `<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" href="/">Spring Security Oauth - Authorization Code</a>
    </div>
  </div>
</nav>
<router-outlet></router-outlet>`
})

export class AppComponent {}

4.2. Home Component

Next is our main component, HomeComponent:

import {Component} from '@angular/core';
import {AppService} from './app.service'
 
@Component({
    selector: 'home-header',
    providers: [AppService],
  template: `<div class="container" >
    <button *ngIf="!isLoggedIn" class="btn btn-primary" (click)="login()" type="submit">Login</button>
    <div *ngIf="isLoggedIn" class="content">
        <span>Welcome !!</span>
        <a class="btn btn-default pull-right"(click)="logout()" href="#">Logout</a>
        <br/>
        <foo-details></foo-details>
    </div>
</div>`
})
 
export class HomeComponent {
     public isLoggedIn = false;

    constructor(
        private _service:AppService){}
 
    ngOnInit(){
        this.isLoggedIn = this._service.checkCredentials();    
        let i = window.location.href.indexOf('code');
        if(!this.isLoggedIn && i != -1){
            this._service.retrieveToken(window.location.href.substring(i + 5));
        }
    }

    login() {
        window.location.href = 'http://localhost:8081/spring-security-oauth-server/oauth/authorize?response_type=code&client_id=' + this._service.clientId + '&redirect_uri='+ this._service.redirectUri;
    }
 
    logout() {
        this._service.logout();
    }
}

Note that:

  • If the user is not logged in, only the login button will appear
  • The login button redirect user to the Authorization URL
  • When user is redirected back with the authorization code, we retrieve access token using this code

4.3. Foo Component

Our third and final component is the FooComponent; this displays the Foo resources – obtained from Resource Server:

import { Component } from '@angular/core';
import {AppService, Foo} from './app.service'

@Component({
  selector: 'foo-details',
  providers: [AppService],  
  template: `<div class="container">
    <h1 class="col-sm-12">Foo Details</h1>
    <div class="col-sm-12">
        <label class="col-sm-3">ID</label> <span>{{foo.id}}</span>
    </div>
    <div class="col-sm-12">
        <label class="col-sm-3">Name</label> <span>{{foo.name}}</span>
    </div>
    <div class="col-sm-12">
        <button class="btn btn-primary" (click)="getFoo()" type="submit">New Foo</button>        
    </div>
</div>`
})

export class FooComponent {
    public foo = new Foo(1,'sample foo');
    private foosUrl = 'http://localhost:8082/spring-security-oauth-resource/foos/';  

    constructor(private _service:AppService) {}

    getFoo(){
        this._service.getResource(this.foosUrl+this.foo.id)
         .subscribe(
            data => this.foo = data,
            error =>  this.foo.name = 'Error');
    }
}

4.4. App Service

Now, let’s take a look at the AppService:

import {Injectable} from '@angular/core';
import { Cookie } from 'ng2-cookies';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
 
export class Foo {
  constructor(
    public id: number,
    public name: string) { }
} 

@Injectable()
export class AppService {
   public clientId = 'fooClientId';
   public redirectUri = 'http://localhost:8089/';

  constructor(
    private _http: HttpClient){}

  retrieveToken(code){
    let params = new URLSearchParams();   
    params.append('grant_type','authorization_code');
    params.append('client_id', this.clientId);
    params.append('redirect_uri', this.redirectUri);
    params.append('code',code);

    let headers = new HttpHeaders({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Basic '+btoa(this.clientId+":secret")});
     this._http.post('http://localhost:8081/spring-security-oauth-server/oauth/token', params.toString(), { headers: headers })
    .subscribe(
      data => this.saveToken(data),
      err => alert('Invalid Credentials')
    ); 
  }

  saveToken(token){
    var expireDate = new Date().getTime() + (1000 * token.expires_in);
    Cookie.set("access_token", token.access_token, expireDate);
    console.log('Obtained Access token');
    window.location.href = 'http://localhost:8089';
  }

  getResource(resourceUrl) : Observable<any>{
    var headers = new HttpHeaders({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Bearer '+Cookie.get('access_token')});
    return this._http.get(resourceUrl,{ headers: headers })
                   .catch((error:any) => Observable.throw(error.json().error || 'Server error'));
  }

  checkCredentials(){
    return Cookie.check('access_token');
  } 

  logout() {
    Cookie.delete('access_token');
    window.location.reload();
  }
}

Let’s do a quick rundown of our implementation here:

  • checkCredentials(): to check if user is logged in
  • retrieveToken(): to obtain access token using authorization code
  • saveToken(): to save Access Token in a cookie
  • getResource(): to get Foo details using its ID
  • logout(): to delete Access Token cookie

5. 運行應用程序

為了運行我們的應用程序並確保一切正常工作,我們需要:

  • 首先,在端口 8081 上運行 Authorization Server
  • 然後,在端口 8082 上運行 Resource Server
  • 最後,運行 Front End

我們需要首先構建我們的應用程序:

mvn clean install

然後,將目錄更改為 src/main/resources:

cd src/main/resources

然後,在端口 8089 上運行我們的應用程序:

npm start

6. 結論

我們學習瞭如何使用 Spring 和 Angular 6 構建一個簡單的前端客户端,用於 Authorization Code 流程。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.