Login 重定向到登录页面

Login 重定向到登录页面,login,typescript,angular,angular2-routing,Login,Typescript,Angular,Angular2 Routing,我来自Asp.NETMVC世界,在那里,试图访问未经授权的页面的用户会自动重定向到登录页面 我试图在角度上重现这种行为。我使用@CanActivate装饰器,但它会导致组件根本不呈现,没有重定向 我的问题如下: window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&am

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

我试图在角度上重现这种行为。我使用@CanActivate装饰器,但它会导致组件根本不呈现,没有重定向

我的问题如下:

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope
  • Angular是否提供了实现此行为的方法
  • 如果是,怎么做?这是一种好的做法吗
  • 如果不是,在Angular中处理用户授权的最佳实践是什么

更新:我已经在Github上发布了一个完整的框架,其中显示了下面提到的指令的作用

一种方法是通过使用
指令
。与Angular 2
组件不同,Angular 2
组件基本上是插入到页面中的新HTML标记(带有关联代码),attribute指令是放置在标记中的属性,它会导致某些行为发生

自定义属性的存在会导致放置指令的组件(或HTML元素)发生问题。考虑一下我目前使用的角度2/OAuth2应用程序的指令:

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
,就像我在上面粘贴的指令一样

<members-only-info [protected]></members-only-info>
相反,如果您需要将用户重定向到一个外部URL,例如您的OAuth2服务器,那么您可以让您的指令执行如下操作:

window.location.href="https://myserver.com/oauth2/authorize?redirect_uri=http://myAppServer.com/myAngular2App/callback&response_type=code&client_id=clientId&scope=my_scope

与最终路由器的使用

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

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
传递到路由,并将其添加到模块的
提供者
数组中

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 },
];
模块声明:

@NgModule({
  declarations: [AppComponent, HomeComponent, LoginComponent]
  imports: [HttpModule, BrowserModule, RouterModule.forRoot(routes)],
  providers: [UserService, LoggedInGuard],
  bootstrap: [AppComponent]
})
class AppModule {}
关于最终版本的详细博客文章:

不推荐使用的路由器的用法

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

@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并决定是否允许它。如果不是,请重定向到登录页面

要让它工作,最后一件事是将它传递给我们的主要组件,而不是内置组件

@Component({
  selector: 'app',
  directives: [LoggedInRouterOutlet],
  template: template
})
@RouteConfig(...)
export class AppComponent { }
此解决方案不能与
@CanActive
生命周期装饰器一起使用,因为如果传递给它的函数解析为false,则不会调用
路由输出的activate方法


还为此写了一篇详细的博文:

请不要覆盖路由器出口!最新的路由器版本(3.0测试版)简直是噩梦

而是使用接口CanActivate和CanDeactivate,并在路由定义中将类设置为CanActivate/CanDeactivate

就像这样:

{ path: '', component: Component, canActivate: [AuthGuard] },
类别:

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(protected router: Router, protected authService: AuthService)
    {

    }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {

        if (state.url !== '/login' && !this.authService.isAuthenticated()) {
            this.router.navigate(['/login']);
            return false;
        }

        return true;
    }
}
@Injectable()
导出类AuthGuard实现了CanActivate{
构造函数(受保护的路由器:路由器,受保护的authService:authService)
{
}
canActivate(路由:ActivatedRouteSnapshot,状态:RouterStateSnashot):可观察的布尔值{
if(state.url!='/login'&&!this.authService.isAuthenticated()){
this.router.navigate(['/login']);
返回false;
}
返回true;
}
}
另见:
这里有一个使用Angular 4的更新示例(也与Angular 5-8兼容)

主路由受AuthGuard保护的路由

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传递到登录页面


对于完整的示例和工作演示,您可以查看上面的精彩答案,我还想
CanActivateChild
:保护儿童路线。它可以用于向子路由添加
guard
,这对ACL之类的情况很有帮助

事情是这样的

src/app/auth-guard.service.ts(节选)

src/app/admin/admin-routing.module.ts(节选)

这是从

参考此代码, auth.ts文件

import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import {  } from 'angular-2-local-storage';
import { Router } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(public localStorageService:LocalStorageService, private router: Router){}
canActivate() {
// Imaginary method that is supposed to validate an auth token
// and return a boolean
var logInStatus         =   this.localStorageService.get('logInStatus');
if(logInStatus == 1){
    console.log('****** log in status 1*****')
    return true;
}else{
    console.log('****** log in status not 1 *****')
    this.router.navigate(['/']);
    return false;
}


}

}
// *****And the app.routes.ts file is as follow ******//
      import {  Routes  } from '@angular/router';
      import {  HomePageComponent   } from './home-page/home- page.component';
      import {  WatchComponent  } from './watch/watch.component';
      import {  TeachersPageComponent   } from './teachers-page/teachers-page.component';
      import {  UserDashboardComponent  } from './user-dashboard/user- dashboard.component';
      import {  FormOneComponent    } from './form-one/form-one.component';
      import {  FormTwoComponent    } from './form-two/form-two.component';
      import {  AuthGuard   } from './authguard';
      import {  LoginDetailsComponent } from './login-details/login-details.component';
      import {  TransactionResolver } from './trans.resolver'
      export const routes:Routes    =   [
    { path:'',              component:HomePageComponent                                                 },
    { path:'watch',         component:WatchComponent                                                },
    { path:'teachers',      component:TeachersPageComponent                                         },
    { path:'dashboard',     component:UserDashboardComponent,       canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formone',       component:FormOneComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formtwo',       component:FormTwoComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'login-details', component:LoginDetailsComponent,            canActivate: [AuthGuard]    },

]; 

1。创建一个防护装置,如下所示。
2.安装ngx cookie服务以获取外部SSO返回的cookie。
3.在environment.ts中创建ssoPath(SSO登录重定向)。
4.获取state.url并使用encodeURIComponent。

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from 
  '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { environment } from '../../../environments/environment.prod';

@Injectable()
export class AuthGuardService implements CanActivate {
  private returnUrl: string;
  constructor(private _router: Router, private cookie: CookieService) {}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.cookie.get('MasterSignOn')) {
      return true;
    } else {
      let uri = window.location.origin + '/#' + state.url;
      this.returnUrl = encodeURIComponent(uri);      
      window.location.href = environment.ssoPath +  this.returnUrl ;   
      return false;      
    }
  }
}

我添加了一个实际的指令,它显示了如何进行身份验证,如果你愿意的话!谢谢我在这里还发现了另一种方法——我不能说哪种方法更好,但也许有人会发现它也很有用。谢谢!我还添加了一个包含参数/protected/:returnUrl的新路由,returnUrl是在指令的ngOnInit处截获的location.path()。这允许用户在登录到最初提示的url后导航。请参阅ans
const adminRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard],
    children: [
      {
        path: '',
        canActivateChild: [AuthGuard],
        children: [
          { path: 'crises', component: ManageCrisesComponent },
          { path: 'heroes', component: ManageHeroesComponent },
          { path: '', component: AdminDashboardComponent }
        ]
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(adminRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AdminRoutingModule {}
import { CanActivate } from '@angular/router';
import { Injectable } from '@angular/core';
import {  } from 'angular-2-local-storage';
import { Router } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(public localStorageService:LocalStorageService, private router: Router){}
canActivate() {
// Imaginary method that is supposed to validate an auth token
// and return a boolean
var logInStatus         =   this.localStorageService.get('logInStatus');
if(logInStatus == 1){
    console.log('****** log in status 1*****')
    return true;
}else{
    console.log('****** log in status not 1 *****')
    this.router.navigate(['/']);
    return false;
}


}

}
// *****And the app.routes.ts file is as follow ******//
      import {  Routes  } from '@angular/router';
      import {  HomePageComponent   } from './home-page/home- page.component';
      import {  WatchComponent  } from './watch/watch.component';
      import {  TeachersPageComponent   } from './teachers-page/teachers-page.component';
      import {  UserDashboardComponent  } from './user-dashboard/user- dashboard.component';
      import {  FormOneComponent    } from './form-one/form-one.component';
      import {  FormTwoComponent    } from './form-two/form-two.component';
      import {  AuthGuard   } from './authguard';
      import {  LoginDetailsComponent } from './login-details/login-details.component';
      import {  TransactionResolver } from './trans.resolver'
      export const routes:Routes    =   [
    { path:'',              component:HomePageComponent                                                 },
    { path:'watch',         component:WatchComponent                                                },
    { path:'teachers',      component:TeachersPageComponent                                         },
    { path:'dashboard',     component:UserDashboardComponent,       canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formone',       component:FormOneComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'formtwo',       component:FormTwoComponent,                 canActivate: [AuthGuard],   resolve: { dashboardData:TransactionResolver } },
    { path:'login-details', component:LoginDetailsComponent,            canActivate: [AuthGuard]    },

]; 
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from 
  '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { environment } from '../../../environments/environment.prod';

@Injectable()
export class AuthGuardService implements CanActivate {
  private returnUrl: string;
  constructor(private _router: Router, private cookie: CookieService) {}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.cookie.get('MasterSignOn')) {
      return true;
    } else {
      let uri = window.location.origin + '/#' + state.url;
      this.returnUrl = encodeURIComponent(uri);      
      window.location.href = environment.ssoPath +  this.returnUrl ;   
      return false;      
    }
  }
}