Spring Security, 로그인 작업중

main
semin.baek 6 months ago
parent 05b9dc84db
commit 2fc4fa18cf

@ -7,7 +7,7 @@
<title>Vite App</title>
</head>
<body>
<div id="app" class="container d-flex"></div>
<div id="app" class="container d-flex min-vh-100 flex-column"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -9,6 +9,7 @@
"version": "0.0.0",
"dependencies": {
"@popperjs/core": "^2.11.8",
"axios": "^1.11.0",
"bootstrap": "^5.3.7",
"bootstrap-icons": "^1.13.1",
"chart.js": "^4.5.0",

@ -11,6 +11,7 @@
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"axios": "^1.11.0",
"bootstrap": "^5.3.7",
"bootstrap-icons": "^1.13.1",
"chart.js": "^4.5.0",

@ -19,8 +19,8 @@ import Login from './views/LoginView.vue'
</div>
</header>
-->
<RouterView />
<footer class="w-100 d-flex">
<RouterView class="flex-fill" />
<footer class="w-100 border-top d-flex">
<div class="bg-info-subtle p-2 d-flex align-items-center w-100 bg-purple">바닥글(저작권, 링크 ) 영역</div>
</footer>
</template>

@ -38,4 +38,5 @@ a,
footer {
min-height: 2rem;
max-height: 2rem;
}

@ -1,8 +1,48 @@
import { ref } from 'vue'
import { reactive } from 'vue'
import axios from 'axios'
const userInfo = ref({
isLogin: true,
const userInfo = reactive({
isLogin: false,
userId: '',
})
export const userApi = {
loginProcess: async function (inputUsername, inputUserPassword) {
const data = { username: inputUsername, password: inputUserPassword }
axios.post('/public-api/loginProcess', data).then((response) => {
if (response.status == 200 && response.data && response.data.success == true) {
userInfo.isLogin = true
userInfo.userId = response.data.username
return true
} else {
return false
}
})
},
logoutProcess: async function () {
axios.post('/public-api/logoutProcess').then((response) => {
if (response.status == 200 && response.data && response.data.success == true) {
userInfo.isLogin = false
userInfo.userId = ''
return true
} else {
return false
}
})
},
loginCheck: async function () {
axios.get('/public-api/sessionCheck').then((response) => {
if (response.status == 200 && response.data && response.data.success == true) {
userInfo.isLogin = true
userInfo.userId = response.data.username
return true
} else {
userInfo.isLogin = false
userInfo.userId = ''
return false
}
})
},
}
window.userApi = userApi
export default userInfo

@ -46,9 +46,9 @@ const router = createRouter({
console.log('test')
router.beforeEach((to, from, next) => {
if (to.meta.isRequiredAuth && !userInfo.value.isLogin) {
if (to.meta.isRequiredAuth && !userInfo.isLogin) {
next('/login.html')
} else if ((to.path === '/login.html' || to.path === '/') && userInfo.value.isLogin) {
} else if ((to.path === '/login.html' || to.path === '/') && userInfo.isLogin) {
next('/main.html')
} else {
next()

@ -1,52 +1,61 @@
<script setup></script>
<script setup>
import { reactive } from 'vue'
import { userApi } from '@/components/userInfo'
const loginForm = reactive({
username: '',
password: '',
})
async function loginProcess() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(loginForm.username)) {
alert('사용자 아이디를 이메일 형식으로 입력하여 주십시오.')
return
}
if (!loginForm.password) {
alert('비밀번호를 입력하여 주십시오.')
return
}
let loginResult = await userApi.loginProcess(loginForm.username, loginForm.password)
if (!loginResult) {
alert('사용자 아이디 또는 비밀번호가 일치하지 않습니다.')
document.getElementById('floatingPassword').focus()
return
}
}
</script>
<template>
<main class="form-signin">
<form>
<main class="form-signin container flex-fill">
<form @submit.prevent="loginProcess">
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input
type="email"
class="form-control"
id="floatingInput"
placeholder="name@example.com"
/>
<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com" :value="loginForm.username" />
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" placeholder="Password" />
<input type="password" class="form-control" id="floatingPassword" placeholder="Password" :value="loginForm.password" @keydown.enter="loginProcess" />
<label for="floatingPassword">Password</label>
</div>
<div class="checkbox mb-3">
<label> <input type="checkbox" value="remember-me" /> Remember me </label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
<button class="w-100 btn btn-lg btn-primary" type="submit" @submit="loginProcess">Sign in</button>
<p class="mt-5 mb-3 text-muted">&copy; 20172021</p>
</form>
</main>
</template>
<style scoped>
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;
margin-top: 20%;
}
.form-signin .checkbox {
@ -76,10 +85,4 @@ body {
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>

@ -0,0 +1,20 @@
package com.bsmlab.dfx.dfxconsole.framework.config;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class ConsoleLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("{\"success\": true}");
}
}

@ -1,32 +0,0 @@
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;
}
}

@ -0,0 +1,60 @@
package com.bsmlab.dfx.dfxconsole.framework.config;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
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 PublicApiController {
private final PasswordEncoder passwordEncoder;
/**
* API
* @param paramMap {"password": "평문 문자열"}
* @return {"password": "암호화 문자열"}
*/
@RequestMapping(value = "/public-api/passwordGenerator")
public ResponseEntity<?> 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 ResponseEntity.ok(resultMap);
}
/**
* API
* @param userDetails DI
* @return {"success": true/false, "username": }
*/
@RequestMapping(value = "/public-api/sessionCheck")
public ResponseEntity<?> sessionCheck(@AuthenticationPrincipal UserDetails userDetails) {
Map<String, Object> resultMap = new HashMap<>();
if(userDetails != null) {
resultMap.put("success", true);
resultMap.put("username", userDetails.getUsername());
}
else {
resultMap.put("success", false);
resultMap.put("username", "");
}
return ResponseEntity.ok(resultMap);
}
}

@ -16,6 +16,7 @@ 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;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
@Slf4j
@Configuration
@ -40,7 +41,7 @@ public class SecurityConfig {
)
.logout(logout -> logout
.logoutUrl("/public-api/logoutProcess")
.logoutSuccessUrl("/login.html")
.logoutSuccessHandler(this.logoutSuccessHandler())
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
)
@ -79,6 +80,11 @@ public class SecurityConfig {
return httpSecurity.build();
}
@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new ConsoleLogoutSuccessHandler();
}
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();

@ -37,7 +37,7 @@ server:
include-exception: true
include-message: always
include-stacktrace: always
port: 9443
port: 443
ssl:
enabled: true
certificate: ${user.home}/.vite-plugin-mkcert/bsm-lab.dev.pem

Loading…
Cancel
Save