Commit af50384f authored by Simone Vuotto's avatar Simone Vuotto

Add jwt authentication, login module and alert module

parent e7f4dea3
This diff is collapsed.
......@@ -21,6 +21,7 @@
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"angular2-jwt": "^0.2.3",
"bootstrap": "^3.3.7",
"bootswatch": "^4.0.0-beta.2",
"core-js": "^2.4.1",
......
<div *ngIf="message"
[ngClass]="{ 'alert': message, 'alert-success': message.type === 'success', 'alert-danger': message.type === 'error' }">
{{message.text}}
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AlertComponent } from './alert.component';
describe('AlertComponent', () => {
let component: AlertComponent;
let fixture: ComponentFixture<AlertComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AlertComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AlertComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import {AlertService} from './alert.service';
@Component({
selector: 'app-alert',
templateUrl: './alert.component.html'
})
export class AlertComponent implements OnInit {
message: any;
constructor(private alertService: AlertService) { }
ngOnInit() {
this.alertService.getMessage().subscribe(message => { this.message = message; });
}
}
import { TestBed, inject } from '@angular/core/testing';
import { AlertService } from './alert.service';
describe('AlertService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AlertService]
});
});
it('should be created', inject([AlertService], (service: AlertService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import { Router, NavigationStart } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class AlertService {
private subject = new Subject<any>();
private keepAfterNavigationChange = false;
constructor(private router: Router) {
// clear alert message on route change
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
if (this.keepAfterNavigationChange) {
// only keep for a single location change
this.keepAfterNavigationChange = false;
} else {
// clear alert
this.subject.next();
}
}
});
}
success(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'success', text: message });
}
error(message: string, keepAfterNavigationChange = false) {
this.keepAfterNavigationChange = keepAfterNavigationChange;
this.subject.next({ type: 'error', text: message });
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
import { NgModule } from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {LoginComponent} from './login/login.component';
import {ProjectsComponent} from './projects/projects.component';
import {AuthGuard} from './auth.guard';
const routes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'projects', component: ProjectsComponent, canActivate: [AuthGuard] },
// otherwise redirect to home
{ path: '**', redirectTo: '' }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ],
})
export class AppRoutingModule { }
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
<!-- navbar -->
<nav class="navbar navbar-default navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">ReqV</a>
<ul class="navbar-nav navbar-right" *ngIf="authenticationService.isAuthenticated()">
<li class="nav-item">
<a class="nav-link" onclick="logout()" href="/" >Logout</a>
</li>
</ul>
</div>
</nav>
<!-- main app container -->
<div class="jumbotron">
<div class="container">
<app-alert></app-alert>
<router-outlet></router-outlet>
</div>
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>
<!-- credits -->
<footer class="text-center">
<p>
Copyright © 2017 Simone Vuotto | All Rights Reserved
</p>
</footer>
import { Component } from '@angular/core';
import { AuthenticationService } from './authentication.service';
@Component({
selector: 'app-root',
......@@ -6,5 +7,10 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(public authenticationService: AuthenticationService) { }
logout() {
this.authenticationService.logout();
}
}
// Modules
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
// Components
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { ProjectsComponent } from './projects/projects.component';
import { AlertComponent } from './alert/alert.component';
// Services
import { AuthenticationService } from './authentication.service';
import { AlertService } from './alert/alert.service';
// Guards
import {AuthGuard} from './auth.guard';
// Http Interceptor
import { JwtInterceptor } from './jwt.interceptor.js';
@NgModule({
declarations: [
AppComponent
AppComponent,
LoginComponent,
ProjectsComponent,
AlertComponent,
],
imports: [
BrowserModule
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
],
providers: [
AuthenticationService,
AuthGuard,
AlertService,
{
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
},
],
providers: [],
bootstrap: [AppComponent]
bootstrap: [ AppComponent ]
})
export class AppModule { }
import { TestBed, inject } from '@angular/core/testing';
import { AuthGuard } from './auth.guard';
describe('AuthGuard', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuard]
});
});
it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
expect(guard).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router} from '@angular/router';
import {AuthenticationService} from './authentication.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private autheticationService: AuthenticationService) { }
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.autheticationService.isAuthenticated()) {
return true;
}
this.router.navigate(['/']);
return false;
}
}
import { TestBed, inject } from '@angular/core/testing';
import { AuthenticationService } from './authentication.service';
describe('AuthenticationService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthenticationService]
});
});
it('should be created', inject([AuthenticationService], (service: AuthenticationService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders, HttpResponse} from '@angular/common/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
import { Observable } from 'rxjs/Observable';
const CURRENT_USER = 'currentUser';
@Injectable()
export class AuthenticationService {
constructor(private http: HttpClient) { }
login(username: string, password: string) {
return new Observable(subscriber =>
this.http.post('/api/login', {username: username, password: password}, {
headers: new HttpHeaders().set('Content-Type', 'application/json'),
observe: 'response',
})
.subscribe(
(response: HttpResponse<any>) => {
console.log(response);
localStorage.setItem(CURRENT_USER, response.headers.get('Authorization'));
subscriber.next(response);
},
error => subscriber.error(error)
)
);
}
getToken(): string {
return localStorage.getItem(CURRENT_USER);
}
public isAuthenticated(): boolean {
// get the token
const token = this.getToken();
// return a boolean reflecting
// whether or not the token is expired
return tokenNotExpired(null, token);
}
logout() {
// remove user from local storage to log user out
localStorage.removeItem(CURRENT_USER);
}
}
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
const url = 'http://localhost:8080';
let authorization = '';
if (currentUser && currentUser.token) {
authorization = `Bearer ${currentUser.token}`;
}
request = request.clone({
url: url + request.url,
setHeaders: {
Authorization: authorization
}
});
return next.handle(request);
}
}
<div class="col-md-6 col-md-offset-3">
<h2>Login</h2>
<form name="form" (ngSubmit)="f.form.valid && login()" #f="ngForm" novalidate>
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !username.valid }">
<label for="username">Username</label>
<input type="text" class="form-control" id="username" name="username" [(ngModel)]="model.username" #username="ngModel" required />
<div *ngIf="f.submitted && !username.valid" class="help-block">Username is required</div>
</div>
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !password.valid }">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" name="password" [(ngModel)]="model.password" #password="ngModel" required />
<div *ngIf="f.submitted && !password.valid" class="help-block">Password is required</div>
</div>
<div class="form-group">
<button [disabled]="loading" class="btn btn-primary">Login</button>
<img *ngIf="loading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
<a [routerLink]="['/register']" class="btn btn-link">Register</a>
</div>
</form>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {AuthenticationService} from '../authentication.service';
import {AlertService} from '../alert/alert.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
model: any = {};
loading = false;
returnUrl: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private alertService: AlertService) { }
ngOnInit() {
// reset login status
this.authenticationService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/projects';
}
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
this.alertService.success('Login succesful!');
this.loading = false;
this.router.navigate(['/projects']);
},
error => {
console.log(error);
this.alertService.error(error.error.message);
this.loading = false;
});
}
}
export class User {
id: number;
username: string;
password: string;
email: string;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment