Angular2 Redirect After Login

2019-02-06 14:39发布

问题:

I'm creating an authentication system in angular2 with the idea that if a user that is not authenticated tries to navigated to a "protected" url, the system will redirect the user to the login page putting in the url a query param called "next" that will help the login system redirect the user back to where he wanted to be in the first place.

login?next=my-redirect-url

To protect my components, I'm using the decorator @CanActivate(isUserAuthenticated) in all of them. The isUserAuthenticated function is something as follows:

function isUserAuthenticated(
    prevInstr: ComponentInstruction, 
    nextInstr: ComponentInstruction
): boolean {
    const authService = injector.get(AuthService);
    const router = injector.get(Router);
    if(authService.isLoggedIn()) {
        return true;
    } else {
        router.navigate(["/Login", {next: nextInstr.urlPath}]);
        return false;
    }
}

This approach is not working because the urlPath property of the nextInstr is not showing the "complete" url (it lacks query params for example).

Is there a way to build the complete url from a ComponentInstruction instance like nextInstr?

回答1:

Yes there is a way:

let url = router.generate(['./Login', {next: nextInstr.urlPath}]).toRootUrl();

Lets say following structure example depending on routeconfig:

login?next=my-redirect-url

And then you use navigateByUrl to navigate to following url

router.navigateByUrl('/' + url);

I have tested it with my example and result you can see on image:

let instruction = router.generate(['./Country', {country: 'de', a: 1, b: 2}]);
console.log(instruction, instruction.toRootUrl());



回答2:

Another way (WITHOUT using query parameters using @angular/router 3.0.0) to achieve the same requirement of redirecting to the original requested resource after authentication is to use RouterStateSnapshot.url, which is a string that contains the url of the resource the user requested. Before redirecting user back to your login form after a failed authentication attempt, inside of the CanActivate hook, get the requested url from RouterStateSnapshot.url and store it in a variable that is accessible to your login function. When the user logs in successfully simply redirect to the stored URL. Here's my example:

//GaurdService - implements CanActivate hook for the protected route

import { Injectable } from '@angular/core';
import { CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class GuardService implements CanActivate {
    constructor( private router: Router, private authService: AuthService ) {}

    canActivate(state: RouterStateSnapshot): boolean {
        let url: string = state.url;
        return this.checkLogin(url);
    }

    checkLogin(url: string): boolean {
        if (this.authService.loggedIn()) { return true; }
        this.authService.redirectUrl = url; // set url in authService here
        this.router.navigate([ '/login' ]); // then ask user to login
        return false;
    }

}

My AuthService (below), which performs the login, will redirect user to the originally requested resource on successful login.

import { Injectable, Inject } from '@angular/core';
import { tokenNotExpired } from 'angular2-jwt';
import { Router } from '@angular/router';
import { Headers, Http, Response, RequestOptions  } from '@angular/http';
import { Observable } from 'rxjs';
import './../rxjs-operators';

const API_URL: string = '';

@Injectable()
export class AuthService {
    public redirectUrl: string = ''; //Here is where the requested url is stored

constructor( @Inject('API_URL') private apiURL: string, private router: Router, private http: Http ) {}

    public loggedIn(): boolean {
        return tokenNotExpired('token');
    }

    public authenticate(username: string, password: string)  {
        let body: string = JSON.stringify({ un: username, pw: password});
        let headers: Headers = new Headers({ 'Content-Type': 'application/json' });
        let options: RequestOptions = new RequestOptions({ headers: headers });
        return this.http.post(this.apiURL + '/authenticate', body, options)
            .map(res => res.json())
            .subscribe(res => {
                localStorage.setItem('token',res.token);
                this.redirect(); // Redirect to the stored url after user is logged in
            });

        .catch(this.handleError);
    }

    private redirect(): void {
        this.router.navigate([ this.redirectUrl ]); //use the stored url here
    }
}

This is how your application can remember the originally requested resource WITHOUT using query parameters.

For more details please see the example guide at angular.io starting at the "GUARD THE ADMIN FEATURE" section: https://angular.io/docs/ts/latest/guide/router.html#!#can-activate-guard

Hope this helps someone.