'스프링 시큐리티' 프레임워크는 스프링 서버에 필요한 인증 및 인가를 위해 많은 기능을 제공해 줌으로써 개발의 수고를 덜어 줍니다. 마치 '스프링' 프레임워크가 웹 서버 구현에 편의를 제공해 주는 것처럼요!
'스프링 시큐리티' 프레임워크 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
'스프링 시큐리티' 활성화
우리 사이트를 접속하는 모든 사람들에게 로그인과 로그아웃 기능만 사용할 수 있도록 만드는 코드입니다.
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 스프링 시큐리티를 사용하게 되면 누구나 어디든 접속 못하게 할 수 있다.
// 우리는 그 중에서 로그인 기능과 로그아웃 기능은 누구나 할 수 있도록 열어 놓아보겠다.
http.authorizeRequests()
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
.and()
// 스프링 시큐리티가 만들어 놓은 로그인 기능 허용
.formLogin()
// 로그인이 성공하게 되면 이동 시킬 위치 (우리는 루트로 지정을 할 것이다.)
.defaultSuccessUrl("/")
.permitAll()
.and()
// 로그아웃 기능 허용
.logout()
.permitAll();
}
}
프레임 워크를 사용한다 -> 프레임 워크의 룰을 따른다.
@Configuration : 스프링이 처음에 기동할 때 설정을 가져와서 서버가 실행될 때 적용하겠다
@EnableWebSecurity: 스프링 시큐리티가 가능하게 해주는 어노테이션
@WebSecurityConfigurerAdapter : 상속받아서 클래스를 만들어줘야지 사용할 수 있음
서버를 구동하고 나서 localhost:8080으로 접근하면 자동으로 login 페이지가 나타납니다.
오잉? 로그인 페이지 만든적이 없는데 어떻게 된거지?
라고 생각하실 수 있지만
이것은 스프링에서 제공하는 로그인 페이지입니다.
아이디는 user
비밀번호는 스프링이 실행될 때 마다 로그로 확인하실 수 있습니다.
자 그럼 저는 아래의 아이디와 패스워드로 접속해 보도록 하겠습니다.
user
bd88cf16-4714-43ed-a757-b9f368c1f43f
아이디와 패스워드를 입력하면 제가 만들어 놓은 웹 페이지로 이동하는 것을 확인할 수 있습니다.
스프링 시큐리티를 적용하면 자동으로 로그아웃 페이지도 제공이 됩니다.
아래 url로 접근을 하면 확인하실 수 있습니다.
http://localhost:8080/logout
자 이제 우리가 만들어 놓은 login 페이지가 나오도록 적용을 해 봅시다.
api에 맞게 로그인 페이지와 로그아웃 페이지를 처리해주는 컨트롤러를 만들어 줍니다.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
// 회원 로그인 페이지
@GetMapping("/user/login")
public String login() {
return "login";
}
// 회원 가입 페이지
@GetMapping("/user/signup")
public String signup() {
return "signup";
}
}
templates/login.html과 templates/signup.html이 준비되어 있어야겠죠??
준비를 해봅시다.
login.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nanum+Gothic&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<div id="login-form">
<div id="login-title">Log into Select Shop</div>
<button id="login-kakao-btn" onclick="location.href='https://kauth.kakao.com/oauth/authorize?client_id=&redirect_uri=http://localhost:8080/user/kakao/callback&response_type=code'">
카카오로 로그인하기
</button>
<button id="login-id-btn" onclick="location.href='/user/signup'">
회원 가입하기
</button>
<form action="/user/login" method="post">
<div class="login-id-label">아이디</div>
<input type="text" name="username" class="login-input-box">
<div class="login-id-label">비밀번호</div>
<input type="password" name="password" class="login-input-box">
<button id="login-id-submit">로그인</button>
</form>
<div id="login-failed" style="display:none" class="alert alert-danger" role="alert">로그인에 실패하였습니다.</div>
</div>
</body>
<script>
const href = location.href;
const queryString = href.substring(href.indexOf("?")+1)
if (queryString === 'error') {
const errorDiv = document.getElementById('login-failed');
errorDiv.style.display = 'block';
}
</script>
</html>
signup.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Nanum+Gothic&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<meta charset="UTF-8">
<title>회원가입 페이지</title>
<script>
function onclickAdmin() {
// Get the checkbox
var checkBox = document.getElementById("admin-check");
// Get the output text
var box = document.getElementById("admin-token");
// If the checkbox is checked, display the output text
if (checkBox.checked == true){
box.style.display = "block";
} else {
box.style.display = "none";
}
}
</script>
</head>
<body>
<div id="login-form">
<div id="login-title">Sign up Select Shop</div>
<form action="/user/signup" method="post">
<div class="login-id-label">Username</div>
<input type="text" name="username" placeholder="Username" class="login-input-box">
<div class="login-id-label">Password</div>
<input type="password" name="password" class="login-input-box">
<div class="login-id-label">E-mail</div>
<input type="text" name="email" placeholder="E-mail" class="login-input-box">
<div>
<input id="admin-check" type="checkbox" name="admin" onclick="onclickAdmin()" style="margin-top: 40px;">관리자
<input id="admin-token" type="password" name="adminToken" placeholder="관리자 암호" class="login-input-box" style="display:none">
</div>
<button id="login-id-submit">회원 가입</button>
</form>
</div>
</body>
</html>
html의 화면을 꾸며줄 style.css도 준비해 봅시다.
static/css/style.css
* {
font-family: 'Nanum Gothic', sans-serif;
}
body {
margin: 0px;
}
#search-result-box {
margin-top: 15px;
}
.search-itemDto {
width: 530px;
display: flex;
flex-direction: row;
align-content: center;
justify-content: space-around;
}
.search-itemDto-left img {
width: 159px;
height: 159px;
}
.search-itemDto-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
.search-itemDto-center div {
width: 280px;
height: 23px;
font-size: 18px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1.3;
letter-spacing: -0.9px;
text-align: left;
color: #343a40;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.search-itemDto-center div.price {
height: 27px;
font-size: 27px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.54px;
text-align: left;
color: #E8344E;
}
.search-itemDto-center span.unit {
width: 17px;
height: 18px;
font-size: 18px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.9px;
text-align: center;
color: #000000;
}
.search-itemDto-right {
display: inline-block;
height: 100%;
vertical-align: middle
}
.search-itemDto-right img {
height: 25px;
width: 25px;
vertical-align: middle;
margin-top: 60px;
cursor: pointer;
}
input#query {
padding: 15px;
width: 526px;
border-radius: 2px;
background-color: #e9ecef;
border: none;
background-image: url('../images/icon-search.png');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
}
input#query::placeholder {
padding: 15px;
}
button {
color: white;
border-radius: 4px;
border-radius: none;
}
.popup-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
}
.popup-container.active {
display: flex;
}
.popup {
padding: 20px;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.3);
position: relative;
width: 370px;
height: 209px;
border-radius: 11px;
background-color: #ffffff;
}
.popup h1 {
margin: 0px;
font-size: 22px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -1.1px;
color: #000000;
}
.popup input {
width: 320px;
height: 22px;
border-radius: 2px;
border: solid 1.1px #dee2e6;
margin-right: 9px;
margin-bottom: 10px;
padding-left: 10px;
}
.popup button.close {
position: absolute;
top: 15px;
right: 15px;
color: #adb5bd;
background-color: #fff;
font-size: 19px;
border: none;
}
.popup button.cta {
width: 369.1px;
height: 43.9px;
border-radius: 2px;
background-color: #15aabf;
border: none;
}
#search-area, #see-area {
width: 530px;
margin: auto;
}
.nav {
width: 530px;
margin: 30px auto;
display: flex;
align-items: center;
justify-content: space-around;
}
.nav div {
cursor: pointer;
}
.nav div.active {
font-weight: 700;
}
.header {
height: 255px;
box-sizing: border-box;
background-color: #15aabf;
color: white;
text-align: center;
padding-top: 80px;
/*padding: 50px;*/
font-size: 45px;
font-weight: bold;
}
#header-title-login-user {
font-size: 36px;
letter-spacing: -1.08px;
}
#header-title-select-shop {
margin-top: 20px;
font-size: 45px;
letter-spacing: 1.1px;
}
#product-container {
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
column-gap: 10px;
row-gap: 15px;
}
.product-card {
width: 300px;
margin: auto;
cursor: pointer;
}
.product-card .card-header {
width: 300px;
}
.product-card .card-header img {
width: 300px;
}
.product-card .card-body {
margin-top: 15px;
}
.product-card .card-body .title {
font-size: 11px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.75px;
text-align: left;
color: #343a40;
margin-bottom: 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.product-card .card-body .lprice {
font-size: 15.8px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.79px;
color: #000000;
margin-bottom: 10px;
}
.product-card .card-body .lprice span {
font-size: 13.4px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.43px;
text-align: left;
color: #E8344E;
}
.product-card .card-body .isgood {
margin-top: 10px;
padding: 10px 20px;
color: white;
border-radius: 2.6px;
background-color: #ff8787;
width: 48px;
}
.pagination {
margin-top: 12px;
margin-left: auto;
margin-right: auto;
width: 53%;
}
.folder-active {
background-color: crimson !important;;
}
.none {
display: none;
}
.folder-bar {
width: 100%;
overflow: hidden;
}
.folder-black, .folder-black:hover {
color: #fff!important;
background-color: #000!important;
}
.folder-bar .folder-button {
white-space: normal;
}
.folder-bar .folder-bar-item {
padding: 8px 16px;
float: left;
width: auto;
border: none;
display: block;
outline: 0;
}
.folder-btn, .folder-button {
border: none;
display: inline-block;
padding: 8px 16px;
vertical-align: middle;
overflow: hidden;
text-decoration: none;
color: inherit;
background-color: inherit;
text-align: center;
cursor: pointer;
white-space: nowrap;
}
#login-form {
width: 538px;
height: 710px;
margin: 70px auto 141px auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
/*gap: 96px;*/
padding: 56px 0 0;
border-radius: 10px;
box-shadow: 0 4px 25px 0 rgba(0, 0, 0, 0.15);
background-color: #ffffff;
}
#login-title {
width: 303px;
height: 32px;
/*margin: 56px auto auto auto;*/
flex-grow: 0;
font-family: SpoqaHanSansNeo;
font-size: 32px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.96px;
text-align: left;
color: #212529;
}
#login-kakao-btn {
border-width: 0;
margin: 96px 0 8px;
width: 393px;
height: 62px;
flex-grow: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10px;
/*margin: 0 0 8px;*/
padding: 11px 12px;
border-radius: 5px;
background-color: #ffd43b;
font-family: SpoqaHanSansNeo;
font-size: 20px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
color: #414141;
}
#login-id-btn {
width: 393px;
height: 62px;
flex-grow: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10px;
/*margin: 8px 0 0;*/
padding: 11px 12px;
border-radius: 5px;
border: solid 1px #212529;
background-color: #ffffff;
font-family: SpoqaHanSansNeo;
font-size: 20px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
color: #414141;
}
.login-input-box {
border-width: 0;
width: 370px !important;
height: 52px;
margin: 14px 0 0;
border-radius: 5px;
background-color: #e9ecef;
}
.login-id-label {
/*width: 44.1px;*/
/*height: 16px;*/
width: 382px;
padding-left: 11px;
margin-top: 40px;
/*margin: 0 337.9px 14px 11px;*/
font-family: NotoSansCJKKR;
font-size: 16px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.8px;
text-align: left;
color: #212529;
}
#login-id-submit {
border-width: 0;
width: 393px;
height: 62px;
flex-grow: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10px;
margin: 40px 0 0;
padding: 11px 12px;
border-radius: 5px;
background-color: #15aabf;
font-family: SpoqaHanSansNeo;
font-size: 20px;
font-weight: bold;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-align: center;
color: #ffffff;
}
#logout-text {
position:absolute;
top:48px;
right:48px;
font-size: 18px;
font-family: SpoqaHanSansNeo;
font-size: 18px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: 0.36px;
text-align: center;
color: #ffffff;
}
@media ( max-width: 768px ) {
body {
margin: 0px;
}
#search-result-box {
margin-top: 15px;
}
.search-itemDto {
width: 330px;
display: flex;
flex-direction: row;
align-content: center;
justify-content: space-around;
}
.search-itemDto-left img {
width: 80px;
height: 80px;
}
.search-itemDto-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
}
.search-itemDto-center div {
width: 190px;
height: 23px;
font-size: 13px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 1.3;
letter-spacing: -0.9px;
text-align: left;
color: #343a40;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.search-itemDto-center div.price {
height: 27px;
font-size: 17px;
font-weight: 600;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.54px;
text-align: left;
color: #E8344E;
}
.search-itemDto-center span.unit {
width: 17px;
height: 18px;
font-size: 14px;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: 1;
letter-spacing: -0.9px;
text-align: center;
color: #000000;
}
.search-itemDto-right {
display: inline-block;
height: 100%;
vertical-align: middle
}
.search-itemDto-right img {
height: 14px;
width: 14px;
vertical-align: middle;
margin-top: 26px;
cursor: pointer;
padding-right: 20px;
}
input#query {
padding: 15px;
width: 290px;
border-radius: 2px;
background-color: #e9ecef;
border: none;
background-image: url('../images/icon-search.png');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
}
input#query::placeholder {
padding: 15px;
}
button {
color: white;
border-radius: 4px;
border-radius: none;
}
.popup-container {
display: none;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
align-items: center;
justify-content: center;
}
.popup-container.active {
display: flex;
}
.popup {
position: absolute;
bottom: 0px;
width: 333px;
border-radius: 11px 11px 0px 0px;
}
.popup input {
width: 278px !important;
height: 39px;
border-radius: 2px;
border: solid 1.1px #dee2e6;
margin-right: 9px;
margin-bottom: 10px;
padding-left: 10px;
}
.popup p {
font-size: 14px;
}
.popup button.close {
position: absolute;
top: 15px;
right: 15px;
color: #adb5bd;
background-color: #fff;
font-size: 19px;
border: none;
}
.popup button.cta {
width: 319px;
height: 43.9px;
border-radius: 2px;
background-color: #15aabf;
border: none;
}
#search-area, #see-area {
width: 330px;
margin: auto;
}
.nav {
width: 330px;
margin: 30px auto;
display: flex;
align-items: center;
justify-content: space-around;
}
.nav div {
cursor: pointer;
}
.nav div.active {
font-weight: 700;
}
.header {
height: 255px;
box-sizing: border-box;
background-color: #15aabf;
color: white;
text-align: center;
padding-top: 80px;
/*padding: 50px;*/
font-size: 45px;
font-weight: bold;
}
/*#product-container {*/
/* grid-template-columns: 100px 50px 100px;*/
/* grid-template-rows: 80px auto 80px;*/
/* column-gap: 10px;*/
/* row-gap: 15px;*/
/*}*/
/*.product-card {*/
/* width: 300px;*/
/* margin: auto;*/
/* cursor: pointer;*/
/*}*/
/*.product-card .card-header {*/
/* width: 300px;*/
/*}*/
/*.product-card .card-header img {*/
/* width: 300px;*/
/*}*/
/*.product-card .card-body {*/
/* margin-top: 15px;*/
/*}*/
/*.product-card .card-body .title {*/
/* font-size: 15px;*/
/* font-weight: normal;*/
/* font-stretch: normal;*/
/* font-style: normal;*/
/* line-height: 1;*/
/* letter-spacing: -0.75px;*/
/* text-align: left;*/
/* color: #343a40;*/
/* margin-bottom: 10px;*/
/* overflow: hidden;*/
/* white-space: nowrap;*/
/* text-overflow: ellipsis;*/
/*}*/
/*.product-card .card-body .lprice {*/
/* font-size: 15.8px;*/
/* font-weight: normal;*/
/* font-stretch: normal;*/
/* font-style: normal;*/
/* line-height: 1;*/
/* letter-spacing: -0.79px;*/
/* color: #000000;*/
/* margin-bottom: 10px;*/
/*}*/
/*.product-card .card-body .lprice span {*/
/* font-size: 21.4px;*/
/* font-weight: 600;*/
/* font-stretch: normal;*/
/* font-style: normal;*/
/* line-height: 1;*/
/* letter-spacing: -0.43px;*/
/* text-align: left;*/
/* color: #E8344E;*/
/*}*/
/*.product-card .card-body .isgood {*/
/* margin-top: 10px;*/
/* padding: 10px 20px;*/
/* color: white;*/
/* border-radius: 2.6px;*/
/* background-color: #ff8787;*/
/* width: 48px;*/
/*}*/
.none {
display: none;
}
input#kakao-login {
padding: 15px;
width: 526px;
border-radius: 2px;
background-color: #e9ecef;
border: none;
background-image: url('images/kakao_login_medium_narrow.png');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
}
}
.autocomplete {
/*the container must be positioned relative:*/
position: relative;
display: inline-block;
}
input {
border: 1px solid transparent;
background-color: #f1f1f1;
padding: 10px;
font-size: 16px;
}
input[type=text] {
background-color: #f1f1f1;
/*width: 100%;*/
}
input[type=password] {
background-color: #f1f1f1;
/*width: 100%;*/
}
input[type=submit] {
background-color: DodgerBlue;
color: #fff;
}
.autocomplete-items {
position: absolute;
border: 1px solid #d4d4d4;
border-bottom: none;
border-top: none;
z-index: 99;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
}
.autocomplete-items div {
padding: 10px;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #d4d4d4;
}
.autocomplete-items div:hover {
/*when hovering an item:*/
background-color: #e9e9e9;
}
.autocomplete-active {
/*when navigating through the items using the arrow keys:*/
background-color: DodgerBlue !important;
color: #ffffff;
}
.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.alert {
width: 300px;
margin-top: 22px;
padding: 1.75rem 1.25rem;
border: 1px solid transparent;
border-radius: .25rem;
}
시큐리티의 설정도 바꿔줘야겠죠??
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 스프링 시큐리티를 사용하게 되면 누구나 어디든 접속 못하게 할 수 있다.
// 우리는 그 중에서 로그인 기능과 로그아웃 기능은 누구나 할 수 있도록 열어 놓아보겠다.
http.authorizeRequests()
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
.and()
// 로그인 기능 허용
.formLogin()
// 로그인 페이지를 직접 지정
.loginPage("/user/login")
.defaultSuccessUrl("/")
.failureUrl("/user/login?error")
.permitAll()
.and()
// 로그아웃 기능 허용
.logout()
.permitAll();
}
}
좋습니다. 준비는 완료했습니다.
이제 서버를 실행하고 결과 페이지를 살펴보도록 하겠습니다.
localhost:8080에 접속을 하면?
오잉? 뭔가 이상한데?
css가 적용이 안 된것 같은데??
개발자 도구를 이용해서 확인해 봅시다.
개발자 도구에서 style.css의 응답이 없는 것을 확인할 수 있습니다.
왜냐하면 스프링 시큐리티에서 login과 logout 페이지의 권한만 열어놓았지 style.css에 권한은 열어놓지 않았기 때문입니다.
자 style.css의 권한도 열어주도록 합시다.
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// image 폴더를 login 없이 허용
.antMatchers("/images/**").permitAll()
// css 폴더를 login 없이 허용
.antMatchers("/css/**").permitAll()
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
.and()
// 로그인 기능 허용
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
.failureUrl("/user/login?error")
.permitAll()
.and()
// 로그아웃 기능 허용
.logout()
.permitAll();
}
}
css와 image 폴더의 권한도 열어 놓았습니다.
서버를 재구동하고 확인을 해 봅시다.
localhost:8080으로 접속을 합니다.
근사한 로그인 화면을 확인할 수 있네요!! 좋습니다!
h2-console과 user api에 대한 권한도 허용해주기 위해서는 아래와 같이 Security config를 변경해주면 됩니다.
package com.sparta.springadvance.security;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
// h2-console 사용에 대한 허용 (CSRF, FrameOptions 무시)
web
.ignoring()
.antMatchers("/h2-console/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 회원 관리 처리 API (POST /user/**) 에 대해 CSRF 무시
http.csrf()
.ignoringAntMatchers("/user/**");
http.authorizeRequests()
// image 폴더를 login 없이 허용
.antMatchers("/images/**").permitAll()
// css 폴더를 login 없이 허용
.antMatchers("/css/**").permitAll()
// 회원 관리 처리 API 전부를 login 없이 허용
.antMatchers("/user/**").permitAll()
// 그 외 어떤 요청이든 '인증'
.anyRequest().authenticated()
.and()
// 로그인 기능
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
.failureUrl("/user/login?error")
.permitAll()
.and()
// 로그아웃 기능
.logout()
.permitAll();
}
}