Spring Security, 로그인 작업중

main
semin.baek 6 months ago
parent d2d2b97423
commit 05b9dc84db

@ -37,6 +37,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4'
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.17.0'
runtimeOnly 'com.ibm.db2:jcc' runtimeOnly 'com.ibm.db2:jcc'
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc' runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
runtimeOnly 'com.mysql:mysql-connector-j' runtimeOnly 'com.mysql:mysql-connector-j'

@ -1,19 +1,54 @@
CREATE SCHEMA DFXCONSOLE; CREATE SCHEMA DFXCONSOLE;
CREATE TABLE TB_CONSOLE_USER ( CREATE TABLE TB_CODE (
CODE_GROUP VARCHAR(64) NOT NULL
, CODE_NAME VARCHAR(64) NOT NULL
, CODE_VALUE VARCHAR(1024) NOT NULL
, CODE_DESCRIPTION VARCHAR(1024)
, ORDER_NO DECIMAL(5) NOT NULL
, PARENT_CODE_NAME VARCHAR(64)
, USE_YN VARCHAR(1) NOT NULL
, CONSTRAINT PK_CODE PRIMARY KEY (CODE_GROUP, CODE_NAME)
);
CREATE TABLE TB_DFX_USER (
USER_UUID VARCHAR(64) NOT NULL USER_UUID VARCHAR(64) NOT NULL
, USER_EMAIL VARCHAR(320) NOT NULL , USER_EMAIL VARCHAR(320) NOT NULL
, USER_PASSWORD VARCHAR(512) , USER_PASSWORD VARCHAR(512)
, USER_NICK VARCHAR(512) NOT NULL , USER_NICK VARCHAR(512) NOT NULL
, USER_REGISTER_TIME VARCHAR(64) , USER_REGISTER_TIME VARCHAR(64)
, LAST_LOGIN_TIME VARCHAR(64) , LAST_LOGIN_TIME VARCHAR(64)
, CONSTRAINT PK_CONSOLE_USER PRIMARY KEY (USER_UUID) , CONSTRAINT PK_DFX_USER PRIMARY KEY (USER_UUID)
); );
COMMENT ON TABLE TB_CONSOLE_USER IS 'CMS 사용자'; COMMENT ON TABLE TB_DFX_USER IS 'DFXCONSOLE 사용자';
COMMENT ON COLUMN TB_CONSOLE_USER.USER_UUID IS '사용자 UUID - 자동생성됨'; COMMENT ON COLUMN TB_DFX_USER.USER_UUID IS '사용자 UUID - 자동생성됨';
COMMENT ON COLUMN TB_CONSOLE_USER.USER_EMAIL IS '사용자 email - 로그인 아이디로 사용됨'; COMMENT ON COLUMN TB_DFX_USER.USER_EMAIL IS '사용자 email - 로그인 아이디로 사용됨';
COMMENT ON COLUMN TB_CONSOLE_USER.USER_PASSWORD IS '사용자 비밀번호'; COMMENT ON COLUMN TB_DFX_USER.USER_PASSWORD IS '사용자 비밀번호';
COMMENT ON COLUMN TB_CONSOLE_USER.USER_NICK IS '닉네임'; COMMENT ON COLUMN TB_DFX_USER.USER_NICK IS '닉네임';
COMMENT ON COLUMN TB_CONSOLE_USER.USER_REGISTER_TIME IS '가입일시'; COMMENT ON COLUMN TB_DFX_USER.USER_REGISTER_TIME IS '가입일시';
COMMENT ON COLUMN TB_CONSOLE_USER.LAST_LOGIN_TIME IS '마지막 로그인 일시'; COMMENT ON COLUMN TB_DFX_USER.LAST_LOGIN_TIME IS '마지막 로그인 일시';
CREATE UNIQUE INDEX IX_CONSOLE_USER_01 ON TB_CONSOLE_USER(USER_EMAIL); CREATE UNIQUE INDEX IX_DFX_USER_01 ON TB_DFX_USER(USER_EMAIL);
CREATE TABLE TB_DFX_AGENT_CONFIG (
AGENT_ID VARCHAR(256) NOT NULL
, HOST_NAME VARCHAR(256)
, LISTEN_PORT DECIMAL(5) NOT NULL
, DESCRIPTION VARCHAR(2048)
, POSTMAN_COUNT DECIMAL(9)
, DROPBOX_COUNT DECIMAL(9)
, STATUS_CODE VARCHAR(64) NOT NULL
, LAST_STATUS_TS TIMESTAMP NOT NULL
, SETTINGS_DATA CLOB
, CONSTRAINT PK_DFX_AGENT_CONFIG PRIMARY KEY (AGENT_ID)
);
COMMENT ON TABLE TB_DFX_AGENT_CONFIG IS '에이전트 설정';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.AGENT_ID IS '에이전트ID';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.HOST_NAME IS '에이전트가 설치된 HOSTNAME 또는 IP';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.LISTEN_PORT IS '에이전트 수신 포트';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.DESCRIPTION IS '에이전트 설명';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.POSTMAN_COUNT IS 'POSTMAN 갯수';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.DROPBOX_COUNT IS 'DROPBOX 갯수';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.STATUS_CODE IS '상태코드 CODE_GROUP: AGENT_STATUS_CODE';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.LAST_STATUS_TS IS '최종상태확인 시간';
COMMENT ON COLUMN TB_DFX_AGENT_CONFIG.SETTINGS_DATA IS '에이전스 설정 파일 내용';

@ -1 +1,14 @@
INSERT INTO TB_CONSOLE_USER (USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK) VALUES ('dad9f3b6-45bf-49f9-85c5-1a83810d921c', 'smbaek@bsm-lab.com', 'qortpals1!', '백세민'); INSERT INTO TB_CODE (
CODE_GROUP, CODE_NAME, CODE_VALUE, CODE_DESCRIPTION, ORDER_NO, PARENT_CODE_NAME, USE_YN
)
VALUES (
'AGENT_STATUS_CODE', 'STATUS_OK', '정상', '정상', 1, NULL, 'Y'
);
INSERT INTO TB_CODE (
CODE_GROUP, CODE_NAME, CODE_VALUE, CODE_DESCRIPTION, ORDER_NO, PARENT_CODE_NAME, USE_YN
)
VALUES (
'AGENT_STATUS_CODE', 'STATUS_UNKNOWN', '확인불가', '확인불가', 2, NULL, 'Y'
);
INSERT INTO TB_DFX_USER (USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK) VALUES ('dad9f3b6-45bf-49f9-85c5-1a83810d921c', 'smbaek@bsm-lab.com', 'qortpals1!', '백세민');

@ -15,7 +15,7 @@ const router = createRouter({
meta: { isRequiredAuth: false }, meta: { isRequiredAuth: false },
}, },
{ {
path: '/about', path: '/about.html',
name: 'about', name: 'about',
// route level code-splitting // route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route // this generates a separate chunk (About.[hash].js) for this route
@ -24,19 +24,19 @@ const router = createRouter({
meta: { isRequiredAuth: false }, meta: { isRequiredAuth: false },
}, },
{ {
path: '/login', path: '/login.html',
name: 'login', name: 'login',
component: LoginView, component: LoginView,
meta: { isRequiredAuth: false }, meta: { isRequiredAuth: false },
}, },
{ {
path: '/main', path: '/main.html',
name: 'main', name: 'main',
component: MainView, component: MainView,
meta: { isRequiredAuth: true }, meta: { isRequiredAuth: true },
}, },
{ {
path: '/main-example', path: '/main-example.html',
name: 'main-example', name: 'main-example',
component: MainViewExample, component: MainViewExample,
meta: { isRequiredAuth: false }, meta: { isRequiredAuth: false },
@ -47,9 +47,9 @@ console.log('test')
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
if (to.meta.isRequiredAuth && !userInfo.value.isLogin) { if (to.meta.isRequiredAuth && !userInfo.value.isLogin) {
next('/login') next('/login.html')
} else if ((to.path === '/login' || to.path === '/') && userInfo.value.isLogin) { } else if ((to.path === '/login.html' || to.path === '/') && userInfo.value.isLogin) {
next('/main') next('/main.html')
} else { } else {
next() next()
} }

@ -1,16 +1,30 @@
<script setup> <script setup>
import '@/assets/main.css' import '@/assets/main.css'
import { Bar } from 'vue-chartjs' import { Bar, Line } from 'vue-chartjs'
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js' import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, PointElement, LineElement, CategoryScale, LinearScale } from 'chart.js'
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale) ChartJS.register(Title, Tooltip, Legend, BarElement, PointElement, LineElement, CategoryScale, LinearScale)
const shortThroughputCahrtData = { const shortThroughputCahrtData = {
labels: ['agent-mkami', 'agent-ami'], labels: ['agent-mkami', 'agent-ami'],
datasets: [{ label: '시간당 처리량', data: [4568, 3232] }], datasets: [{ label: '당 처리량', data: [4568, 3232] }],
} }
const shortThroughputChartOptions = { const shortThroughputChartOptions = {
responsive: true, responsive: true,
indexAxis: 'y', indexAxis: 'y',
} }
const daysThroughputChartData = {
labels: ['2025-09-01', '2025-09-02', '2025-09-03', '2025-09-04', '2025-09-05', '2025-09-06', '2025-09-07'],
datasets: [
{
label: '주간 처리량',
backgroundColor: '#f87979',
data: [40, 39, 10, 40, 39, 80, 40],
},
],
}
const daysThroughputChartOptions = {
responsive: true,
maintainAspectRatio: false,
}
</script> </script>
<template> <template>
@ -48,8 +62,8 @@ const shortThroughputChartOptions = {
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">agent-mkami <i class="bi bi-lightbulb-fill text-success"></i></h5> <h5 class="card-title">agent-mkami <i class="bi bi-lightbulb-fill text-success"></i></h5>
<p class="card-text">상태 : 아이콘</p> <p class="card-text">Host : mkami-oracle</p>
<p class="card-text">처리량 : 실시간 처리량 숫자</p> <p class="card-text">처리량 : 789</p>
</div> </div>
</div> </div>
</div> </div>
@ -57,8 +71,8 @@ const shortThroughputChartOptions = {
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">agent-ami <i class="bi bi-lightbulb-fill text-success"></i></h5> <h5 class="card-title">agent-ami <i class="bi bi-lightbulb-fill text-success"></i></h5>
<p class="card-text">상태 : 아이콘</p> <p class="card-text">상태 : ami-cubrid</p>
<p class="card-text">처리량 : 실시간 처리량 숫자</p> <p class="card-text">처리량 : 345</p>
</div> </div>
</div> </div>
</div> </div>
@ -78,9 +92,7 @@ const shortThroughputChartOptions = {
<div class="col-6"> <div class="col-6">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">주간 처리량</h5> <Line :option="daysThroughputChartOptions" :data="daysThroughputChartData"></Line>
<p class="card-text">그래프</p>
<p class="card-text">에이전트 throughtput 모니터링 요약</p>
</div> </div>
</div> </div>
</div> </div>

@ -1,13 +0,0 @@
package com.bsmlab.dfx.dfxconsole.app.user;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ConsoleUserMapper {
ConsoleUserDto selectConsoleUserByUserUuid(ConsoleUserDto consoleUserDto);
ConsoleUserDto selectConsoleUserByUserEmail(ConsoleUserDto consoleUserDto);
List<ConsoleUserDto> selectConsoleUserSearchListForPage(UserSearchParameterDto userSearchParameterDto);
void insertConsoleUser(ConsoleUserDto consoleUserDto);
}

@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder @Builder
public class ConsoleUserDto { public class DfxUserDto {
private String userUuid; private String userUuid;
private String userEmail; private String userEmail;
private String userPassword; private String userPassword;

@ -0,0 +1,13 @@
package com.bsmlab.dfx.dfxconsole.app.user;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DfxUserMapper {
DfxUserDto selectConsoleUserByUserUuid(DfxUserDto dfxUserDto);
DfxUserDto selectConsoleUserByUserEmail(DfxUserDto dfxUserDto);
List<DfxUserDto> selectConsoleUserSearchListForPage(UserSearchParameterDto userSearchParameterDto);
void insertConsoleUser(DfxUserDto dfxUserDto);
}

@ -1,6 +1,6 @@
package com.bsmlab.dfx.dfxconsole.framework.config; package com.bsmlab.dfx.dfxconsole.framework.config;
import com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto; import com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto;
import io.micrometer.common.util.StringUtils; import io.micrometer.common.util.StringUtils;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
@ -11,11 +11,11 @@ import java.util.Collection;
import java.util.List; import java.util.List;
public class AppUserDetails implements UserDetails { public class AppUserDetails implements UserDetails {
private final ConsoleUserDto consoleUserDto; private final DfxUserDto dfxUserDto;
private final Collection<? extends GrantedAuthority> authorities; private final Collection<? extends GrantedAuthority> authorities;
public AppUserDetails(ConsoleUserDto consoleUserDto) { public AppUserDetails(DfxUserDto dfxUserDto) {
this.consoleUserDto = consoleUserDto; this.dfxUserDto = dfxUserDto;
this.authorities = List.of(new SimpleGrantedAuthority("USER")); this.authorities = List.of(new SimpleGrantedAuthority("USER"));
} }
@ -27,32 +27,32 @@ public class AppUserDetails implements UserDetails {
@Override @Override
public String getPassword() { public String getPassword() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder.encode(this.consoleUserDto.getUserPassword()); return bCryptPasswordEncoder.encode(this.dfxUserDto.getUserPassword());
//return this.consoleUserDto.getUserPassword(); //return this.consoleUserDto.getUserPassword();
} }
@Override @Override
public String getUsername() { public String getUsername() {
return this.consoleUserDto.getUserEmail(); return this.dfxUserDto.getUserEmail();
} }
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return this.consoleUserDto != null && StringUtils.isNotBlank(this.consoleUserDto.getUserUuid()); return this.dfxUserDto != null && StringUtils.isNotBlank(this.dfxUserDto.getUserUuid());
} }
@Override @Override
public boolean isAccountNonLocked() { public boolean isAccountNonLocked() {
return this.consoleUserDto != null && StringUtils.isNotBlank(this.consoleUserDto.getUserUuid()); return this.dfxUserDto != null && StringUtils.isNotBlank(this.dfxUserDto.getUserUuid());
} }
@Override @Override
public boolean isCredentialsNonExpired() { public boolean isCredentialsNonExpired() {
return this.consoleUserDto != null && StringUtils.isNotBlank(this.consoleUserDto.getUserUuid()); return this.dfxUserDto != null && StringUtils.isNotBlank(this.dfxUserDto.getUserUuid());
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return this.consoleUserDto != null && StringUtils.isNotBlank(this.consoleUserDto.getUserUuid()); return this.dfxUserDto != null && StringUtils.isNotBlank(this.dfxUserDto.getUserUuid());
} }
} }

@ -1,7 +1,7 @@
package com.bsmlab.dfx.dfxconsole.framework.config; package com.bsmlab.dfx.dfxconsole.framework.config;
import com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto; import com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto;
import com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserMapper; import com.bsmlab.dfx.dfxconsole.app.user.DfxUserMapper;
import io.micrometer.common.util.StringUtils; import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -14,13 +14,13 @@ import org.springframework.stereotype.Service;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class AppUserDetailsService implements UserDetailsService { public class AppUserDetailsService implements UserDetailsService {
private final ConsoleUserMapper consoleUserMapper; private final DfxUserMapper dfxUserMapper;
@Override @Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException { public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
ConsoleUserDto consoleUserDto = consoleUserMapper.selectConsoleUserByUserEmail(ConsoleUserDto.builder().userEmail(userEmail).build()); DfxUserDto dfxUserDto = dfxUserMapper.selectConsoleUserByUserEmail(DfxUserDto.builder().userEmail(userEmail).build());
if(consoleUserDto == null || StringUtils.isBlank(consoleUserDto.getUserUuid())) { if(dfxUserDto == null || StringUtils.isBlank(dfxUserDto.getUserUuid())) {
throw new UsernameNotFoundException("사용자 정보를 찾을 수 없습니다."); throw new UsernameNotFoundException("사용자 정보를 찾을 수 없습니다.");
} }
return new AppUserDetails(consoleUserDto); return new AppUserDetails(dfxUserDto);
} }
} }

@ -1,8 +1,11 @@
package com.bsmlab.dfx.dfxconsole.framework.config; package com.bsmlab.dfx.dfxconsole.framework.config;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -11,9 +14,13 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Slf4j
@RequiredArgsConstructor
public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
@ -21,7 +28,6 @@ public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAu
throw new IllegalArgumentException("not application/json"); throw new IllegalArgumentException("not application/json");
} }
try { try {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> userInfoMap = objectMapper.readValue(request.getInputStream(), Map.class); Map<String, String> userInfoMap = objectMapper.readValue(request.getInputStream(), Map.class);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userInfoMap.get("username"), userInfoMap.get("password")); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userInfoMap.get("username"), userInfoMap.get("password"));
this.setDetails(request, usernamePasswordAuthenticationToken); this.setDetails(request, usernamePasswordAuthenticationToken);
@ -31,4 +37,28 @@ public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAu
throw new AuthenticationServiceException("사용자 정보를 확인할 수 없습니다."); throw new AuthenticationServiceException("사용자 정보를 확인할 수 없습니다.");
} }
} }
// 로그인 성공 후 결과 메시지 전달. 자동 호출됨.
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException {
log.info("로그인 성공: {}", authResult.getName());
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("application/json");
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("username", authResult.getName());
objectMapper.writeValue(response.getWriter(), result);
}
// 로그인 실패 후 결과 메시지 전달. 자동 호출됨.
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException {
log.warn("로그인 실패: {}", failed.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("message", "인증에 실패했습니다.");
objectMapper.writeValue(response.getWriter(), result);
}
} }

@ -0,0 +1,32 @@
package com.bsmlab.dfx.dfxconsole.framework.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RequiredArgsConstructor
@Controller
public class PasswordGeneratorController {
private final PasswordEncoder passwordEncoder;
@RequestMapping(value = "/public-api/passwordGenerator")
public Map<String, String> passwordGenerator(Map<String, String> paramMap) {
Map<String, String> resultMap = new HashMap<>();
String plainPassword = paramMap.get("password");
if(StringUtils.isNotEmpty(plainPassword)) {
String encryptedPassword = passwordEncoder.encode(plainPassword);
resultMap.put("password", encryptedPassword);
}
else {
resultMap.put("password", "");
}
return resultMap;
}
}

@ -5,10 +5,13 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
@ -31,12 +34,12 @@ public class SecurityConfig {
) )
.formLogin(form -> form .formLogin(form -> form
.loginPage("/login.html") .loginPage("/login.html")
.loginProcessingUrl("/loginProcess") .loginProcessingUrl("/public-api/loginProcess")
.defaultSuccessUrl("/index.html") .defaultSuccessUrl("/index.html")
.permitAll() .permitAll()
) )
.logout(logout -> logout .logout(logout -> logout
.logoutUrl("/logout") .logoutUrl("/public-api/logoutProcess")
.logoutSuccessUrl("/login.html") .logoutSuccessUrl("/login.html")
.invalidateHttpSession(true) .invalidateHttpSession(true)
.deleteCookies("JSESSIONID") .deleteCookies("JSESSIONID")
@ -46,15 +49,31 @@ public class SecurityConfig {
return httpSecurity.build(); return httpSecurity.build();
} }
/*
1. POST /public-api/loginProcess (username, password)
2. JsonUsernamePasswordAuthenticationFilter attemptAuthentication()
3. UsernamePasswordAuthenticationToken
4. AuthenticationManager DaoAuthenticationProvider
5. DaoAuthenticationProvider
AppUserDetailsService.loadUserByUsername()
UserDetails.getPassword()
password : passwordEncoder.matches(raw, encoded)
or
*/
@Bean @Bean
public SecurityFilterChain securityFilterChainAjax(HttpSecurity httpSecurity) throws Exception { public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter(); JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter();
jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(httpSecurity.getSharedObject(AuthenticationManager.class)); jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);
jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/public-api/loginProcess"); jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/public-api/loginProcess");
return jsonUsernamePasswordAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChainAjax(HttpSecurity httpSecurity, AuthenticationConfiguration authenticationConfiguration) throws Exception {
httpSecurity httpSecurity
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize.requestMatchers("/app-api/**").hasRole("USER").anyRequest().permitAll()) .authorizeHttpRequests(authorize -> authorize.requestMatchers("/app-api/**").authenticated().anyRequest().permitAll())
.addFilterAt(jsonUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .addFilterAt(this.jsonUsernamePasswordAuthenticationFilter(this.authenticationManager(authenticationConfiguration)), UsernamePasswordAuthenticationFilter.class)
.sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false)) .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false))
; ;
return httpSecurity.build(); return httpSecurity.build();
@ -70,4 +89,17 @@ public class SecurityConfig {
return new GrantedAuthorityDefaults(""); // "ROLE_" prefix 를 제거한다. return new GrantedAuthorityDefaults(""); // "ROLE_" prefix 를 제거한다.
} }
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
} }

@ -0,0 +1,25 @@
package com.bsmlab.dfx.dfxconsole.framework.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.HashMap;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
public class SearchParameterDto {
@Builder.Default
private long page = 1;
@Builder.Default
private int itemCountPerPage = 20;
@Builder.Default
private long totalItemCount = 0;
@Builder.Default
private Map<String, String> parameters = new HashMap<>();
}

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserMapper"> <mapper namespace="com.bsmlab.dfx.dfxconsole.app.user.DfxUserMapper">
<select id="selectConsoleUserByUserUuid" parameterType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto"> <select id="selectConsoleUserByUserUuid" parameterType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto">
SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME
FROM TB_CONSOLE_USER FROM TB_DFX_USER
WHERE USER_UUID = #{userUuid} WHERE USER_UUID = #{userUuid}
</select> </select>
<select id="selectConsoleUserByUserEmail" parameterType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto"> <select id="selectConsoleUserByUserEmail" parameterType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto">
SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME
FROM TB_CONSOLE_USER FROM TB_DFX_USER
WHERE USER_EMAIL = #{userEmail} WHERE USER_EMAIL = #{userEmail}
</select> </select>
<select id="selectConsoleUserSearchListForPage" parameterType="com.bsmlab.dfx.dfxconsole.app.user.UserSearchParameterDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto"> <select id="selectConsoleUserSearchListForPage" parameterType="com.bsmlab.dfx.dfxconsole.app.user.UserSearchParameterDto" resultType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto">
SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME, COUNT(USER_UUID) OVER () AS TOTAL_ITEM_COUNT SELECT USER_UUID, USER_EMAIL, USER_PASSWORD, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME, COUNT(USER_UUID) OVER () AS TOTAL_ITEM_COUNT
FROM TB_CONSOLE_USER FROM TB_DFX_USER
WHERE 1 = 1 WHERE 1 = 1
AND USER_EMAIL LIKE '%'||#{userEmail}||'%' AND USER_EMAIL LIKE '%'||#{userEmail}||'%'
AND USER_NICK LIKE '%'||#{userNick}||'%' AND USER_NICK LIKE '%'||#{userNick}||'%'
@ -24,8 +24,8 @@
LIMIT #{itemCountPerPage} * (#{page}) OFFSET #{itemCountPerPage} * (#{page} - 1) LIMIT #{itemCountPerPage} * (#{page}) OFFSET #{itemCountPerPage} * (#{page} - 1)
</select> </select>
<insert id="insertConsoleUser" parameterType="com.bsmlab.dfx.dfxconsole.app.user.ConsoleUserDto"> <insert id="insertConsoleUser" parameterType="com.bsmlab.dfx.dfxconsole.app.user.DfxUserDto">
INSERT INTO TB_CONSOLE_USER ( INSERT INTO TB_DFX_USER (
USER_UUID, USER_EMAIL, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME USER_UUID, USER_EMAIL, USER_NICK, USER_REGISTER_TIME, LAST_LOGIN_TIME
) )
VALUES ( VALUES (

Loading…
Cancel
Save