Mise à niveau angulaire 4 pour Spring Security OAuth

1. Vue d’ensemble

Dans ce didacticiel rapide, nous allons mettre à niveau notre lien d’application angulaire existant:/rest-api-spring-oauth2-angularjs[décrit]ici, pour utiliser Angular 4 au lieu d’AngularJS.

2. Configuration angulaire4

Tout d’abord, nous utiliserons Angular CLI pour générer et gérer nos modules front-end.

  • Nous allons d’abord installer node and npm ** - Angular CLI étant un outil npm.

Ensuite, Inclure les dépendances Maven , nous devons utiliser frontend-maven-plugin pour construire notre projet Angular en utilisant maven:

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

Et enfin, générer un nouveau module en utilisant un CLI angulaire:

ng new oauthApp

Notez que nous aurons deux modules frontaux - un pour le flux de mots de passe et l’autre pour le flux implicite.

Dans les sections suivantes, nous aborderons la logique d’application angulaire pour chaque module.

3. Mot de passe utilisant Angular4

Ensuite, abordons la logique de notre module frontal Password Flow.

3.1. Service App

Commençons par notre AppService - situé à app.service.ts - qui contient la logique pour les interactions de serveur:

  • obtainAccessToken () : pour obtenir le jeton d’accès en fonction des informations d’identification de l’utilisateur

  • saveToken () : enregistrer notre jeton d’accès dans un cookie en utilisant ng2-cookies

bibliothèque ** getResource () : pour obtenir un objet Foo du serveur à l’aide de son ID

  • checkCredentials () : pour vérifier si l’utilisateur est connecté ou non

  • logout () : pour supprimer le cookie de jeton d’accès et déconnecter l’utilisateur

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. Composant de connexion

Voyons maintenant notre LoginComponent qui est responsable du formulaire de login:

@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. Composant d’accueil

Ensuite, notre HomeComponent qui est responsable de l’affichage et de la manipulation de notre page d’accueil:

@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. Composant Foo

Enfin, notre FooComponent pour afficher nos détails Foo:

@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. Composant d’application

Notre simple AppComponent pour agir en tant que composant racine:

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

export class AppComponent {}

Et le AppModule où nous enveloppons tous nos composants, services et itinéraires:

@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. Flux implicite

Ensuite, nous nous concentrerons sur le module Flux implicite.

4.1. Service App

De même, nous commencerons par notre service, mais cette fois-ci, nous utiliserons la bibliothèque angular-oauth2-oidc au lieu d’obtenir nous-mêmes un jeton d’accès:

@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. Composant d’accueil

Notre HomeComponent pour gérer notre page d’accueil simple:

@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. Composant Foo

Notre FooComponent est exactement le même que dans le module de flux de mots de passe.

4.4. Module d’application

Enfin, notre AppModule :

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

5. Exécuter le front end

{vide} 1. Pour exécuter l’un de nos modules front-end, nous devons d’abord créer l’application:

mvn clean install

{vide} 2. Ensuite, nous devons accéder à notre répertoire d’applications angulaires:

cd src/main/resources

{vide} 3. Enfin, nous allons commencer notre application:

npm start

Le serveur démarrera par défaut sur le port 4200, pour changer de port de module, le

"start": "ng serve"

dans package.json pour le faire fonctionner sur le port 8086, par exemple:

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

6. Conclusion

Dans ce rapide didacticiel, nous avons vu comment mettre à niveau une application AngularJS vers une application utilisant un frontal Angular4.

Comme toujours, le code source complet peut être trouvé à l’adresse over sur GitHub .