Angular实现登录保护

1. 前言

一个网站,通常都会包含公开页面和受保护页面两种,如果是 OA 或者企业应用网站,甚至可能全部都是保护页面,访问者需要在进行身份认证后,才能正常的浏览相关页面。

在本实验中, 我们会创建一个登录页面, 一个受保护的页面, 和一个面向公众公开的信息页面. 当用户访问受保护页面时, 需要先登录再访问, 而公众公开页面不需要登录即可访问.

2. 创建一个 angular 应用

创建一个名为 test 的新 angular 项目

1
ng new test

执行完以上命令, angular cli 将为我们创建基本的项目骨架.
对于项目的结构的详解, 可以参考鹏叔的博客空间关于创建 Angular 项目

3. 创建页面

创建三个页面, 一个命名为 public, 一个为 private, 一个为 login, public 页面无需登录即可访问, 而 private 页面需要先登录然后才能访问,
当用户在未登录的情况下访问 private, 则被导航到 login 页面被要求登录.

创建 public page, anuglar 的每一个页面都对于于一个 component, 所以创建页面也就是创建 component

1
2
3
4
5
6
$ ng g component components/public
CREATE src/app/components/public/public.component.html (21 bytes)
CREATE src/app/components/public/public.component.spec.ts (626 bytes)
CREATE src/app/components/public/public.component.ts (275 bytes)
CREATE src/app/components/public/public.component.css (0 bytes)
UPDATE src/app/app.module.ts (407 bytes)

创建 private page

1
2
3
4
5
6
7
8

$ ng g component components/private
CREATE src/app/components/private/private.component.html (22 bytes)
CREATE src/app/components/private/private.component.spec.ts (633 bytes)
CREATE src/app/components/private/private.component.ts (279 bytes)
CREATE src/app/components/private/private.component.css (0 bytes)
UPDATE src/app/app.module.ts (504 bytes)

创建 login page

1
2
3
4
5
6
$ ng g component components/login
CREATE src/app/components/login/login.component.html (20 bytes)
CREATE src/app/components/login/login.component.spec.ts (619 bytes)
CREATE src/app/components/login/login.component.ts (271 bytes)
CREATE src/app/components/login/login.component.css (0 bytes)
UPDATE src/app/app.module.ts (835 bytes)

4. 添加路由

4.1. 引入@angular/router 包

1
npm i --save @angular/router

4.2. 创建路由配置

前面提到每一个页面都会对应一个 component, 当用户访问 angular SPA 应用时, 当路径匹配到路由中配置的路径时, 就会将该路径对应的 component 加载到根组件 AppComponent 的<router-outlet></router-outlet>节点.
在 src/app 目录下创建一个名为 app.routes.ts 的文件, 在配置文件中添加两条路由配置如下.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Routes } from "@angular/router";
import { PublicComponent } from "./components/public/public.component";
import { PrivateComponent } from "./components/private/private.component";
import { LoginComponent } from "./components/login/login.component";

export const rootRouterConfig: Routes = [
{
path: "login", // http://localhost:4200/login
component: LoginComponent,
},
{
path: "public", // http://localhost:4200/public
component: PublicComponent,
},
{
path: "private", // http://localhost:4200/private
component: PrivateComponent,
},
];

4.3. 加载路由配置

并在 app.module.ts 中引入该模块.
根路由模块包含了路由所需的使用服务,它以路由配置为参数,调用 RouterModule.forRoot()
app.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在根模块中导入路由配置
import { rootRouterConfig } from "./app.routes"; // 注意没有.ts
// 需要调用RouterModule.forRoot()方法
import { RouterModule } from "@angular/router";
// 创建路由模块
// 根路由模块默认提供的路由策略为PathLocationStrategy(另外一个是HashLocationStrategy)。
// PathLocationStrategy路由策略需要一个base路径,设置base路径有2种方式,最简单的是在index.html中设置<base>
// 如果要创建HashLocationStrategy 可以使用如下方式创建, 即需要传递options {useHash: true}
// const rootRouterModule: ModuleWithProviders = RouterModule.forRoot(rootRouterConfig, {useHash: true});
const rootRouterModule: ModuleWithProviders =
RouterModule.forRoot(rootRouterConfig);

// 然后将路由模块导入AppModule
@NgModule({
declarations: [],
imports: [rootRouterModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

修改根组件
一般情况下,这个指令是放在根组件中。
app.conponent.html

1
2
3
<section class="container">
<router-outlet></router-outlet>
</section>

5. 路由进阶 - 身份认证

首先我们添加一个 AuthGuard, 此 Guard 的职责就是坚持用户是否登录, 如果未登录则将用户导航到登陆页面.

1
$ng g guard guards/Auth

然后实现 canActivate 接口
src\app\guards\auth.guard.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { Injectable } from "@angular/core";
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree,
} from "@angular/router";
import { Observable } from "rxjs";

@Injectable({
providedIn: "root",
})
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}

canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>
| boolean
| UrlTree {
console.log("AuthGuard#canActivate called");
if (this.isLoggedIn()) {
// l已登录,返回Ture
console.log("AuthGuard: 用户已登陆。");
return true;
}
// 未登陆,重定向URL到登录页面,包含返回URL参数,然后返回False
this.router.navigate(["/login"], { queryParams: { returnUrl: state.url } });
return false;
}

private isLoggedIn(): boolean {
//随机返回Ture /False
let loggedIn: boolean = false;
if (!loggedIn) {
console.log("AuthGuard: 用户未登陆。");
}
return loggedIn;
}
}

这样一个简单的 AuthGuard 就实现了, 在这个身份验证 guard 中, 我们强制的将 loggedIn 状态设置为 false, 让用户处于未登录状态, 来模拟用户未登录状态先, 访问 private 页面被导航到登录页面.

有了 AuthGuard 后, 我们修改一下路由配置, 让其守护 private 页面, 保证只有登录用户才让访问 private 页面.
修改 app.routes.ts

1
2
3
4
5
{
path: 'private', // http://localhost:4200/private
component: PrivateComponent,
canActivate: [AuthGuard]
},

同时修改一下 app.module.ts, 在 application 启动时加载 AuthGuard

6. Angular 系列文章

最新更新以及更多 Angular 相关文章请访问 Angular 合集 | 鹏叔的技术博客

7. 参考文档

Angular 2.0 SPA 应用 - 身份认证 2
Angular2 使用 Guard 和 Resolve 进行验证和权限控制