首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Angular重定向至登录页面

Angular重定向至登录页面
EN

Stack Overflow用户
提问于 2015-12-17 17:52:12
回答 7查看 276K关注 0票数 136

我来自Asp.Net MVC世界,在那里,用户试图访问未经授权的页面时,会自动重定向到登录页面。

我正在尝试在Angular上重现这种行为。我遇到了@CanActivate装饰器,但它导致组件根本不呈现,没有重定向。

我的问题如下:

  • 是否提供了一种实现此行为的方法?
  • 如果提供,是如何实现的?这是一个好的实践吗?
  • 如果不是,在Angular中处理用户授权的最佳实践是什么?
EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2015-12-17 21:35:39

我已经在Github上发布了一个完整的框架Angular 2 project with OAuth2 integration,它展示了下面提到的指令的实际效果。

与Angular 2HTML不同,Angular 2HTML基本上是您插入到页面中的新components标记(带有相关代码),而属性指令是您放在标记中的一个属性,它会导致某些行为发生。Docs here

自定义属性的存在会导致放置指令的组件(或HTML元素)发生一些事情。考虑一下我在当前的Angular2/OAuth2应用程序中使用的指令:

代码语言:javascript
复制
import {Directive, OnDestroy} from 'angular2/core';
import {AuthService} from '../services/auth.service';
import {ROUTER_DIRECTIVES, Router, Location} from "angular2/router";

@Directive({
    selector: '[protected]'
})
export class ProtectedDirective implements OnDestroy {
    private sub:any = null;

    constructor(private authService:AuthService, private router:Router, private location:Location) {
        if (!authService.isAuthenticated()) {
            this.location.replaceState('/'); // clears browser history so they can't navigate with back button
            this.router.navigate(['PublicPage']);
        }

        this.sub = this.authService.subscribe((val) => {
            if (!val.authenticated) {
                this.location.replaceState('/'); // clears browser history so they can't navigate with back button
                this.router.navigate(['LoggedoutPage']); // tells them they've been logged out (somehow)
            }
        });
    }

    ngOnDestroy() {
        if (this.sub != null) {
            this.sub.unsubscribe();
        }
    }
}

这利用了我编写的身份验证服务来确定用户是否已经登录,并且还向订阅了身份验证事件,以便在用户注销或超时时将其踢出。

你也可以做同样的事情。您将创建一个与我的指令类似的指令,用于检查是否存在必要的cookie或其他指示用户已通过身份验证的状态信息。如果他们没有您正在寻找的那些标志,请将用户重定向到您的主公共页面(如我所做的)或您的OAuth2服务器(或其他任何内容)。您可以将该指令属性放在需要保护的任何组件上。在这种情况下,可以将其命名为protected,就像我在上面粘贴的指令中一样。

代码语言:javascript
复制
<members-only-info [protected]></members-only-info>

然后,您可能希望将用户导航/重定向到应用程序中的登录视图,并在那里处理身份验证。您必须将当前路由更改为您想要执行此操作的路由。因此,在这种情况下,您可以使用依赖项注入在指令的constructor()函数中获取一个Router object,然后使用navigate()方法将用户发送到您的登录页面(如上面的示例所示)。

这假设您在某处有一系列控制<router-outlet>标记的路由,可能如下所示:

代码语言:javascript
复制
@RouteConfig([
    {path: '/loggedout', name: 'LoggedoutPage', component: LoggedoutPageComponent, useAsDefault: true},
    {path: '/public', name: 'PublicPage', component: PublicPageComponent},
    {path: '/protected', name: 'ProtectedPage', component: ProtectedPageComponent}
])

相反,如果您需要将用户重定向到外部 URL,例如您的OAuth2服务器,那么您应该让您的指令执行类似以下操作:

代码语言:javascript
复制
window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope
票数 91
EN

Stack Overflow用户

发布于 2016-08-16 15:29:51

下面是一个使用Angular 4的更新示例(也兼容Angular 5- 8)

具有受AuthGuard保护的主路由的路由

代码语言:javascript
复制
import { Routes, RouterModule } from '@angular/router';

import { LoginComponent } from './login/index';
import { HomeComponent } from './home/index';
import { AuthGuard } from './_guards/index';

const appRoutes: Routes = [
    { path: 'login', component: LoginComponent },

    // home route protected by auth guard
    { path: '', component: HomeComponent, canActivate: [AuthGuard] },

    // otherwise redirect to home
    { path: '**', redirectTo: '' }
];

export const routing = RouterModule.forRoot(appRoutes);

如果用户未登录AuthGuard将重定向到登录页面

已更新以将查询参数中的原始url传递到登录页面

代码语言:javascript
复制
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private router: Router) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        if (localStorage.getItem('currentUser')) {
            // logged in so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
        return false;
    }
}

有关完整的示例和工作演示,请查看this post

票数 142
EN

Stack Overflow用户

发布于 2016-03-06 01:30:27

最终路由器的使用率

随着新路由器的引入,保护路由变得更加容易。您必须定义一个作为服务的防护,并将其添加到路由中。

代码语言:javascript
复制
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { UserService } from '../../auth';

@Injectable()
export class LoggedInGuard implements CanActivate {
  constructor(user: UserService) {
    this._user = user;
  }

  canActivate() {
    return this._user.isLoggedIn();
  }
}

现在将LoggedInGuard传递给路由,并将其添加到模块的providers数组中。

代码语言:javascript
复制
import { LoginComponent } from './components/login.component';
import { HomeComponent } from './components/home.component';
import { LoggedInGuard } from './guards/loggedin.guard';

const routes = [
    { path: '', component: HomeComponent, canActivate: [LoggedInGuard] },
    { path: 'login', component: LoginComponent },
];

模块声明:

代码语言:javascript
复制
@NgModule({
  declarations: [AppComponent, HomeComponent, LoginComponent]
  imports: [HttpModule, BrowserModule, RouterModule.forRoot(routes)],
  providers: [UserService, LoggedInGuard],
  bootstrap: [AppComponent]
})
class AppModule {}

关于它如何与最终版本一起工作的详细博客文章:https://medium.com/@blacksonic86/angular-2-authentication-revisited-611bf7373bf9

在不推荐使用的路由器上使用

更健壮的解决方案是扩展RouterOutlet,并在激活路由时检查用户是否已登录。这样,您就不必将指令复制并粘贴到每个组件。另外,基于子组件的重定向可能会产生误导。

代码语言:javascript
复制
@Directive({
  selector: 'router-outlet'
})
export class LoggedInRouterOutlet extends RouterOutlet {
  publicRoutes: Array;
  private parentRouter: Router;
  private userService: UserService;

  constructor(
    _elementRef: ElementRef, _loader: DynamicComponentLoader,
    _parentRouter: Router, @Attribute('name') nameAttr: string,
    userService: UserService
  ) {
    super(_elementRef, _loader, _parentRouter, nameAttr);

    this.parentRouter = _parentRouter;
    this.userService = userService;
    this.publicRoutes = [
      '', 'login', 'signup'
    ];
  }

  activate(instruction: ComponentInstruction) {
    if (this._canActivate(instruction.urlPath)) {
      return super.activate(instruction);
    }

    this.parentRouter.navigate(['Login']);
  }

  _canActivate(url) {
    return this.publicRoutes.indexOf(url) !== -1 || this.userService.isLoggedIn()
  }
}

无论用户是否登录,UserService代表您的业务逻辑所在的位置。您可以在构造函数中使用DI轻松添加它。

当用户导航到您网站上的新url时,将使用当前指令调用activate方法。从它你可以抓取url并决定是否允许它。如果不是,只需重定向到登录页面。

剩下的最后一件事是将它传递给我们的主组件,而不是内置的组件。

代码语言:javascript
复制
@Component({
  selector: 'app',
  directives: [LoggedInRouterOutlet],
  template: template
})
@RouteConfig(...)
export class AppComponent { }

此解决方案不能与@CanActive生命周期装饰器一起使用,因为如果传递给它的函数解析为false,则不会调用RouterOutlet的activate方法。

我还为此写了一篇详细的博客文章:https://medium.com/@blacksonic86/authentication-in-angular-2-958052c64492

票数 56
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34331478

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档