JHipster一知半解- 4.6.4 webapp-shared目錄-1

weixin_34124651發表於2018-06-13

迴文集目錄:JHipster一知半解

shared.module.ts

SharedModule整合了Lib模組,Common模組兩個子模組的內容,並且重新匯出,作為整個的共享模組(工具模組)使用。

@NgModule({
    //引入兩個子模組
    imports: [
        JhipsterSampleApplicationNg2SharedLibsModule,
        JhipsterSampleApplicationNg2SharedCommonModule
    ],
    //宣告自己的元件,指令
    declarations: [
        JhiLoginModalComponent,
        HasAnyAuthorityDirective
    ],
    //宣告通用的服務,主要,由於不是在主模組provider了,只有引入SharedModule的才能使用這些
    //實際上,其他所有的子模組都有引入SharedLibsModule。
    providers: [
        LoginService,
        LoginModalService,
        AccountService,
        StateStorageService,
        Principal,
        CSRFService,
        AuthServerProvider,
        UserService,
        DatePipe
    ],
    //模組的入口元件--SharedModule並不會被路由懶載入到,所有宣告並沒有意義
    entryComponents: [JhiLoginModalComponent],
    exports: [
        JhipsterSampleApplicationNg2SharedCommonModule,
        JhiLoginModalComponent,
        HasAnyAuthorityDirective,
        DatePipe
    ],
    //語言模式,CUSTOM_ELEMENTS_SCHEMA是angular提供的一種模式,允許在命名時使用“-”。
    /* Defines a schema that will allow:
    * - any non-Angular elements with a `-` in their name,
    * - any properties on elements with a `-` in their name which is the common rule for custom elements.
    */
    schemas: [CUSTOM_ELEMENTS_SCHEMA]

})
export class JhipsterSampleApplicationNg2SharedModule {}

shared-libs.module.ts

@NgModule({
    imports: [
        NgbModule.forRoot(),
        NgJhipsterModule.forRoot({
            // set below to true to make alerts look like toast
            alertAsToast: false,
            i18nEnabled: true,
            defaultI18nLang: 'en'
        }),
        InfiniteScrollModule,
        CookieModule.forRoot()
    ],
    exports: [
        FormsModule,
        HttpModule,
        CommonModule,
        NgbModule,
        NgJhipsterModule,
        InfiniteScrollModule
    ]
})
export class JhipsterSampleApplicationNg2SharedLibsModule {}

SharedLibsModule比較簡單,引用了NgbModule,NgJhipsterModule,InfiniteScrollModule,CookieModule,並且初始化了它們,重新匯出了它們。
值得主要的是,這裡是初始化ng-jhipster的地方,參見
TODO:連結ng-jhipster原始碼1-頂層

shared-common.module.ts

定義了內部(JHipster)實現的語言幫助(顯示Title),警告元件兩個部分,
奇怪的是,有的元件是放Shared頂層的,有的又放到Common裡面,是因為語言幫助和警告框更有通用性吧

alert目錄

JhiAlertErrorComponent和JhiAlertComponent兩個元件的外觀(template)是一直的,但是內部邏輯有所區別。JhiAlertComponent就完全依賴alertService,使用服務進行顯示提示框。
則在JhiAlertErrorComponent在自己內部儲存alert陣列(也依賴alertService構造),並且通過cleanHttpErrorListener註冊監聽httpError的錯誤,通過非同步的方式在compont裡面進行通訊錯誤的提示。

auth目錄

account.service.ts

@Injectable()
export class AccountService  {
    //這裡注入的http是Jhipster修改過的。
    constructor(private http: Http) { }
    //呼叫Restful介面獲取使用者資料,返回一個any型,比較寬鬆
    get(): Observable<any> {
        return this.http.get(SERVER_API_URL + 'api/account').map((res: Response) => res.json());
    }
    //呼叫Restful儲存獲取使用者資料
    save(account: any): Observable<Response> {
        return this.http.post(SERVER_API_URL + 'api/account', account);
    }
}

auth-jwt.service.ts

在Shared內部使用的登入輔助類,JWT認證方式時候的通訊類。

    //傳入憑證資訊,進行登入認證通訊
    login(credentials): Observable<any> {
        //post內容,使用者名稱,密碼,是否記住
        const data = {
            username: credentials.username,
            password: credentials.password,
            rememberMe: credentials.rememberMe
        };
        //這裡呼叫api/authenticate認證,
        return this.http.post(SERVER_API_URL + 'api/authenticate', data).map(authenticateSuccess.bind(this));

        function authenticateSuccess(resp) {
            //登入成功從返回報文頭獲取Authorization,並且使用魔數“Bearer ”分隔。
            const bearerToken = resp.headers.get('Authorization');
            if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') {
                const jwt = bearerToken.slice(7, bearerToken.length);
                this.storeAuthenticationToken(jwt, credentials.rememberMe);
                return jwt;
            }
        }
    }
    //另外一種登入方式,僅儲存的jwt資訊,無需特別通訊(在每次通訊的報文頭都會帶上jwt的token資訊)
    loginWithToken(jwt, rememberMe) {
        if (jwt) {
            this.storeAuthenticationToken(jwt, rememberMe);
            return Promise.resolve(jwt);
        } else {
            return Promise.reject('auth-jwt-service Promise reject'); // Put appropriate error message here
        }
    }
    //根據rememberMe的值,決定是儲存到$localStorage還是$sessionStorage
    storeAuthenticationToken(jwt, rememberMe) {
        if (rememberMe) {
            this.$localStorage.store('authenticationToken', jwt);
        } else {
            this.$sessionStorage.store('authenticationToken', jwt);
        }
    }
    //無http操作,僅清空本地儲存的token即可
    logout(): Observable<any> {

        return new Observable((observer) => {
            this.$localStorage.clear('authenticationToken');
            this.$sessionStorage.clear('authenticationToken');
            observer.complete();
        });
    }

其中$localStorage和$sessionStorage是由ngx-webstorage(之前是ng2-webstorage)提供的瀏覽器端儲存服務,顧名思義可知,API呼叫方法相同,儲存的位置,有效期不同。

csrf.service.ts

從cookie中獲取XSRF-TOKEN的值。

has-any-authority.directive.ts

通過判斷許可權列表,決定頁面某個標籤是否顯示。通過注入principal進行判斷。其判斷有點類似ngif的指令,根據輸入的值,呼叫viewContainerRef重新繪製頁面。

P.S Jhipster的許可權控制,大都是Hard code近程式碼的,比較簡單,動態性小。

principal.service.ts

核心的服務,儲存使用者的認證資訊,使用者名稱,是否認證,認證狀態主題(Subject),核心程式碼為幾個使用者許可權的判斷hasAnyAuthority(hasAnyAuthority,hasAnyAuthorityDirect,hasAuthority),以及使用者狀態的判斷isAuthenticated。

state-storage.service.ts

constructor(
        private $sessionStorage: SessionStorageService
    ) {}

在構造器中注入$sessionStorage,用來在URL跳轉中能記錄狀態。主要包括previousState,destinationState,previousUrl三種資訊。其中previousState包含name和params兩個內容。

storePreviousState(previousStateName, previousStateParams) {
        const previousState = { 'name': previousStateName, 'params': previousStateParams };
        this.$sessionStorage.store('previousState', previousState);
    }

destinationState包含目標狀態destinationState,目標引數destinationStateParams,目標來源fromState三個內容

storeDestinationState(destinationState, destinationStateParams, fromState) {
        const destinationInfo = {
            'destination': {
                'name': destinationState.name,
                'data': destinationState.data,
            },
            'params': destinationStateParams,
            'from': {
                'name': fromState.name,
            }
        };
        this.$sessionStorage.store('destinationState', destinationInfo);
    }

user-route-access-service.ts

它是一個路由守衛CanActivate,用來控制能否進行url跳轉,進入某個頁面,核心方法為canActivate,呼叫checkLogin進行驗證
checkLogin(authorities: string[], url: string): Promise<boolean> {
        const principal = this.principal;
        //返回一個Promise物件
        return Promise.resolve(principal.identity().then((account) => {
            //判斷要求的許可權列表,為空直接放行
            if (!authorities || authorities.length === 0) {
                return true;
            }
            //如果登入情況,呼叫principal是否具有許可權
            if (account) {
              return principal.hasAnyAuthority(authorities).then(
                (response) => {
                  if (response) {
                    return true;
                  }
                  return false;
                }
              );
            }
            //登記當前url,呼叫stateStorageService儲存當前url,並且通過loginModalService顯示登入框給使用者登入。
            this.stateStorageService.storeUrl(url);
            this.router.navigate(['accessdenied']).then(() => {
                // only show the login dialog, if the user hasn't logged in yet
                if (!account) {
                    this.loginModalService.open();
                }
            });
            return false;
        }));
    }

相關文章