auto login is implemented
This commit is contained in:
parent
497f04efca
commit
0b8c11de48
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -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)"
|
||||
>
|
||||
|
|
|
@ -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
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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, {
|
||||
if (rememberMe || autoLogin) {
|
||||
this.localStorageService.encSet<AppUserInfo>(
|
||||
KEY_APP_USER_INFO,
|
||||
{
|
||||
...loginInfo,
|
||||
loginPw: undefined
|
||||
});
|
||||
loginPw: autoLogin ? loginInfo.loginPw : undefined,
|
||||
rememberMe,
|
||||
autoLogin
|
||||
},
|
||||
environment.customConfig.appKey
|
||||
);
|
||||
} else {
|
||||
this.localStorageService.remove(KEY_LOGIN_INFO);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}>()
|
||||
);
|
||||
|
|
|
@ -78,7 +78,12 @@ export class Effects {
|
|||
this.actions$.pipe(
|
||||
ofType(webLogin),
|
||||
map(action => action),
|
||||
exhaustMap((params: { loginInfo: LoginInfo; rememberMe: boolean }) =>
|
||||
exhaustMap(
|
||||
(params: {
|
||||
loginInfo: LoginInfo;
|
||||
rememberMe: boolean;
|
||||
autoLogin: boolean;
|
||||
}) =>
|
||||
this.piService
|
||||
.login2({
|
||||
loginId: params.loginInfo.loginId,
|
||||
|
@ -95,6 +100,7 @@ export class Effects {
|
|||
return webLoginSuccess({
|
||||
loginInfo: params.loginInfo,
|
||||
rememberMe: params.rememberMe,
|
||||
autoLogin: params.autoLogin,
|
||||
login2Response: res
|
||||
});
|
||||
}
|
||||
|
@ -118,7 +124,8 @@ export class Effects {
|
|||
if (!update) {
|
||||
this.appAuthenticationService.login(
|
||||
params.loginInfo,
|
||||
params.rememberMe
|
||||
params.rememberMe,
|
||||
params.autoLogin
|
||||
);
|
||||
this.router.navigate(['/messenger']);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
.auto-login {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user