1. Overview
1.概述
In this tutorial, we’ll continue our Spring Security OAuth series by building a simple front end for Authorization Code flow.
在本教程中,我们将继续Spring Security OAuth系列,为授权代码流程构建一个简单的前端。
Keep in mind that the focus here is the client-side; have a look at the Spring REST API + OAuth2 + AngularJS writeup – to review detailed configuration for both Authorization and Resource Servers.
请记住,这里的重点是客户端;请看Spring REST API + OAuth2 + AngularJS的文章 – 以查看授权和资源服务器的详细配置。
2. Authorization Server
2.授权服务器
Before we get to our front end, we need to add our client details in our Authorization Server configuration:
在我们进入前端之前,我们需要在我们的授权服务器配置中添加我们的客户细节。
@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/")
...
Note how we now have the Authorization Code grant type enabled, with the following, simple details:
请注意,我们现在启用了授权码授予类型,有以下简单的细节。
- our client id is fooClientId
- our scopes are foo, read and write
- the redirect URI is http://localhost:8089/ (we’re going to use port 8089 for our front-end app)
3. The Front End
3.前端
Now, let’s start building our simple front-end application.
现在,让我们开始构建我们简单的前端应用程序。
As we’re going to use to use Angular 6 for our app here, we need to use the frontend-maven-plugin plugin in our Spring Boot application:
由于我们的应用程序将使用Angular 6,我们需要在Spring Boot应用程序中使用frontend-maven-plugin插件。
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<nodeVersion>v8.11.3</nodeVersion>
<npmVersion>6.1.0</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>
Note that, naturally, we need to install Node.js first on our box; we’ll use the Angular CLI to generate the base for our app:
请注意,我们自然需要先在我们的盒子上安装Node.js;我们将使用Angular CLI来为我们的应用程序生成基础。
ng new authCode
ng new authCode
4. Angular Module
4.Angular模块
Now, let’s discuss our Angular Module in detail.
现在,让我们来详细讨论一下我们的Angular模块。
Here’s our simple AppModule:
这是我们简单的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
4.1.应用组件
Let’s start with our AppComponent which is the root component:
让我们从我们的AppComponent开始,它是根组件。
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
4.2.主页组件
Next is our main component, HomeComponent:
接下来是我们的主要组件,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
4.3.Foo组件
Our third and final component is the FooComponent; this displays the Foo resources – obtained from Resource Server:
我们的第三个也是最后一个组件是FooComponent;它显示Foo资源–从资源服务器获得。
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
4.4.应用服务
Now, let’s take a look at the AppService:
现在,让我们看一下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. Run the Application
5.运行应用程序
To run our application and make sure everything is working properly, we need to:
为了运行我们的应用程序并确保一切工作正常,我们需要。
- First, run Authorization Server on port 8081
- Then, run the Resource Server on port 8082
- Finally, run the Front End
We’ll need to build our app first:
我们需要首先建立我们的应用程序。
mvn clean install
Then change directory to src/main/resources:
然后将目录改为 src/main/resources。
cd src/main/resources
Then run our app on port 8089:
然后在8089端口运行我们的应用程序。
npm start
6. Conclusion
6.结论
We learned how to build a simple front end client for Authorization Code flow using Spring and Angular 6.
我们学习了如何使用Spring和Angular 6为授权代码流建立一个简单的前端客户端。
And, as always, the full source code is available over on GitHub.
而且,像往常一样,完整的源代码可以在GitHub上获得,。