Jerry's Blog

Recording what I learned everyday

View on GitHub


9 July 2019

Angular (25) -- Authentication

by Jerry Zhang

LeetCode Day 4: P69. Sqrt(x) (Easy)

题目

计算sqrt(x),只要整数部分

我的思路:

直接用Math里面的sqrt, 然后转成整数。

我的算法:

return (int)Math.sqrt(x);

我的代码:

因为这道题太简单了,所以我没用Math里的方法,自己实现了一下。(恰好刚做过一个计算器)

public class E_69_SqrtX {
    public static int mySqrt(int x) {
        if (x == 1) {
            return 1;
        }
        double guess = x / 2;
        double difference = guess * guess - x;
        if (difference < 0) {
            difference = -difference;
        }
        while ( difference > 0.1 ) {
            guess = ( guess + x / guess ) / 2;
            difference = guess * guess - x;
            if (difference < 0) {
                difference = -difference;
            }
        }
        return (int)guess;
    }

    public static void main(String[] args) {
        System.out.println(mySqrt(224));
    }
}

最优解法:

class Solution {
    public int mySqrt(int x) {
        return (int)Math.sqrt(x);
    }
}

Angular

Auto Login

Step 1: We have to store the token in the local storage.

private handleAuthentication(email: string, userId: string, token: string, expiresIn: number) {
    const expirationDate = new Date(new Date().getTime() + expiresIn * 1000);
    const user = new User(email, userId, token, expirationDate);
    this.user.next(user);
    localStorage.setItem('userData', JSON.stringify(user));
}

Now, we have save this user data in the localStorage. Then we need to use this localStorage to auto login.

Step 2: Create a method, autoLogin(), in auth service.

autoLogin() {
    const userData: {
      email: string;
      id: string;
      _token: string;
      _tokenExpirationDate: string;
    } = JSON.parse(localStorage.getItem('userData'));
    if (!userData) {
      return;
    }
    const loadedUser = new User(userData.email, userData.id, userData._token, new Date(userData._tokenExpirationDate));

    if (loadedUser.token) {
      this.user.next(loadedUser);
    }
  }

Step 3: Call this autoLogin method at the very beginning of this app starts. In the app.component:

export class AppComponent implements OnInit {

  constructor(private authService: AuthService) {}

  ngOnInit(): void {
    this.authService.autoLogin();
  }

}

Auto Logout

Our token will expire after one hour. So, we should auto logout when the token expires.

First of all, when we log out, we should clear the user in the localStorage.

logout() {
    this.user.next(null);
    this.router.navigate(['/auth']);
    localStorage.removeItem('userData');
  }

Then we create another method called autoLogout. This method accepts a number expirationDuration. We use setTimeout() of JavaScript to logout after some time.

autoLogout(expirationDuration: number) {
    this.tokenExpirationTimer = setTimeout(() => {
      this.logout();
    }, expirationDuration);
  }

Here, we created a new property, tokenExpirationTimer, because when the user logout by hand, we need to clear this timer so that it will not log out again when the timer ends.

Therefore, our logout() method should be changed like this:

logout() {
    this.user.next(null);
    this.router.navigate(['/auth']);
    localStorage.removeItem('userData');
    if (this.tokenExpirationTimer) {
      clearTimeout(this.tokenExpirationTimer);
    }
    this.tokenExpirationTimer = null;
  }

This means if there is a timer, then clear this timer and set this timer to null.

Next, we need to call this auto logout method somewhere. Basically, we need to call this method whenever we emit a user.

private handleAuthentication(email: string, userId: string, token: string, expiresIn: number) {
    const expirationDate = new Date(new Date().getTime() + expiresIn * 1000);
    const user = new User(email, userId, token, expirationDate);
    this.user.next(user);
    this.autoLogout(expiresIn * 1000);
    localStorage.setItem('userData', JSON.stringify(user));
  }
autoLogin() {
    const userData: {
      email: string;
      id: string;
      _token: string;
      _tokenExpirationDate: string;
    } = JSON.parse(localStorage.getItem('userData'));
    if (!userData) {
      return;
    }
    const loadedUser = new User(userData.email, userData.id, userData._token, new Date(userData._tokenExpirationDate));

    if (loadedUser.token) {
      this.user.next(loadedUser);
      const expirationDuration = new Date(userData._tokenExpirationDate).getTime() - new Date().getTime();
      this.autoLogout(expirationDuration);
    }
  }

Auth Guard

Create a new file auth.guard.ts.

@Injectable({providedIn: 'root'})
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.authService.user.pipe(
      take(1),
      map(user => {
      const isAuth = !!user;
      if (isAuth) {
        return true;
      }
      return this.router.createUrlTree(['/auth']);
    }));
  }

}

If there is a user, we grand the access and return true; If there is not a user, we navigate to another url. We want only listen to the user once, but not listen to this user forever.

In the app-routing, we want to protect the recipes path.

{ path: 'recipes',
    component: RecipesComponent,
    canActivate: [AuthGuard],
    children:
    ...
tags: Angular