Перейти к основному содержимому

Интерфейсное приложение с Spring Security OAuth — поток кода авторизации

· 5 мин. чтения

1. Обзор

В этом руководстве мы продолжим нашу серию Spring Security OAuth , создав простой внешний интерфейс для потока кода авторизации.

Имейте в виду, что основное внимание здесь уделяется стороне клиента; взгляните на запись Spring REST API + OAuth2 + AngularJS — чтобы просмотреть подробную конфигурацию как для серверов авторизации, так и для серверов ресурсов.

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/")
...

Обратите внимание, что теперь у нас включен тип предоставления кода авторизации со следующими простыми деталями:

  • наш идентификатор клиента - fooClientId
  • наши области видимости foo , чтение и запись
  • URI перенаправления — http://localhost:8089/ (мы собираемся использовать порт 8089 для нашего внешнего приложения)

3. Внешний вид

Теперь давайте начнем создавать наше простое внешнее приложение.

Поскольку мы собираемся использовать Angular 6 для нашего приложения здесь, нам нужно использовать плагин frontend-maven- plugin в нашем приложении Spring Boot:

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

Обратите внимание, что, естественно, нам нужно сначала установить Node.js на нашу машину; мы будем использовать Angular CLI для создания базы для нашего приложения:

ng новый код авторизации

4. Угловой модуль

Теперь давайте подробно обсудим наш модуль Angular.

Вот наш простой 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 { }

Наш модуль состоит из трех компонентов и одного сервиса, мы обсудим их в следующих разделах.

4.1. Компонент приложения

Начнем с нашего 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. Домашний компонент

Далее идет наш основной компонент 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();
}
}

Обратите внимание, что:

  • Если пользователь не вошел в систему, появится только кнопка входа
  • Кнопка входа перенаправляет пользователя на URL-адрес авторизации
  • Когда пользователь перенаправляется обратно с кодом авторизации, мы получаем токен доступа, используя этот код.

4.3. Фу Компонент

Наш третий и последний компонент — 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. Служба приложений

Теперь давайте посмотрим на 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();
}
}

Давайте сделаем краткое изложение нашей реализации здесь:

  • checkCredentials() : чтобы проверить, вошел ли пользователь в систему
  • retrieveToken() : для получения токена доступа с использованием кода авторизации
  • saveToken() : сохранить токен доступа в файле cookie.
  • getResource() : чтобы получить информацию о Foo, используя его идентификатор
  • logout() : удалить cookie токена доступа

5. Запустите приложение

Чтобы запустить наше приложение и убедиться, что все работает правильно, нам необходимо:

  • Сначала запустите Сервер авторизации на порту 8081.
  • Затем запустите сервер ресурсов на порту 8082.
  • Наконец, запустите интерфейс

Сначала нам нужно создать наше приложение:

mvn clean install

Затем измените каталог на src/main/resources:

cd src/main/resources

Затем запустите наше приложение на порту 8089:

npm start

6. Заключение

Мы узнали, как создать простой интерфейсный клиент для потока кода авторизации с использованием Spring и Angular 6.

И, как всегда, полный исходный код доступен на GitHub .