Spring Security OAuthのAngular 4アップグレード

1概要

このクイックチュートリアルでは、AngularJSの代わりにAngular 4を使用するために、既存のAngularアプリケーションリンク(/rest-api-spring-oauth2-angularjs)をアップグレードします。

2セットアップAngular 4

まず、フロントエンドモジュールの生成と管理にhttps://cli.angular.io/[Angular CLI]を使用します。

  • まず、https://nodejs.org/en/download/[node and npm]** をインストールします - Angular CLIはnpmツールです。

それから、Mavenを使ったAngularプロジェクトを構築するには、https://github.com/eirslett/frontend-maven-plugin[ frontend-maven-plugin ]を使用する必要があります。

<build>
    <plugins>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>1.3</version>
            <configuration>
                <nodeVersion>v6.10.2</nodeVersion>
                <npmVersion>3.10.10</npmVersion>
                <workingDirectory>src/main/resources</workingDirectory>
            </configuration>
            <executions>
                <execution>
                    <id>install node and npm</id>
                    <goals>
                        <goal>install-node-and-npm</goal>
                    </goals>
                </execution>
                <execution>
                    <id>npm install</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                </execution>
                <execution>
                    <id>npm run build</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                    <configuration>
                        <arguments>run build</arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

そして最後に、Angular CLIを使用して** 新しいモジュールを生成します。

ng new oauthApp

フロントエンドモジュールが2つあります。1つはパスワードフロー用、もう1つは暗黙的フロー用です。

次のセクションでは、各モジュールのAngularアプリロジックについて説明します。

3 Angular 4 を使用したパスワードフロー

次に、パスワードフローフロントエンドモジュールのロジックについて説明しましょう。

3.1. アプリサービス

app.service.ts にある AppService から始めましょう。これにはサーバーとのやり取りのロジックが含まれています。

  • obtainAccessToken() :与えられたユーザー資格情報のアクセストークンを取得する

  • saveToken() :ng2-cookiesを使ってクッキーにアクセストークンを保存する

としょうかん ** getResource() :IDを使ってサーバからFooオブジェクトを取得する

  • checkCredentials() :ユーザがログインしているかどうかを確認する

  • logout() :アクセストークンcookieを削除し、ユーザをログアウトさせる

export class Foo {
  constructor(
    public id: number,
    public name: string) { }
}

@Injectable()
export class AppService {
  constructor(
    private __router: Router, private __http: Http){}

  obtainAccessToken(loginData){
    let params = new URLSearchParams();
    params.append('username',loginData.username);
    params.append('password',loginData.password);
    params.append('grant__type','password');
    params.append('client__id','fooClientIdPassword');
    let headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Basic '+btoa("fooClientIdPassword:secret")});
    let options = new RequestOptions({ headers: headers });

    this.__http.post('http://localhost:8081/spring-security-oauth-server/oauth/token', params.toString(), options)
      .map(res => res.json())
      .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);
    this.__router.navigate(['/']);
  }

  getResource(resourceUrl) : Observable<Foo>{
    var headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Bearer '+Cookie.get('access__token')});
    var options = new RequestOptions({ headers: headers });
    return this.__http.get(resourceUrl, options)
                   .map((res:Response) => res.json())
                   .catch((error:any) => Observable.throw(error.json().error || 'Server error'));
  }

  checkCredentials(){
    if (!Cookie.check('access__token')){
        this.__router.navigate(['/login']);
    }
  }

  logout() {
    Cookie.delete('access__token');
    this.__router.navigate(['/login']);
  }
}

3.2. ログインコンポーネント

次に、ログインフォームを担当する LoginComponent を見てみましょう。

@Component({
  selector: 'login-form',
  providers:[AppService],
  template: `<h1>Login</h1>
    <input type="text"[(ngModel)]="loginData.username"/>
    <input type="password" [(ngModel)]="loginData.password"/>
    <button (click)="login()" type="submit">Login</button>`
})
export class LoginComponent {
    public loginData = {username: "", password: ""};

    constructor(private __service:AppService) {}

    login() {
        this.__service.obtainAccessToken(this.loginData);
    }
}

3.3. ホームコンポーネント

次に、ホームページの表示と操作を担当する HomeComponent を作成します。

@Component({
    selector: 'home-header',
    providers:[AppService],
  template: `<span>Welcome !!</span>
    <a (click)="logout()" href="#">Logout</a>
    <foo-details></foo-details>`
})

export class HomeComponent {
    constructor(
        private __service:AppService){}

    ngOnInit(){
        this.__service.checkCredentials();
    }

    logout() {
        this.__service.logout();
    }
}

3.4. Fooコンポーネント

最後に、Fooの詳細を表示するための FooComponent :

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

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');
    }
}

3.5. アプリコンポーネント

ルートコンポーネントとして機能するシンプルな AppComponent :

@Component({
    selector: 'app-root',
    template: `<router-outlet></router-outlet>`
})

export class AppComponent {}

そして、 AppModule ** __すべてのコンポーネント、サービス、およびルートをラップします。

@NgModule({
  declarations:[    AppComponent,
    HomeComponent,
    LoginComponent,
    FooComponent
 ],
  imports:[    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot([     { path: '', component: HomeComponent },
    { path: 'login', component: LoginComponent }])
 ],
  providers:[],
  bootstrap:[AppComponent]})
export class AppModule { }

4暗黙のフロー

次に、「暗黙のフロー」モジュールに注目します。

4.1. アプリサービス

同様に、私達は私達のサービスから始めますが、今回は自分自身でアクセストークンを取得する代わりにライブラリhttps://github.com/manfredsteyer/angular-oauth2-oidc[angular-oauth2-oidc]を使用します。

@Injectable()
export class AppService {

  constructor(
    private __router: Router, private __http: Http, private oauthService: OAuthService){
        this.oauthService.loginUrl = 'http://localhost:8081/spring-security-oauth-server/oauth/authorize';
        this.oauthService.redirectUri = 'http://localhost:8086/';
        this.oauthService.clientId = "sampleClientId";
        this.oauthService.scope = "read write foo bar";
        this.oauthService.setStorage(sessionStorage);
        this.oauthService.tryLogin({});
    }

  obtainAccessToken(){
      this.oauthService.initImplicitFlow();
  }

  getResource(resourceUrl) : Observable<Foo>{
    var headers = new Headers({'Content-type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': 'Bearer '+this.oauthService.getAccessToken()});
    var options = new RequestOptions({ headers: headers });
    return this.__http.get(resourceUrl, options)
      .map((res:Response) => res.json())
      .catch((error:any) => Observable.throw(error.json().error || 'Server error'));
  }

  isLoggedIn(){
    if (this.oauthService.getAccessToken() === null){
       return false;
    }
    return true;
  }

  logout() {
      this.oauthService.logOut();
      location.reload();
  }
}

4.2. ホームコンポーネント

私たちの HomeComponent は私たちの単純なホームページを処理します。

@Component({
    selector: 'home-header',
    providers:[AppService],
  template: `
    <button ** ngIf="!isLoggedIn" (click)="login()" type="submit">Login</button>
    <div ** ngIf="isLoggedIn">
        <span>Welcome !!</span>
        <a (click)="logout()" href="#">Logout</a>
        <br/>
        <foo-details></foo-details>
    </div>`
})

export class HomeComponent {
    public isLoggedIn = false;

    constructor(
        private __service:AppService){}

    ngOnInit(){
        this.isLoggedIn = this.__service.isLoggedIn();
    }

    login() {
        this.__service.obtainAccessToken();
    }

    logout() {
        this.__service.logout();
    }
}

4.3. Fooコンポーネント

私たちの FooComponent は、パスワードフローモジュールとまったく同じです。

4.4. アプリモジュール

最後に、私たちの AppModule :

@NgModule({
  declarations:[    AppComponent,
    HomeComponent,
    FooComponent
 ],
  imports:[    BrowserModule,
    FormsModule,
    HttpModule,
    OAuthModule.forRoot(),
    RouterModule.forRoot([     { path: '', component: HomeComponent }])
 ],
  providers:[],
  bootstrap:[AppComponent]})
export class AppModule { }

5フロントエンドを実行する

{空} 1フロントエンドモジュールを実行するには、まずアプリを構築する必要があります。

mvn clean install

{空} 2。それから、Angularアプリディレクトリに移動する必要があります。

cd src/main/resources

{空} 3。最後に、アプリを起動します。

npm start

サーバーはデフォルトでポート4200で起動し、任意のモジュールのポートを変更します。

"start": "ng serve"

たとえば package.json で、ポート8086で実行するには

"start": "ng serve --port 8086"

6. 結論

このクイックチュートリアルでは、Angular 4フロントエンドを使用してAngular JSアプリケーションをアップグレードする方法を説明しました。

いつものように、完全なソースコードはhttps://github.com/Baeldung/spring-security-oauth[over on GitHub]にあります。