Angular 2 authenticate state

2019-05-02 07:33发布

I've implemented a login page using Angular 2. After login, I get jsonwebtoken, userId, userRole, userName from server. I'm storing this info in localstorage so that I can access it any time and maintain login state if user refreshes page.

AuthService.ts

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

@Injectable()
export class AuthService {
  redirectUrl: string;

  logout() {
    localStorage.clear();
  }

  isLoggedIn() {
    return localStorage.getItem('token') !== null;
  }

  isAdmin() {
    return localStorage.getItem('role') === 'admin';
  }

  isUser() {
    return localStorage.getItem('role') === 'user';
  }

}

To check the login status, I'm just checking if token exists in localstorage. As localstorage is editable so just adding any token in localstorage would bypass login page. Similarly, if client edit user role in localstorage, client can easily access admin or user pages.

How do I solve these problems?

This is more like a general problem, I want to know how websites maintain login status?

P.S. NodeJS Server side login code to generate jsonwebtoken

const jwt = require('jsonwebtoken');
const User = require('../models/User');

/**
 * POST /login
 * Sign in using username and password
 */
exports.postLogin = (req, res, next) => {
    User.findOne({username: req.body.username})
        .then(user=> {
            if (!user) {
                res.status(401);
                throw new Error('Invalid username');
            }
            return user.comparePassword(req.body.password)
                .then(isMatch=> {
                    if (isMatch != true) {
                        res.status(401);
                        throw new Error('Invalid password');
                    }
                    let token = jwt.sign({user: user}, process.env.JWT_SECRET, {
                        expiresIn: process.env.JWT_TIMEOUT
                    });
                    return res.status(200).json({
                        success: true,
                        token: token,
                        userId: user._id,
                        role:user.role,
                        name:user.name
                    });
                });
        })
        .catch(err=>next(err));
};

-Thanks

3条回答
Animai°情兽
2楼-- · 2019-05-02 07:48

1) Tokens are supposed to be unique and hard to type (as of a big length). Also, they should be refreshed with some frequency. Better to read oAuth docs on this

2) Roles should not be stored on client side. Only checking on server. Also, when using oAuth consider using Scopes.

查看更多
淡お忘
3楼-- · 2019-05-02 07:50

store token in localStorage/sessionStorage and validate token with server whenever required. I am having following implementation to validate token

UserProfileService.ts

@Injectable()
export class UserProfileService {
  private isLoggedIn: boolean = false;
  private apiEndPoint: string;
  constructor(private http: Http) {
    this.apiEndPoint = environment.apiEndpoint;
  }

  login(token: string) {
    localStorage.setItem('auth_token', token);
    this.isLoggedIn = true;
  }

  logout(){
    localStorage.removeItem('auth_token');
    this.isLoggedIn = false;
  }

  isAuthorized(): Observable<boolean> {
    if (!this.isLoggedIn) {
      let authToken = localStorage.getItem('auth_token');
      if(authToken){
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');
        headers.append('Accept', 'application/json');
        headers.append('Authorization', `Bearer ${authToken}`);
        return this.http.get(`${this.apiEndPoint}/validate-token`, { headers: headers })
        .map(res => {
          let serverResponse = res.json();
          this.isLoggedIn = serverResponse['valid'];
          if (!this.isLoggedIn) {
            localStorage.removeItem('auth_token');
          }
          return this.isLoggedIn;
        })
        .catch(this._serverError);
      }
    }
    return Observable.of(this.isLoggedIn);
  }

  private _serverError(err: any) {
    localStorage.removeItem('auth_token');
    if(err instanceof Response) {
      console.log(err.json());
      return Observable.of(false);
    }
    return Observable.of(false);
  }

}

AuthService.ts

@Injectable()
export class CanActivateAuthGuard implements CanActivate, CanActivateChild, CanLoad {
  constructor(private userProfileService: UserProfileService, private router: Router) { }

  canLoad(route: Route) {
    return this.userProfileService.isAuthorized().map(authorized => {
      if(authorized) {
        return authorized;
      } else {
        let url = `/${route.path}`;
        this.router.navigate(['/login'], { queryParams: { redirectTo: url } });
        return authorized;
      }
    });
  }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {
    return this.userProfileService.isAuthorized().map(authorized => {
      if(authorized) {
        return authorized;
      } else {
        this.router.navigate(['/login'], { queryParams: { redirectTo: state.url } });
        return authorized;
      }
    });
  }

  canActivateChild(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ) {
    return this.canActivate(route, state);
  }
}
查看更多
Juvenile、少年°
4楼-- · 2019-05-02 08:09

You digitally sign the authentication token on the server side:

jwt.sign({user: user}, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_TIMEOUT
})

This signature then should be verified by the server side on subsequent requests. It becomes invalid when client changes the content of the token.

查看更多
登录 后发表回答