如何在Angular 12中实现自动注销

513 阅读8分钟

在Angular 12中实现自动注销

自动注销是Web开发和移动应用中一个普遍存在的功能,特别是在银行系统中。因此,它在确保数据的安全性和完整性方面发挥着重要作用。

自动注销的用处在于,应用程序的用户在使用后可能会忘记注销系统。

本教程讨论了我们如何建立一个安全的Angular应用程序,可以签出闲置的屏幕。

先决条件

要跟上进度,你需要具备以下条件。

  • 具备JavaScript或TypeScript的基本知识
  • 基本的Angular概念
  • 了解Node Package Manager(NPM)的概念。

教学目的

本教程的目的是教给你一切你所需要的Angular应用安全入门知识。

我们将建立一个能自动注销用户的认证应用样本。

开始使用自动注销

自动注销是一项安全功能,它决定了一个屏幕可以保持空闲的时间。简单地说,它是指一个屏幕在没有用户操作(如点击事件)的情况下保持活动的时间。

这个功能在资源管理和安全方面是非常核心的。

在资源管理方面,它有助于停止不必要的API调用,并最大限度地减少受攻击的可能性。

设置一个自动注销项目的样本

让我们在Angular 12中建立一个示例应用程序,实现自动注销的功能。

让我们从安装一个Angular应用程序开始,如下图所示。

ng new sample-auto-logout

这个命令安装了一个Angular应用程序sample-auto-logout ,其中包含所有需要的依赖。

接下来,cd ,进入这个项目根目录,并创建以下组件。

cd sample-auto-logout
ng  g component auth/sign-in
ng g component auth/create-account

上面的命令在auth 目录内创建了2个组件,signInComponentCreateAccountComponent

安装软件包

在这个应用程序中,我们将使用Angular材料来设计我们的网页,以及使用Snotify包来显示警报。

让我们通过运行以下命令将它们添加到我们的Angular应用程序中。

npm i ng-snotify # for NPM users or

yarn add ng-snotify #for yarn

如果你是第一次使用snotify ,请随时在这里浏览它的文档。或者,你也可以使用Toast 通知。让这些警报服务在屏幕上通知用户他们已经注销了,这很重要。

让我们继续通过运行以下命令来添加Angular Material

ng add @angular/material

这提示了快速的是/否问题。

一旦完成,你就可以把所需的模块导入到app.module.ts ,这将在接下来的步骤中解释。

创建认证表格

现在我们的应用程序已经有了auth组件,在CreateAccountComponent 模板中添加以下内容。

<div class="citizen-registration">
	<div class="container">
		<mat-card class="mt-5 mb-5">
			<mat-card-title class="text-center">Register</mat-card-title>
			<mat-card-content class="justify-content-center">
				<form
					[formGroup]="citizenRegistrationForm"
					(ngSubmit)="onCitizenRegistration()"
					novalidate
					role="form"
				>
					<input type="hidden" formControlName="role" value="citizen" />
					<p>
						<mat-form-field appearance="standard" color="primary">
							<mat-label>Full Name</mat-label>
							<input
								matInput
								placeholder="Ezekiel Alawode"
								required
								name="fullName"
								formControlName="fullName"
								autocomplete="fullName"
							/>
							<mat-icon matSuffix>account_circle</mat-icon>
						</mat-form-field>
					</p>
					<p>
						<mat-form-field appearance="standard">
							<mat-label>Town/City</mat-label>
							<input
								matInput
								placeholder="Okene"
								required
								formControlName="city"
								name="city"
								autocomplete="city"
							/>
						</mat-form-field>
					</p>
					<p>
						<mat-form-field appearance="standard">
							<mat-label>Phone</mat-label>
							<input
								type="tel"
								matInput
								placeholder="08143651284"
								required
								name="phone"
								formControlName="phone"
								autocomplete="phone"
							/>
						</mat-form-field>
					</p>

					<p>
						<mat-form-field appearance="standard">
							<mat-label>Email Address</mat-label>
							<input
								type="tel"
								matInput
								placeholder="johndoe@example.com"
								required
								name="email"
								formControlName="email"
								autocomplete="email"
							/>
						</mat-form-field>
					</p>

					<p>
						<mat-form-field appearance="standard">
							<mat-label>Password</mat-label>
							<input
								type="password"
								matInput
								required
								name="password"
								formControlName="password"
								autocomplete="password"
							/>
						</mat-form-field>
					</p>

					<p>
						<mat-checkbox class="example-margin"
							>I agree with the <a href="#">Terms and Conditions</a>, governing
							this site.</mat-checkbox
						>
					</p>
					<div class="row mt-5">
						<div class="col-md-6">
							<button *ngIf="!submitting" type="submit" class="register-button">
								Register
							</button>
							<button *ngIf="submitting" type="submit" class="register-button">
								Processing...
							</button>
						</div>
						<div class="col-md-6">
							<a class="login-button text-right" [routerLink]="['/auth/login']"
								>Login Here</a
							>
						</div>
					</div>
				</form>
			</mat-card-content>
		</mat-card>
	</div>
</div>

上面的模板是一个组织的注册表格样本,公司在其中捕获了用户的详细信息。

这个HTML页面使用Angular材料,通过运行以下命令添加。

ng add @angular/material

这个命令会提示你回答基本的是/否问题,这有助于材料的定制。

我们需要从之前安装的软件包中导入一些模块,以便我们上面的模板能够工作。

最简单的方法是在src/app 目录中创建一个新的模块来实现。

ng g module app-material

现在继续更新这个模块的内容,添加以下内容。

import { NgModule } from "@angular/core";

import { MatCardModule } from "@angular/material/card";
import { MatCheckboxModule } from "@angular/material/checkbox";

@NgModule({
	exports: [MatCardModule, MatCheckboxModule],
})
export class DemoMaterialModule {}

接下来,把这个模块导入到app.module.ts 文件中,如下图所示。

...
import {MaterialModule} from "./material-module";

@NgModule({
  declarations: [
    AppComponent,
    SignInComponent,
    CreateAccountComponent,
  ],
  imports: [
    ...
    AppRoutingModule,
  ],
  providers: [ ],
  bootstrap: [AppComponent]
})
export class AppModule { }

在上面的模块中,我们声明了我们之前创建的组件,然后导入材料模块。

现在,让我们添加一些样式来美化我们的页面。

mat-form-field {
	font-size: 16px;
}
mat-card-title {
	font-family: Poppins;
	font-style: normal;
	font-weight: bolder;
	font-size: 40px;
	line-height: 60px;
	text-align: center;

	color: #ffffff;
}
mat-form-field {
	width: 100%;
	color: #ffffff;
}
mat-card {
	width: 525px;
	height: auto;
	left: auto;
	top: auto;
	margin: 0 auto;
	background: #c60c5a;
	box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.6);
	border-radius: 5px;
}
mat-label {
	font-family: Poppins;
	font-style: normal;
	font-weight: bold;
	font-size: 24px;
	line-height: 36px;
	color: #ffffff;
}
mat-form-field input {
	padding: 5px;
	color: #ffffff;
}
mat-icon {
	color: #ffffff;
}
mat-checkbox {
	color: #ffffff;
}
.register-button {
	width: 194px;
	height: 45px;
	background: #004598;
	box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.5);
	border-radius: 3px;
	font-family: "Poppins", sans-serif;
	font-style: normal;
	font-weight: 600;
	font-size: 16px;
	line-height: 24px;
	text-align: center;

	color: #ffffff;
}
.login-button {
	font-family: "Poppins", sans-serif;
	font-style: normal;
	font-weight: normal;
	font-size: 18px;
	line-height: 27px;
	text-align: right;
	text-decoration-line: underline;
	color: #ffffff;
}

我们在上面的样式文件中用我们定义的CSS样式编辑默认的Angular material表单。当然,你可以自由定制,以满足你的需求。

输出。

register

如何确定一个屏幕是否闲置

现在我们有了一个完整的认证表单,让我们来确定用户的屏幕是否处于闲置状态。

这个工作非常简单,我们跟踪用户在网络应用上的操作。值得注意的是,这些行动相当于事件。

当用户在应用程序上采取行动时,我们将其作为一个行动记录在浏览器的本地存储中。

每当用户采取行动时,我们就重置本地存储中的时钟并重新开始计数。然后这个时钟与本地时间同步,以进行适当的改变。

让我们创建一个服务AutoLogOffService ,并添加以下代码。

...
@Injectable({
  providedIn: 'root'
})
export class AutoLogoutService {

  //log off details
  isLogin = false;

  constructor(
      private router: Router,
      private snotifyService: SnotifyService,
      private ngZone: NgZone
  ) {
    if(this.isUserLoggedIn()){
      this.isLogin=true;
    }
    this.lastAction(Date.now());
    this.check();
    this.initListener();
    this.initInterval();
  }

  /**
   * last action
   */
  getLastAction() {
    return localStorage.getItem('lastAction');
  }

  /**
   * set last action
   * @param value
   */
  lastAction(value) {
    localStorage.setItem('lastAction', JSON.stringify(value))
  }

  /**
   * start event listener
   */
  initListener() {
    this.ngZone.runOutsideAngular(() => {
      document.body.addEventListener('click', () => this.reset());
    });
  }

  /**
   * time interval
   */
  initInterval() {
    this.ngZone.runOutsideAngular(() => {
      setInterval(() => {
        this.check();
      }, 1000);
    })
  }

  /**
   * reset timer
   */
  reset() {
    this.lastAction(Date.now());
  }

  /**
   * check timer
   */
  check() {
    const now = Date.now();
    const timeLeft = parseInt(this.getLastAction()) + (5) * 60 * 1000;
    const diff = timeLeft - now;
    const isTimeout = diff < 0;
    //this.isLoggedIn.subscribe(event => this.isLogin = event);
    this.ngZone.run(() => {
      if (isTimeout && this.isLogin) {
        localStorage.removeItem('user_id');
        localStorage.removeItem('lastAction');
        setTimeout(()=>{
          console.log("Your Session Expired due to longer Inactivity, Login Again To Continue");
        },10000);
        this.router.navigate(['login']);
      }
    });
  }

  /**
   *check if a user is logged in
   */
  isUserLoggedIn():string{
    return environment.authKey;
  }
}

上面的代码有一个时间间隔和事件监听器;因此我们可以根据需要设置自动注销时间。

让我们看看每个步骤,深入了解它是如何工作的。

  • isLogin = false - 这是一个布尔属性,用于检查用户是否已登录。
  • constructor() - 构造器注入了3个服务。
    • router - 这是我们用来在用户被自动注销时将其重定向到登录页面的服务。它是Angular的一个内置工具。
    • snotifyService -Snotify是第三方软件包,用于在不破坏用户界面的情况下在屏幕上显示警报。
    • NgZone - NgZone使我们能够明确地在Angular的Zone之外运行某些代码,防止Angular运行任何变化检测。处理程序仍然会被执行。然而,由于它们不会在Angular的区域内运行,Angular不会得到任务完成的通知。因此,不会有任何变化检测被执行。
  • if statement - 在构造函数中,我们要检查用户是否真的登录了。
  • getLastAction() - 我们用这个方法来获取当前用户与应用程序交互的最新时间。重要的是要记住,我们必须通过重设我们的时钟来跟踪应用程序上的每一个事件,试图获得最新的行动。
  • lastAction(value) - 这是一个简单的方法,在每次事件发生时,在本地存储中设置我们的时钟。
  • initListener() - 之前,我们已经说过,我们正在跟踪用户在应用程序上的活动。它监听应用程序上的每个动作;在我们的例子中,我们监听的是 事件。每次有 事件发生时,这个方法都会重置我们在本地存储中的时钟。click click
  • initInterval() - 这个方法初始化检查点击事件的时间间隔;这完全取决于你想如何跟踪这些情况;在我们的例子中,我们将时间间隔设置为每秒(1000ms=1s)。
  • reset() - 这个方法通过调用 方法并传递给它当前的日期来重置最后一个动作的时钟。lastAction()
  • check() - 这个方法检查定时器。例如,它计算出当前时间和上一个动作的时间之间的差异。然后,这个差值被用来决定用户是应该注销还是保持会话状态。事实上,正是在这个方法上,我们设置了一个屏幕应该保持不活动的时间。在我们的案例中,我们将其设置为 分钟。当然,你可以把它设置为你希望的任何时间。5
  • isUserLoggedIn() - 这个方法通过检查认证令牌来检查用户是否已经登录。然而,这完全由你来决定如何认证用户。

我们实现的问题

在上一节中,你已经看到了如何使用Angular实现自动注销。然而,你可能已经注意到一些设定的时间间隔。

对于我们的应用程序来说,自动注销一个空闲的屏幕,它必须运行一些检查并跟踪每一个动作。这可能会耗费资源。

总结

在本教程中,我们已经介绍了Angular应用程序中自动注销的概念。

我们已经看到,我们可以使用事件监听器来跟踪我们应用程序上的活动,这有助于确定行动。