auto login is implemented

This commit is contained in:
병준 박 2019-12-15 22:49:35 +09:00
parent 497f04efca
commit 0b8c11de48
17 changed files with 288 additions and 65 deletions

View File

@ -0,0 +1,60 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
UrlTree,
Router
} from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
import { environment } from '../../environments/environment';
import * as AuthenticationStore from '@app/store/account/authentication';
@Injectable({
providedIn: 'root'
})
export class AppAutoLoginGuard implements CanActivate {
constructor(
private router: Router,
private store: Store<any>,
private localStorageService: LocalStorageService
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| boolean
| UrlTree
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree> {
return new Promise<boolean | UrlTree>((resolve, reject) => {
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
if (!!appUserInfo && appUserInfo.autoLogin) {
this.store.dispatch(
AuthenticationStore.webLogin({
loginInfo: {
companyCode: appUserInfo.companyCode,
companyGroupType: appUserInfo.companyGroupType,
loginId: appUserInfo.loginId,
loginPw: appUserInfo.loginPw
},
rememberMe: appUserInfo.rememberMe,
autoLogin: appUserInfo.autoLogin
})
);
resolve(false);
} else {
resolve(true);
}
});
}
}

View File

@ -2,12 +2,14 @@ import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginPageComponent } from './components/login.page.component';
import { AppAutoLoginGuard } from '@app/guards/auto-login.guard';
const routes: Routes = [
{ path: '', redirectTo: '/account/login', pathMatch: 'full' },
{
path: 'login',
component: LoginPageComponent
component: LoginPageComponent,
canActivate: [AppAutoLoginGuard]
}
];

View File

@ -6,6 +6,12 @@
[notiText]="fixedNotiBtnText"
[loginBtnEnable]="loginBtnEnable"
[loginBtnText]="loginBtnText"
[companyCode]="appUserInfo?.companyCode"
[loginId]="appUserInfo?.loginId"
[rememberMe]="appUserInfo?.rememberMe"
[autoLogin]="appUserInfo?.autoLogin"
[useRememberMe]="useRememberMe"
[useAutoLogin]="useAutoLogin"
(login)="onLogin($event)"
(notiClick)="onClickNoti($event)"
>

View File

@ -23,6 +23,10 @@ import {
NoticeDialogData
} from '@app/layouts/messenger/dialogs/account/notice.dialog.component';
import { environment } from '../../../../environments/environment';
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
@Component({
selector: 'app-page-account-login',
templateUrl: './login.page.component.html',
@ -44,11 +48,25 @@ export class LoginPageComponent implements OnInit, OnDestroy {
defatulWaitingTime: number;
waitingTime: number;
appUserInfo: AppUserInfo;
useRememberMe: boolean;
useAutoLogin: boolean;
constructor(
private store: Store<any>,
private router: Router,
private dialogService: DialogService
) {}
private dialogService: DialogService,
private localStorageService: LocalStorageService
) {
this.useRememberMe =
environment.productConfig.authentication.rememberMe.use;
this.useAutoLogin = environment.productConfig.authentication.autoLogin.use;
this.appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
}
ngOnInit(): void {
this.defatulLoginBtnText = 'LOGIN';
@ -152,6 +170,7 @@ export class LoginPageComponent implements OnInit, OnDestroy {
loginId: string;
loginPw: string;
rememberMe: boolean;
autoLogin: boolean;
notValid: () => void;
}) {
this.store.dispatch(
@ -162,7 +181,8 @@ export class LoginPageComponent implements OnInit, OnDestroy {
loginId: value.loginId,
loginPw: value.loginPw
},
rememberMe: value.rememberMe
rememberMe: value.rememberMe,
autoLogin: value.autoLogin
})
);
}

View File

@ -14,6 +14,9 @@ import {
import { PasswordUtil } from '@ucap-webmessenger/pi';
import { DaesangCipherService } from '@ucap-webmessenger/daesang';
import { environment } from '../../environments/environment';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
@Injectable({
providedIn: 'root'
})
@ -31,25 +34,33 @@ export class AppAuthenticationService {
return null !== loginInfo && !!loginInfo.loginId;
}
login(loginInfo: LoginInfo, rememberMe: boolean) {
login(loginInfo: LoginInfo, rememberMe: boolean, autoLogin: boolean) {
loginInfo = { ...loginInfo, localeCode: LocaleCode.Korean };
const encLoginPw = this.daesangCipherService.encrypt(
environment.customConfig.pw.userKey,
loginInfo.loginPw,
environment.customConfig.pw.isBase64
);
// PasswordUtil.encrypt(loginInfo.loginPw)
this.sessionStorageService.set<LoginInfo>(KEY_LOGIN_INFO, {
...loginInfo,
initPw: loginInfo.loginId === loginInfo.loginPw,
// loginPw: PasswordUtil.encrypt(loginInfo.loginPw)
loginPw: this.daesangCipherService.encrypt(
'DaesangSSOProject',
loginInfo.loginPw,
'N'
)
loginPw: encLoginPw
});
if (rememberMe) {
this.localStorageService.set<LoginInfo>(KEY_LOGIN_INFO, {
...loginInfo,
loginPw: undefined
});
if (rememberMe || autoLogin) {
this.localStorageService.encSet<AppUserInfo>(
KEY_APP_USER_INFO,
{
...loginInfo,
loginPw: autoLogin ? loginInfo.loginPw : undefined,
rememberMe,
autoLogin
},
environment.customConfig.appKey
);
} else {
this.localStorageService.remove(KEY_LOGIN_INFO);
}

View File

@ -14,6 +14,7 @@ export const webLogin = createAction(
props<{
loginInfo: LoginInfo;
rememberMe: boolean;
autoLogin: boolean;
}>()
);
@ -22,6 +23,7 @@ export const webLoginSuccess = createAction(
props<{
loginInfo: LoginInfo;
rememberMe: boolean;
autoLogin: boolean;
login2Response: Login2Response;
}>()
);

View File

@ -78,29 +78,35 @@ export class Effects {
this.actions$.pipe(
ofType(webLogin),
map(action => action),
exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) =>
this.piService
.login2({
loginId: params.loginInfo.loginId,
loginPw: params.loginInfo.loginPw,
companyCode: params.loginInfo.companyCode
})
.pipe(
map((res: Login2Response) => {
if ('success' !== res.status.toLowerCase()) {
this.store.dispatch(increaseLoginFailCount({}));
return webLoginFailure({ error: 'Failed' });
} else {
this.store.dispatch(initialLoginFailCount({}));
return webLoginSuccess({
loginInfo: params.loginInfo,
rememberMe: params.rememberMe,
login2Response: res
});
}
}),
catchError(error => of(webLoginFailure({ error })))
)
exhaustMap(
(params: {
loginInfo: LoginInfo;
rememberMe: boolean;
autoLogin: boolean;
}) =>
this.piService
.login2({
loginId: params.loginInfo.loginId,
loginPw: params.loginInfo.loginPw,
companyCode: params.loginInfo.companyCode
})
.pipe(
map((res: Login2Response) => {
if ('success' !== res.status.toLowerCase()) {
this.store.dispatch(increaseLoginFailCount({}));
return webLoginFailure({ error: 'Failed' });
} else {
this.store.dispatch(initialLoginFailCount({}));
return webLoginSuccess({
loginInfo: params.loginInfo,
rememberMe: params.rememberMe,
autoLogin: params.autoLogin,
login2Response: res
});
}
}),
catchError(error => of(webLoginFailure({ error })))
)
)
)
);
@ -118,7 +124,8 @@ export class Effects {
if (!update) {
this.appAuthenticationService.login(
params.loginInfo,
params.rememberMe
params.rememberMe,
params.autoLogin
);
this.router.navigate(['/messenger']);
}

View File

@ -0,0 +1,13 @@
import { LocaleCode } from '@ucap-webmessenger/core';
export const KEY_APP_USER_INFO = 'ucap::APP_USER_INFO';
export interface AppUserInfo {
loginId?: string;
loginPw?: string;
rememberMe?: boolean;
autoLogin?: boolean;
companyCode?: string;
companyGroupType?: string;
localeCode?: LocaleCode;
}

View File

@ -22,7 +22,13 @@ export const environment: Environment = {
productId: 'PRO_000482',
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
usePrivateInformationAgree: false,
rememberMe: {
use: false
},
autoLogin: {
use: true
}
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,
@ -30,6 +36,14 @@ export const environment: Environment = {
}
},
customConfig: {
pw: {
userKey: 'DaesangSSOProject',
isBase64: 'N'
},
appKey: '!@#$DAESANG%^&*'
},
commonApiModuleConfig: {
hostConfig: {
protocol: 'http',

View File

@ -22,7 +22,13 @@ export const environment: Environment = {
productId: 'PRO_000482',
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
usePrivateInformationAgree: false,
rememberMe: {
use: false
},
autoLogin: {
use: true
}
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,
@ -30,6 +36,14 @@ export const environment: Environment = {
}
},
customConfig: {
pw: {
userKey: 'DaesangSSOProject',
isBase64: 'N'
},
appKey: '!@#$DAESANG%^&*'
},
commonApiModuleConfig: {
hostConfig: {
protocol: 'http',

View File

@ -22,7 +22,13 @@ export const environment: Environment = {
productId: 'PRO_000482',
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
usePrivateInformationAgree: false,
rememberMe: {
use: false
},
autoLogin: {
use: true
}
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,

View File

@ -22,7 +22,13 @@ export const environment: Environment = {
productId: 'PRO_000482',
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
usePrivateInformationAgree: false,
rememberMe: {
use: false
},
autoLogin: {
use: true
}
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,

View File

@ -44,6 +44,12 @@ export interface Environment {
productName: string;
authentication: {
usePrivateInformationAgree: boolean;
rememberMe: {
use: boolean;
};
autoLogin: {
use: boolean;
};
};
updateCheckConfig: {
deviceType: DeviceType;
@ -51,6 +57,8 @@ export interface Environment {
};
};
customConfig?: any;
commonApiModuleConfig: CommonApiModuleConfig;
publicApiModuleConfig: PublicApiModuleConfig;
externalApiModuleConfig: ExternalApiModuleConfig;

View File

@ -42,16 +42,22 @@
fxLayoutAlign="space-between center"
>
<mat-checkbox
*ngIf="useRememberMe"
class="remember-me"
formControlName="remember"
formControlName="rememberMe"
aria-label="Remember Me"
>
Remember Me
아이디 저장
</mat-checkbox>
<a class="forgot-password">
Forgot Password?
</a>
<mat-checkbox
*ngIf="useAutoLogin"
class="auto-login"
formControlName="autoLogin"
aria-label="Auto Login"
>
자동 로그인
</mat-checkbox>
</div>
<button
@ -67,6 +73,7 @@
<div class="register" fxLayout="column" fxLayoutAlign="center center">
<span class="text">Don't have an account?</span>
<a class="link">Forgot Password?</a>
<a class="link">Create an account</a>
</div>
<div class="policy">

View File

@ -32,7 +32,7 @@
margin-bottom: 16px;
}
.forgot-password {
.auto-login {
font-size: 13px;
font-weight: 600;
margin-bottom: 16px;

View File

@ -10,8 +10,6 @@ import {
} from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Company } from '@ucap-webmessenger/api-external';
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
@Component({
selector: 'ucap-account-login',
@ -30,12 +28,27 @@ export class LoginComponent implements OnInit {
@Input()
notiText?: string;
@Input()
companyCode: string;
@Input()
loginId: string;
@Input()
rememberMe: boolean;
@Input()
autoLogin: boolean;
@Input()
useRememberMe: boolean;
@Input()
useAutoLogin: boolean;
@Output()
login = new EventEmitter<{
companyCode: string;
loginId: string;
loginPw: string;
rememberMe: boolean;
autoLogin: boolean;
notValid: () => void;
}>();
@Output()
@ -47,35 +60,37 @@ export class LoginComponent implements OnInit {
constructor(
private formBuilder: FormBuilder,
private changeDetectorRef: ChangeDetectorRef,
private localStorageService: LocalStorageService
private changeDetectorRef: ChangeDetectorRef
) {}
ngOnInit() {
const loginInfo: LoginInfo = this.localStorageService.get<LoginInfo>(
KEY_LOGIN_INFO
);
this.loginForm = this.formBuilder.group({
companyCode: ['', [Validators.required]],
loginId: ['', [Validators.required]],
loginPw: ['', Validators.required],
remember: [false]
rememberMe: [false],
autoLogin: [false]
});
if (!!this.rememberMe && this.rememberMe) {
this.loginForm.get('rememberMe').setValue(true);
}
if (!!this.autoLogin && this.autoLogin) {
this.loginForm.get('autoLogin').setValue(true);
}
if (!!this.curCompanyCode) {
this.loginForm.get('companyCode').setValue(this.curCompanyCode);
}
if (loginInfo && loginInfo.companyCode && loginInfo.loginId) {
this.loginForm.get('companyCode').setValue(loginInfo.companyCode);
this.loginForm.get('loginId').setValue(loginInfo.loginId);
this.loginForm.get('remember').setValue(true);
this.changeDetectorRef.detectChanges();
if (!!this.loginId) {
this.loginForm.get('loginId').setValue(this.loginId);
}
if (!this.loginBtnText || this.loginBtnText.trim().length === 0) {
this.loginBtnText = 'LOGIN';
}
this.changeDetectorRef.detectChanges();
}
onClickLogin() {
@ -83,7 +98,8 @@ export class LoginComponent implements OnInit {
companyCode: this.loginForm.get('companyCode').value,
loginId: this.loginForm.get('loginId').value,
loginPw: this.loginForm.get('loginPw').value,
rememberMe: this.loginForm.get('remember').value,
rememberMe: this.loginForm.get('rememberMe').value,
autoLogin: this.loginForm.get('autoLogin').value,
notValid: () => {
this.loginPwElementRef.nativeElement.focus();
}

View File

@ -2,6 +2,8 @@ import { StorageUtil, ExpiredUnit } from '../utils/storage.util';
import { fromEvent, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import CryptoJS from 'crypto-js';
export class StorageService {
constructor(private storage: Storage) {}
@ -18,6 +20,35 @@ export class StorageService {
return StorageUtil.set(this.storage, key, value, expiredAt, expiredUnit);
}
encGet<T>(key: string, secretPassphrase: string): T | null {
const encrypted = StorageUtil.get(this.storage, key) as CryptoJS.WordArray;
if (!encrypted) {
return null;
}
const decrypted = CryptoJS.AES.decrypt(encrypted, secretPassphrase);
const json = decrypted.toString(CryptoJS.enc.Utf8);
return JSON.parse(json) as T;
}
encSet<T>(
key: string,
value: T,
secretPassphrase: string,
expiredAt: number = 0,
expiredUnit: ExpiredUnit = 'd'
) {
const json = JSON.stringify(value);
const encrypted = CryptoJS.AES.encrypt(json, secretPassphrase);
return StorageUtil.set(
this.storage,
key,
encrypted.toString(),
expiredAt,
expiredUnit
);
}
remove(key: string | RegExp) {
if (typeof key === 'string') {
StorageUtil.remove(this.storage, key);