JHipster一知半解- 4.6.4 webapp-share
回文集目录: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;
}));
}