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 @@ + + + + + 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();