diff --git a/src/main/front/index.html b/src/main/front/index.html
index 8388c4b..e61b9b3 100644
--- a/src/main/front/index.html
+++ b/src/main/front/index.html
@@ -1,10 +1,13 @@
-
+
+
+
- Vite + Vue
+
+ Dfx console
diff --git a/src/main/front/index.html.bak b/src/main/front/index.html.bak
new file mode 100644
index 0000000..8388c4b
--- /dev/null
+++ b/src/main/front/index.html.bak
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite + Vue
+
+
+
+
+
+
diff --git a/src/main/front/src/App.vue b/src/main/front/src/App.vue
index 81c28ec..93feecd 100644
--- a/src/main/front/src/App.vue
+++ b/src/main/front/src/App.vue
@@ -1,30 +1,21 @@
-
-
+
+
diff --git a/src/main/front/src/App.vue.bak b/src/main/front/src/App.vue.bak
new file mode 100644
index 0000000..81c28ec
--- /dev/null
+++ b/src/main/front/src/App.vue.bak
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/front/src/assets/css/login.css b/src/main/front/src/assets/css/login.css
new file mode 100644
index 0000000..4732d1f
--- /dev/null
+++ b/src/main/front/src/assets/css/login.css
@@ -0,0 +1,39 @@
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: flex;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 40px;
+ background-color: #f5f5f5;
+}
+
+.form-signin {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+ margin: auto;
+}
+
+.form-signin .checkbox {
+ font-weight: 400;
+}
+
+.form-signin .form-floating:focus-within {
+ z-index: 2;
+}
+
+.form-signin input[type="email"] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
diff --git a/src/main/front/src/components/ConsoleMain.vue b/src/main/front/src/components/ConsoleMain.vue
new file mode 100644
index 0000000..546ebbc
--- /dev/null
+++ b/src/main/front/src/components/ConsoleMain.vue
@@ -0,0 +1,43 @@
+
+
+
+ {{ msg }}
+
+
+
+
+ Edit
+ components/HelloWorld.vue to test HMR
+
+
+
+
+ Check out
+ create-vue, the official Vue + Vite starter
+
+
+ Learn more about IDE Support for Vue in the
+ Vue Docs Scaling up Guide.
+
+ Click on the Vite and Vue logos to learn more
+
+
+
diff --git a/src/main/front/src/components/LoginView.vue b/src/main/front/src/components/LoginView.vue
new file mode 100644
index 0000000..121206a
--- /dev/null
+++ b/src/main/front/src/components/LoginView.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/JsonUsernamePasswordAuthenticationFilter.java b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/JsonUsernamePasswordAuthenticationFilter.java
new file mode 100644
index 0000000..662d538
--- /dev/null
+++ b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/JsonUsernamePasswordAuthenticationFilter.java
@@ -0,0 +1,34 @@
+package com.bsmlab.dfx.dfxconsole.framework.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+ if(!request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
+ throw new IllegalArgumentException("not application/json");
+ }
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map userInfoMap = objectMapper.readValue(request.getInputStream(), Map.class);
+ UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userInfoMap.get("username"), userInfoMap.get("password"));
+ this.setDetails(request, usernamePasswordAuthenticationToken);
+ return this.getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
+ }
+ catch(IOException e) {
+ throw new AuthenticationServiceException("사용자 정보를 확인할 수 없습니다.");
+ }
+ }
+}
diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/SecurityConfig.java b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/SecurityConfig.java
index d78292a..218c797 100644
--- a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/SecurityConfig.java
+++ b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/config/SecurityConfig.java
@@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
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.configurers.AbstractHttpConfigurer;
@@ -11,6 +12,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Slf4j
@Configuration
@@ -18,8 +20,8 @@ import org.springframework.security.web.SecurityFilterChain;
@RequiredArgsConstructor
public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
+ //@Bean
+ public SecurityFilterChain securityFilterChainFormLogin(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(AbstractHttpConfigurer::disable) // REST API 를 제공하는 서비스이므로 Cross Site Request Forgery disable
.authorizeHttpRequests(authorize -> authorize
@@ -44,6 +46,20 @@ public class SecurityConfig {
return httpSecurity.build();
}
+ @Bean
+ public SecurityFilterChain securityFilterChainAjax(HttpSecurity httpSecurity) throws Exception {
+ JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter = new JsonUsernamePasswordAuthenticationFilter();
+ jsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(httpSecurity.getSharedObject(AuthenticationManager.class));
+ jsonUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/public-api/loginProcess");
+ httpSecurity
+ .csrf(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(authorize -> authorize.requestMatchers("/app-api/**").hasRole("USER").anyRequest().permitAll())
+ .addFilterAt(jsonUsernamePasswordAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
+ .sessionManagement(session -> session.maximumSessions(1).maxSessionsPreventsLogin(false))
+ ;
+ return httpSecurity.build();
+ }
+
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();