How to efficiently store and use Authentication in Ionic 3

时光怂恿深爱的人放手 提交于 2019-12-03 07:51:14

I will expose you how I deal with authentification on my ionic app.

The first step consists of creating a representation of the data you want to store. I used to store some user's information and a jwt token for the authentification.

user.ts :

export class AuthenticatedUser {
  private _username: string;
  private _id: number;
  private _token: string;

  public static GetNewInstance(): AuthenticatedUser {
    return new AuthenticatedUser(null, null, null)
  }

  public static ParseFromObject(object): AuthenticatedUser {
    const model = AuthenticatedUser.GetNewInstance();

    if (object) {
      model.username = object.username;
      model.id = object.id;
      model.token = object.token;
    }

    return model;
  }

  constructor(username: string, id: number, token: string) {
    this._username = username;
    this._id = id;
    this._token = token;
  }

  get username(): string {
    return this._username;
  }

  set username(value: string) {
    this._username = value;
  }

  get id(): number {
    return this._id;
  }

  set id(value: number) {
    this._id = value;
  }

  get token(): string {
    return this._token;
  }

  set token(value: string) {
    this._token = value;
  }
}
  • The GetNewInstance method is used to return an instantiated AuthenticatedUser variable.
  • The ParseFromObject method is used to return an AuthenticatedUser from an object. It's very useful to convert an api response object into a model.

After that I create some services. The first one is the UserService. I use it to access my AuthenticatedUser inside my local storage. In the local storage the datas are persistence and will not be delete when you close the app.

user.service.ts :

export class UsersService {

  private _user: Subject<AuthenticatedUser> = new Subject<AuthenticatedUser>();

  constructor(private storage: Storage) {}

  /* ---------------------------------------------------------------------------------------------------------------- */
  /* Observable use object                                                                                            */

  public subscribeToUserService(callback) {
    return this._user.subscribe(callback);
  }

  public updateUserService(user: AuthenticatedUser) {
    this._user.next(user);
  }

  /* ---------------------------------------------------------------------------------------------------------------- */
  /* User storage management                                                                                          */

  /**
   * Write user properties in the local storage.
   *
   * @param user
   * @returns {Promise<AuthenticatedUser>}
   */
  createOnStorage(user: AuthenticatedUser): Promise<AuthenticatedUser> {
    return new Promise((resolve) => {
      this.getOnStorage().then((res) => {
        if (res) {
          this.deleteOnStorage().then(() => {

          });
        }
      }).then(() => {
        this.updateUserService(user);
        this.storage.set('user', JSON.stringify(user));
        resolve();
      });
    });
  }

  /**
   * Get user properties from local storage.
   *
   * @returns {Promise<AuthenticatedUser>}
   */
  getOnStorage(): Promise<AuthenticatedUser> {
    return new Promise((resolve) => {
      this.updateUserService(JSON.parse(this.storage.get('user')));
      resolve(this.storage.get('user'));
    });
  }

  /**
   * Get user properties from local storage.
   *
   * @returns {Promise<AuthenticatedUser>}
   */
  getOnStorageSync() {
    this.updateUserService(JSON.parse(this.storage.get('user')));
    return this.storage.get('user');
  }

  /**
   * Update user properties from local storage.
   *
   * @param user
   * @returns {Promise<AuthenticatedUser>}
   */
  updateOnStorage(user: AuthenticatedUser): Promise<AuthenticatedUser> {
    return new Promise((resolve) => {
      resolve(this.storage.get('user'));
    });
  }

  /**
   * Delete user properties from local storage.
   *
   * @returns {Promise<AuthenticatedUser>}
   */
  deleteOnStorage(): Promise<AuthenticatedUser> {
    return new Promise((resolve) => {
      this.storage.clear();
      resolve();
    });
  }
}

The second service is ApiService. This service is use to send different HTTP methods and handled answers. In this service we will Format the header and access to the authenticatedUser's token.

@Injectable()
export class ApiService {

  private baseUrl = "https://my.api.co";
  private user: AuthenticatedUser;  


  /* ---------------------------------------------------------------------------------------------------------------- */

  constructor(private userService: UserService, private http: Http) {
     getAuthUser()
  }

   private getAuthUser() {
     this.userService.getOnStorage.then(
       (user) => {
         this.user = user;
       });      
   }

   /**
   * Get the Json Web Token from the local storage.
   *
   * @returns {RequestOptions}
   */
  private formatHeader(): RequestOptions {
    const headers: Headers = new Headers();
    if (this.user.token) {
      headers.append('Authorization', 'Bearer ' + this.user.token);
    }
    return new RequestOptions({headers});
  }

  /**
   * Get the body of an HTTP response.
   *
   * @param res
   * @returns {any|{}}
   */
  private handleBody(res: Response) {
    return res.json() || {};
  }

  /**
   * Format the error message of an HTTP response.
   *
   * @param error
   * @returns {any}
   */
  private handleError(error: Response | any) {
    let errorModel: any = {};

    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);

      errorModel = { status: error.status, message: `${error.status} - ${err.cause} ` };
    } else {
      errorModel = { status: error.status, message: error.toString()};
    }
    return Observable.throw(errorModel);
  }
  /* ---------------------------------------------------------------------------------------------------------------- */

  /**
   * Perform a PUT request.
   *
   * @param url
   * @param auth
   * @param body
   * @returns {Observable<>}
   */
  putRequest(url: string, body: Object, auth: boolean = true): Observable<Object> {
    let header = null;

    if (auth) {
      header = ApiService.formatHeader();
    }
    return this.http.put(this.BASE_URL + url, body, header)
      .map(ApiService.handleBody)
      .catch(ApiService.handleError);
  }

  /**
   * Perform a POST request.
   *
   * @param url
   * @param auth
   * @param body
   * @returns {Observable<>}
   */
  postRequest(url: string, body: Object, auth: boolean = true): Observable<Object> {
    let header = null;

    if (auth) {
      header = ApiService.formatHeader();
    }
    return this.http.post(this.BASE_URL + url, body, header)
      .map(ApiService.handleBody)
      .catch(ApiService.handleError);
  }

  /**
   * Perform a HEAD request.
   *
   * @param url
   * @param auth
   * @returns {Observable<>}
   */
  headRequest(url: string, auth: boolean = true): Observable<Object> {
    let header = null;

    if (auth) {
      header = ApiService.formatHeader();
    }
    return this.http.head(this.BASE_URL + url, header)
      .map(ApiService.handleBody)
      .catch(ApiService.handleError);
  }

  /**
   * Perform a GET request.
   *
   * @param url
   * @param auth
   * @returns {Promise<>}
   */
  getRequest(url: string, auth: boolean = true): Observable<Object> {
    let header = null

    if(auth) {
      header = ApiService.formatHeader();
    }

    return this.http.get(this.BASE_URL + url, header)
      .map(ApiService.handleBody)
      .catch(ApiService.handleError);
  }

  /**
   * Perform a DELETE request.
   *
   * @param url
   * @param auth
   * @returns {Observable<>}
   */
  deleteRequest(url: string, auth: boolean = true): Observable<Object> {
    let header = null;

    if (auth) {
      header = ApiService.formatHeader();
    }
    return this.http.delete(this.BASE_URL + url, header)
      .map(ApiService.handleBody)
      .catch(ApiService.handleError);
  }

And the last service is the AuthService. This service will send login/logout requests.

auth.service.ts :

@Injectable()
export class AuthService {
   private user: AuthenticatedUser;
  /* ---------------------------------------------------------------------------------------------------------------- */

  constructor(private userService: userService, private apiService: ApiService) {
  this.getAuthUser();
}
   getAuthUser() {
     this.userService.getOnStorage().then(
        (user) => {
          this.user = user;
        }
     );
   }

  /* ---------------------------------------------------------------------------------------------------------------- */

  /**
   * Request an authentication access.
   *
   * @param email the email of the user
   * @param password the password of the user
   * @returns {Promise<any>}
   */
  login(email: string, password: string): Promise<AuthentificatedUser> {
    return new Promise((resolve, reject) => {
      this.apiService.postRequest('/auth', {email: email, password: password})
        .subscribe(
          res => resolve(AuthentificatedUser.ParseFromObject(res)),
          error => reject(<any>error));
    });
  }

  /**
   * Logout a user from the authentication process.
   *
   * @returns {Promise<any>}
   */
  logout(): Promise<any> {
    return new Promise((resolve) => {
      this.userService.deleteOnStorage().then(() => {
        resolve();
      });
    });
  }


  /**
   * Check whether a user is already logged in.
   *
   * @returns {boolean}
   */
  isLoggedIn() {
    if (this.user.token) {
      return true;
    } else {
      return false;
    }
  }

  /* ---------------------------------------------------------------------------------------------------------------- */
}

In your login page :

this.authService.login('login', 'password').then(
  (user) => {
    this.userSerivce.createOnStorage(user);
    this.navCtrl.push(HomePage);
  }).catch(
    (err) => {
      //show the error
    }
  );

The last thing you have to do this to check if a user is connected or redirect the user to the login screen.

export HomePage() {
  private user: AuthenticatedUser;

  constructor(private userService: UserService) {
    this.getAuthUser();
  }

  private getAuthUser() {
    this.userService.getOnStorage().then(
      (user) => {
        this.user = user;

        if (!this.user.token) {
          this.navCtrl.push(LoginPage)
        }
      }
    );
  }
}

If you want to persist the data durably, you can use ionic-storage : http://ionicframework.com/docs/storage/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!