186 lines
6.4 KiB
Plaintext
186 lines
6.4 KiB
Plaintext
= 로그인
|
|
|
|
SDL은 Knox EpTray, ID/PW, ADFS 로그인을 지원한다.
|
|
|
|
== EpTray
|
|
Knox EpTray 로그인은 Chrome, Edge 등 멀티 브라우저에서 가능하다.
|
|
사용자가 Knox에 로그인을 한 후 시스템에 접속을 하게 되면 EpTray값을 이용해 시스템 로그인을 한다.
|
|
|
|
NOTE: Knox EpTray SSO 를 위한 연계 신청이 필요하다. (스테이지/운영) +
|
|
자세한 내용 안내 및 문의는 Knox Support (매뉴얼 > KnoxPortal NewEpTray 연계 가이드) 를 참조한다.
|
|
|
|
=== EpTray 적용
|
|
연계신청 과정에서 생성되는 *rsaprivkey8.pem* 파일을 +
|
|
config.properties의 login.sso.knox-tray-private-key-path 경로에 넣어준다.
|
|
|
|
CAUTION: *rsaprivkey8.pem* 파일이 없는 경우 아래와 같은 에러가 발생하므로 주의한다.
|
|
|
|
image::newEpTrayUtil.png[Can not load new EpTray Private Key.png]
|
|
|
|
=== LoginPage.vue
|
|
|
|
UI 에서 사용자 정보가 없을 경우 LoginPage.vue 페이지로 이동하게 되고 인증 절차를 시작한다.
|
|
|
|
EpTray 연계를 위한 메서드
|
|
|
|
[source,javascript]
|
|
----
|
|
async loginByNewEpTray() {
|
|
let loginResult = false;
|
|
SDLUtil.showLoadingBar(true);
|
|
await this.socConnect().then((data) => {
|
|
this.newEpTrayKey = data;
|
|
this.websocket.close();
|
|
}).catch((err) => {
|
|
SDLUtil.alert(err);
|
|
});
|
|
SDLUtil.showLoadingBar(false);
|
|
|
|
if (this.newEpTrayKey) {
|
|
if (this.newEpTrayKey.result === 'success') {
|
|
const { userInfo, key } = this.newEpTrayKey;
|
|
loginResult = await this.loginNewEpTray({ userInfo, key });
|
|
} else {
|
|
SDLUtil.alert(`sdl.epTray.error.${this.newEpTrayKey.errorCode}`);
|
|
}
|
|
}
|
|
|
|
if (!loginResult) {
|
|
if (this.$route.query.token) {
|
|
loginService.setToken(this.$route.query.token);
|
|
document.location = `${SDLUtil.WEB_CONTEXT_PATH}/`;
|
|
}
|
|
}
|
|
this.afterLoginProcess(loginResult);
|
|
},
|
|
----
|
|
|
|
[source,javascript]
|
|
----
|
|
socConnect() {
|
|
return new Promise(((resolve, reject) => {
|
|
const server = new WebSocket('wss://localhost:29283'); // <1>
|
|
this.websocket = server;
|
|
server.onopen = () => {
|
|
server.send('{"rqtype":"getknoxsso","token":"","data":"KCC60TRAY0072"}'); // <2>
|
|
server.onmessage = (event) => {
|
|
const socketData = JSON.parse(event.data);
|
|
if (socketData.rpcode === 'RETURN_SUCCESS') {
|
|
resolve(JSON.parse(socketData.data));
|
|
} else {
|
|
this.websocket.close();
|
|
let err;
|
|
if (socketData.rpcode === 'EMPTY_BOX') err = 'sdl.epTray.rpCode.EMPTY_BOX';
|
|
else err = `sdl.epTray.rpCode.${socketData.detail}`;
|
|
reject(err);
|
|
}
|
|
};
|
|
};
|
|
server.onerror = () => {
|
|
const err = 'sdl.epTray.rpCode.CONNECTION_FAILED';
|
|
reject(err);
|
|
};
|
|
}));
|
|
},
|
|
----
|
|
<1> WebSocket HOST 정보
|
|
<2> 발급받은 연계용 system ID
|
|
|
|
=== LoginController.java
|
|
|
|
LoginController 에서는 EpTray값의 정보를 이용해 사용자를 로그인 한다.
|
|
이때 시스템 사용자가 아닐 경우, 등록 화면으로 이동하고, 정상적인 시스템 사용자일 경우
|
|
Token을 발급한다.
|
|
|
|
GET /noauth/login/new-eptray
|
|
|
|
[source,java]
|
|
----
|
|
@PostMapping("/noauth/login/new-eptray")
|
|
@Operation(value = "", description = "knox new eptray 로그인")
|
|
public JwtResponse loginByNewEptray(HttpServletRequest request, HttpServletResponse response, @RequestParam String encodeUserInfo, @RequestParam String encodeAesKey ) throws AccountException {
|
|
loginInterceptorExecutor.applyPreLogin(request, response);
|
|
User user = authenticationService.loginByNewEpTray(encodeUserInfo, encodeAesKey);
|
|
user.setRecentLoginIp(webUtil.getClientIp(request));
|
|
JwtResponse jwtResponse = new JwtResponse(jwtUtil.generateToken(user), user.getUserId());
|
|
user.setJwt(jwtResponse.getJwtToken().split("\\.")[2]);
|
|
loginInterceptorExecutor.applyPostLogin(request, response, user);
|
|
return jwtResponse;
|
|
}
|
|
----
|
|
|
|
== ADFS 로그인
|
|
|
|
NOTE: ADFS 통합인증 사이트 (https://adsso.sec.samsung.net) 에서 신청 후 사용해야 한다. +
|
|
자세한 내용은 해당 사이트를 참조하거나 전자통합인증3 (nextsso3@samsung.com) 으로 문의 한다.
|
|
|
|
SDL에서는 ADFS 통합인증 사이트의 가이드를 참조하여 AD 로그인 샘플을 제공하고 있으며, 각 프로젝트 환경에 맞게 수정하여 사용한다.
|
|
|
|
=== 참고할 샘플 파일
|
|
* LoginPage.vue
|
|
* AdLoginController.java
|
|
* onelogin.saml.properties
|
|
|
|
== 로그인/아웃 전,후 처리
|
|
|
|
로그인 전과 후, 로그아웃 전과 후 비즈니스 로직이 필요한 경우 LoginInterceptor를 상속받아 구현한다.
|
|
|
|
LoginInterceptor 클래스는 아래와 같은 Interface를 제공한다.
|
|
[source, java]
|
|
----
|
|
/**
|
|
* 로그인 전
|
|
*/
|
|
default void preLogin(HttpServletRequest request, HttpServletResponse response) {
|
|
}
|
|
|
|
/**
|
|
* 로그인 후
|
|
*/
|
|
default void postLogin(HttpServletRequest request, HttpServletResponse response, User user) {
|
|
}
|
|
|
|
/**
|
|
* 로그아웃 전
|
|
*/
|
|
default void preLogout(HttpServletRequest request, HttpServletResponse response, User user) {
|
|
}
|
|
|
|
/**
|
|
* 로그아웃 후
|
|
*/
|
|
default void postLogout(HttpServletRequest request, HttpServletResponse response, User user) {
|
|
}
|
|
----
|
|
|
|
LoginInterceptor를 구현한 클래스는 LoginInterceptorExecutor에 의해 실행되며 Spring Bean으로 등록하고 SpringWebCofig에 아래와 같이 LoginInterceptorExecutor에 등록한다.
|
|
|
|
[source, java]
|
|
----
|
|
@Bean
|
|
public LoginInterceptorExecutor loginInterceptorExecutor() {
|
|
List<LoginInterceptor> loginInterceptorList = new ArrayList<>();
|
|
loginInterceptorList.add(userUpdateInterceptor());
|
|
loginInterceptorList.add(loginOutLogInterceptor());
|
|
return new LoginInterceptorExecutor(loginInterceptorList);
|
|
}
|
|
----
|
|
|
|
아래는 로그인 후 사용자 정보를 update 하는 UserUpdateInterceptor의 일부분이다.
|
|
|
|
[source, java]
|
|
----
|
|
@Override
|
|
public void postLogin(HttpServletRequest request, HttpServletResponse response, User user) {
|
|
|
|
String recentLoginIp = user.getRecentLoginIp();
|
|
Date recentLoginDatetime = user.getRecentLoginDatetime();
|
|
user.setLastLoginDate(recentLoginDatetime);
|
|
user.setLastLoginIp(recentLoginIp);
|
|
user.setRecentLoginDatetime(new Date());
|
|
user.setRecentLoginIp(webUtil.getClientIp(request));
|
|
user.setLastActivityTime(System.currentTimeMillis());
|
|
userService.updateUser(user);
|
|
}
|
|
----
|