-
[Spring] ์คํ๋ง ํ์๊ฐ์ , ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ with Spring SecuritySpring 2023. 9. 14. 22:29
๐ ์์ฝ
- BCryptPasswordEncoder๋ฅผ ํตํด ๋น๋ฐ๋ฒํธ ์ํธํํ์ฌ ํ์๊ฐ์ ์งํ.
- Spring Security๋ฅผ ์ด์ฉํ์ฌ ๋ก๊ทธ์ธ ๊ตฌํ๊ฐ๋จํ๊ฒ ์คํ๋ง์ผ๋ก ๊ฒ์ํ์ ๋ง๋ค๋ฉด์, ํ์๊ฐ์ , ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ์๋ค.
1. ํ๊ฒฝ ์ ํ
- spring boot
- Project - Gradle
- Spring boot - 2.7.15
- Jar
- java - 11
- MariaDB
- JSP
- JPA
- Lombok
- Spring Security
2. ํ์๊ฐ์ ๊ตฌํ
[๋ก์ง ์์ฝ]
- ์ ์ ๋ก๋ถํฐ, โ์์ด๋โ, โํจ์ค์๋โ, โ๋๋ค์โ ์ ๋ ฅ ๋ฐ๊ธฐ
- ์ ํจ์ฑ ์ฒดํฌ ํ ํ์๊ฐ์ API ํธ์ถ
- ํ์๊ฐ์ ๋ก์ง ์คํ
- ์์ด๋ ์ค๋ณต ๋ฐ๊ฒฌ ์ โ์ค๋ณต alert ์ฐฝ ํ์. ๋ฌธ์ ์์ผ๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋.
1) ์ ์ ๋ก๋ถํฐ, โ์์ด๋โ, โํจ์ค์๋โ, โ๋๋ค์โ ์ ๋ ฅ ๋ฐ๊ธฐ
<form id="joinForm" class="space-y-6" action="/users/join" method="POST"> <div> <label for="loginId" class="block text-sm font-medium leading-6 text-gray-900">์์ด๋</label> <div class="mt-2"> <input id="loginId" name="loginId" type="text" class="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" > <form:errors path="loginId"/> </div> </div> <div> <label for="password" class="block text-sm font-medium leading-6 text-gray-900">ํจ์ค์๋</label> <div class="mt-2"> <input id="password" name="password" type="password" required class="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> </div> </div> <div> <label for="username" class="block text-sm font-medium leading-6 text-gray-900">๋๋ค์</label> <div class="mt-2"> <input id="username" name="username" type="text" required class="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> </div> </div> <div> <button type="button" id="joinBtn" onclick="join()" class="flex w-full justify-center rounded-md bg-indigo-500 px-3 py-3 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"> ํ์๊ฐ์ </button> </div> </form>
2) ์ ํจ์ฑ ์ฒดํฌ ํ ํ์๊ฐ์ API ํธ์ถ
const join = async ()=>{ const loginId = $("#loginId").val(); const password = $("#password").val(); const username = $("#username").val(); // ์ ํจ์ฑ ๊ฒ์ฌ if(!checkValidation(loginId, password, username)){ return; } // api ์งํ const requestBody = { loginId, password, username } const resultStatus = await proceedJoinAPI(requestBody); if(resultStatus ==200){ alert("ํ์๊ฐ์ ์ถํ๋๋ฆฝ๋๋ค. ๋ก๊ทธ์ธ ๋ถํ๋๋ฆฝ๋๋ค.") location.href="/users/login" }else if(resultStatus==409){ alert("์์ด๋๊ฐ ์ค๋ณต๋ฉ๋๋ค. ๋ค๋ฅธ ์์ด๋๋ก ์๋ํด์ฃผ์ธ์.") } } const checkValidation = (loginId, password, username)=>{ const regIdPw = /^[a-zA-Z0-9]{4,12}$/; const regName = /^[๊ฐ-ํฃa-zA-Z0-9]{2,15}$/; if (!regIdPw.test(loginId)) { alert("์์ด๋๋ ์ซ์, ์๋ฌธ 4๊ธ์ ์ด์ 12์ ์ดํ๋ง ๊ฐ๋ฅํฉ๋๋ค.") return false; } if (!regIdPw.test(password)) { alert("ํจ์ค์๋ ์ซ์, ์๋ฌธ 4๊ธ์ ์ด์ 12์ ์ดํ๋ง ๊ฐ๋ฅํฉ๋๋ค.") return false; } if (!regName.test(username)) { alert("๋๋ค์์ ์ซ์, ์๋ฌธ, ํ๊ธ 2๊ธ์ ์ด์ 15๊ธ์ ์ดํ๋ง ๊ฐ๋ฅํฉ๋๋ค.") return false; } return true; } const proceedJoinAPI = async (requestBody)=>{ const resp = await fetch("http://localhost:8080/users/join", { method:"POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestBody) }); return resp.status }
- ์์ด๋, ํจ์ค์๋, ๋๋ค์์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฌ ์งํ
- ์ ํจ์ฑ ๊ฒ์ฌ์ ํต๊ณผ๋์ง ๋ชปํ๋ฉด alert ํ์
- ์ ํจ์ฑ ๊ฒ์ฌ์ ํต๊ณผ๋ ํ, ํ์๊ฐ์
API ํธ์ถ
- request body์ loginId, password, username ์ ๋ฌ
3) ํ์๊ฐ์ ๋ก์ง ์คํ
controller
@PostMapping("/users/join") @ResponseBody public ResponseEntity<String> join(@RequestBody UserJoinDto userJoinDto){ log.info(userJoinDto.toString()); // ์ค๋ณต ๊ฒ์ฌ if(userService.existsDuplicatedUser(userJoinDto)){ log.info("์ค๋ณต ์กด์ฌ"); return new ResponseEntity<>("fail", HttpStatus.CONFLICT); } userService.join(userJoinDto); return new ResponseEntity<>("success", HttpStatus.OK); }
- โ/users/joinโ API ํธ์ถํ์ฌ, ์์ ์ปจํธ๋กค๋ฌ ๋ก์ง ์คํ
- @RequestBody : request body์ ์จ data๋ฅผ ๊บผ๋ด์ด์ ์ฌ์ฉํ ์ ์๋ ์ ๋ ธํ ์ด์
- userService.existsDuplicatedUser(userJoinDto) : ๋น์ฆ๋์ค ๋ก์ง์ด ๋ด๊ฒจ์๋ ์๋น์ค ์ธต์์, ์ ์ ๋ก๊ทธ์ธ ์์ด๋๊ฐ ์ค๋ณต๋์๋์ง ํ์ธ.
- userService.join(userJoinDto) : ๋น์ฆ๋์ค ๋ก์ง์ด ๋ด๊ฒจ์๋ ์๋น์ค ์ธต์์, ์ ์ ์ ํ์๊ฐ์ ์งํ.
service
@Service @Transactional(readOnly = true) @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final BCryptPasswordEncoder encoder; @Transactional public void join(UserJoinDto userJoinDto){ String encodedPassword = encoder.encode(userJoinDto.getPassword()); userRepository.save(userJoinDto.toEntity(encodedPassword)); } public boolean existsDuplicatedUser(UserJoinDto userJoinDto){ // ์์ด๋ ์ค๋ณต ์ฒดํฌ if(userRepository.existsByLoginId(userJoinDto.getLoginId())){ return true; } return false; } }
- BCryptPasswordEncoder : ๋น๋ฐ๋ฒํธ ์ํธํํ๋๋ฐ ์ฌ์ฉํ๋ encoder. ์๋์์ ์ค๋ช ํ Spring Security ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด ๋ก๊ทธ์ธ์ ๊ตฌํํ๊ธฐ ์ํด์๋ Security๊ฐ ์ ๊ณตํ๋ BcryptPasswordEncoder๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
- existsDuplicatedUser : repository ์์ existsByLoginId๋ฅผ ์ด์ฉํ์ฌ login ์์ด๋๊ฐ ์กด์ฌํ๋์ง ์ฒดํฌํ๋ ํจ์
- join : ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํ๊ณ entity ๊ฐ์ฒด๋ก ๋ณํ ํ repository์ user ์ ์ฅ. (UserJoinDto ๋ฅผ User ์ํฐํฐ๋ก ๋ณํํ๋ ๋ก์ง์ builder()๋ฅผ ํตํด ์งํ)
repository
@Repository public interface UserRepository extends JpaRepository<User, Long> { ... Boolean existsByLoginId(String loginId); }
Boolean existsByLoginId(String loginId) : User Table์์ ํ๋ผ๋ฏธํฐ๋ก ๋์ด์จ loginId ๊ฐ๊ณผ ์ผ์นํ loginId๊ฐ ์๋์ง ํ์ธํ๋ ํจ์. (์ถ์ํ๊ฐ ์๋์ด์๊ธฐ ๋๋ฌธ์, ์ง์ ๊ตฌํ์ํด๋ ๋จ)
3. ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ
๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ๋ค์ด๊ฐ๊ธฐ ์์ ๋จผ์ , Spring Security์ ๋ํด ์๊ณ ๊ฐ์ผํ๋ค.
๐ [Spring Security]
spring security๋ ์คํ๋ง ๊ธฐ๋ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ณด์(์ธ์ฆ, ์ธ๊ฐ)์ ๋ด๋นํ๋ ํ๋ ์์ํฌ.
filter ํ๋ฆ์ ๋ฐ๋ผ ๋ก์ง์ ์ฒ๋ฆฌํ๋๊ฒ ํน์ง์ด๋ค.
- ์ธ์ฆ(Authentication) : ์ฌ์ฉ์๊ฐ โ์ธ์ฆโํ๋ ๊ณผ์ โ ๋ก๊ทธ์ธ ๊ณผ์ ์ฒ๋ฆฌ
- ์ธ๊ฐ(Authorization) : ์๋ฒ๊ฐ ์ฌ์ฉ์์ ๋ํ ๊ถํ ๋ถ์ฌ
spring security์ ๋ํ ํน์ ์ ํ ์์ด, ์์ํ๋ฉด ๋ชจ๋ ํ์ด์ง์ ๋ํด ๋ก๊ทธ์ธํ ์ฌ๋์ ๋ํด ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๊ฐ์ ์ ์ผ๋ก ์ด๋ํ๊ฒ ๋๋ค. ๊ทธ๋์, ํ์ด์ง๋ณ ๊ถํ ์ค์ ์ด ํ์ํ๋ค.
๋จผ์ SecurityConfig ํ์ผ์ ์์ฑํ์.
// SecurityConfig @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { // ๋น๋ฐ๋ฒํธ ์ํธํ encoder @Bean public BCryptPasswordEncoder encoder(){ return new BCryptPasswordEncoder(); } // ๋ก๊ทธ์ธํ์ง ์์ ์ ์ ๋ค๋ง ์ ๊ทผ ๊ฐ๋ฅํ URL private static final String[] anonymousUserUrl = {"/users/login", "/users/join"}; // ๋ก๊ทธ์ธํ ์ ์ ๋ค๋ง ์ ๊ทผ ๊ฐ๋ฅํ URL private static final String[] authenticatedUserUrl = {"/boards/write","/boards/**/**/edit", "/boards/**/**/delete", "/likes/**", "/users/boards/**"}; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .csrf().disable() .cors().and() // ํ์ด์ง๋ณ ๊ถํ ์ฒ๋ฆฌ .authorizeRequests() .antMatchers(anonymousUserUrl).anonymous() .antMatchers(authenticatedUserUrl).authenticated() .anyRequest().permitAll() .and() .exceptionHandling() .and() .build(); } }
- authorizeRequests() : URL์ ๋ฐ๋ฅธ ํ์ด์ง์ ๋ํ ๊ถํ์ ๋ถ์ฌํ๊ธฐ ์ํด ์์ํ๋ ๋ฉ์๋
- antMatchers(anonymousUserUrl).anonymous() : anonymousUserUrl ํ์ด์ง์ ๋ํด์๋ ๋ก๊ทธ์ธํ์ง ์์ ์ ์ ๋ง ์ ๊ทผ ๊ฐ๋ฅ.
- antMatchers(authenticatedUserUrl).authenticated() : authenticatedUserUrl์ ๋ํด์๋ ๋ก๊ทธ์ธํ ์ ์ ๋ง ์ ๊ทผ ๊ฐ๋ฅ.
- anyRequest().permitAll() : ํน์ URL์ ์ ์ธํ ๋๋จธ์ง URL์ ๋ํด์๋ ์ ๋ถ ์ธ๊ฐํด์ค.
[๋ก๊ทธ์ธ ๋ก์ง ์์ฝ]
1) ์ ์ ๋ก๋ถํฐ โ์์ด๋โ์ โํจ์ค์๋โ ์ ๋ ฅ ๋ฐ์
2) form action์ ํตํด loginId, password ์ ๋ฌ
3) spring security๊ฐ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ํ
1) ์ ์ ๋ก๋ถํฐ โ์์ด๋โ, โํจ์ค์๋โ ์ ๋ ฅ๋ฐ์.
<form class="space-y-6" method="POST"> <div> <label for="loginId" class="block text-sm font-medium leading-6 text-gray-900">์์ด๋</label> <div class="mt-2"> <input id="loginId" name="loginId" type="text" required class="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> </div> </div> <div> <label for="password" class="block text-sm font-medium leading-6 text-gray-900">ํจ์ค์๋</label> <div class="mt-2"> <input id="password" name="password" type="password" required class="block w-full rounded-md border-0 px-2 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"> </div> </div> <div> <button type="submit" class="flex w-full justify-center rounded-md bg-indigo-500 px-3 py-3 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"> ๋ก๊ทธ์ธ </button> </div> </form>
๋๋ ์ฌ๊ธฐ์ ์์ด๋์ ํด๋นํ๋ ํ๋ผ๋ฏธํฐ ํค ๊ฐ์ 'loginId', ๋น๋ฐ๋ฒํธ์ ํด๋นํ๋ ํค ๊ฐ์ 'password'๋ก ์ง์ ํ์๋ค.
2. form action์ ํตํด loginId, password ์ ๋ฌ
๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญ์, form action ์งํ
3. Spring Security๊ฐ ๋ก๊ทธ์ธ ์งํ
Spring Security๋ฅผ ์ด์ฉํ๋ฉด, form action์ ํตํด ์งํํ๋ ๋ก๊ทธ์ธ ๊ตฌํ ๊ธฐ๋ฅ์ Spring Security์ ์์ํ ์ ์๋ค.
// SecurityConfig package com.grampus.commnuity.config; import com.grampus.commnuity.config.auth.LoginSuccessHandler; import com.grampus.commnuity.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; 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.WebSecurityConfiguration; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { @Bean public BCryptPasswordEncoder encoder(){ return new BCryptPasswordEncoder(); } private final UserRepository userRepository; // ๋ก๊ทธ์ธํ์ง ์์ ์ ์ ๋ค๋ง ์ ๊ทผ ๊ฐ๋ฅํ URL private static final String[] anonymousUserUrl = {"/users/login", "/users/join"}; // ๋ก๊ทธ์ธํ ์ ์ ๋ค๋ง ์ ๊ทผ ๊ฐ๋ฅํ URL private static final String[] authenticatedUserUrl = {"/boards/write","/boards/**/**/edit", "/boards/**/**/delete", "/likes/**", "/users/boards/**", "/users/edit", "/users/delete"}; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .csrf().disable() .cors().and() // url ๋ณ ๊ถํ ์ฒ๋ฆฌ .authorizeRequests() .antMatchers(anonymousUserUrl).anonymous() .antMatchers(authenticatedUserUrl).authenticated() .antMatchers("/users/admin/**").hasAuthority("ADMIN") .anyRequest().permitAll() .and() .exceptionHandling() .and() // ํผ ๋ก๊ทธ์ธ .formLogin() .loginPage("/users/login") // ๋ก๊ทธ์ธ ํ์ด์ง .usernameParameter("loginId") // ๋ก๊ทธ์ธ์ ์ฌ์ฉ๋ id .passwordParameter("password") // ๋ก๊ทธ์ธ์ ์ฌ์ฉ๋ password .failureUrl("/users/login?fail") // ๋ก๊ทธ์ธ ์คํจ ์ redirect ๋ URL => ์คํจ ๋ฉ์ธ์ง ์ถ๋ ฅ .successHandler(new LoginSuccessHandler(userRepository)) .and() // ๋ก๊ทธ์์ .logout() .logoutUrl("/users/logout") // ๋ก๊ทธ์์ URL .invalidateHttpSession(true).deleteCookies("JSESSIONID") .and() .build(); } }
- formLogin() : form ๋ก๊ทธ์ธ ์ฒ๋ฆฌ
- loginPage("/users/login") : ๋ก๊ทธ์ธํ ํ์ด์ง
- usernameParameter("loginId") : ๋ก๊ทธ์ธ์ ์ฌ์ฉ๋ ์์ด๋์ ๋ํ ํ๋ผ๋ฏธํฐ KEY ๊ฐ์ ๊ธฐ์
- passwordParameter("password") : ๋ก๊ทธ์ธ์ ์ฌ์ฉ๋ ๋น๋ฐ๋ฒํธ์ ๋ํ ํ๋ผ๋ฏธํฐ KEY ๊ฐ์ ๊ธฐ์
- failureUrl("/users/login?fail") : ๋ก๊ทธ์ธ ์คํจ์ redirect ํ๋ url
- successHandler(new LoginSuccessHandler(userRepository)) : ๋ก๊ทธ์ธ ์ฑ๊ณต ์, ์คํ๋๋ ํธ๋ค๋ฌ
์ฆ, ์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ด ํด์์ด ๋๋ค.
โ/users/loginโ ํ์ด์ง์์ form์ ํตํด ์ก์ ์ด ๋ค์ด์ค๋ฉด,
ํ๋ผ๋ฏธํฐ์์ ์์ด๋(loginId)์ ํจ์ค์๋(password)๋ฅผ ๊ฐ์ ธ์ ๋ก๊ทธ์ธ์ ์คํ.
๋ก๊ทธ์ธ ์ฑ๊ณต์, LoginSuccessHandler()๊ฐ ์คํ๋๋ฉฐ, ๋ก๊ทธ์ธ ์คํจ์, โurl/login?failโ๋ก ์ด๋์ด ๋๋ค.์ถ๊ฐ์ ์ผ๋ก, ์ง์ db์์ ํ์์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ๋ก๊ทธ์ธ์ด ์คํ๋๋๋ก ํด์ผ ํจ์ผ๋ก,
UserDetailsService์์ loadUserByUsername๋ฅผ ์ค๋ฒ๋ก๋ ํด์ค๋ค.
@Service @RequiredArgsConstructor public class UserDetailService implements UserDetailsService { private final UserRepository userRepository; // db์์ ํ์์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ์ญํ . @Override public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException { User user = userRepository.findByLoginId(loginId).orElseThrow(()-> { return new UsernameNotFoundException("ํด๋น ์ ์ ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."); }); return new UserDetail(user); } }
public class UserDetail implements UserDetails { private User user; public UserDetail(User user) { this.user = user; } // ๊ณ์ ์ด ๊ฐ์ง๊ณ ์๋ ๊ถํ ๋ชฉ๋ก return @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> collection = new ArrayList<>(); collection.add(() -> { return user.getRole().toString(); }); return collection; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getLoginId(); } // ๊ณ์ ์ด ๋ง๋ฃ๋์๋์ง (true: ๋ง๋ฃ X) @Override public boolean isAccountNonExpired() { return true; } // ๊ณ์ ์ด ์ ๊ฒผ๋์ง (true : ์ ๊น X) @Override public boolean isAccountNonLocked() { return true; } // ๋น๋ฐ๋ฒํธ๊ฐ ๋ง๋ฃ๋์๋์ง (ture: ๋ง๋ฃ X) @Override public boolean isCredentialsNonExpired() { return true; } // ๊ณ์ ์ด ํ์ฑํ(์ฌ์ฉ ๊ฐ๋ฅ)์ธ์ง (true: ํ์ฑํ) @Override public boolean isEnabled() { return true; } }
Spring Security๋ฅผ ์ด์ฉํ์ฌ ํ์๊ฐ์ ๊ณผ ๋ก๊ทธ์ธ์ ๊ตฌํํ์๋ค.
๋ณดํต ๋ก๊ทธ์ธ ๋ฐฉ์์ ๊ตฌํํ ๋, ์ธ์ , ์ฟ ํค, jwt ํ ํฐ์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ,
์ธ์ ๊ณผ ์ฟ ํค๋ ๋ฐฉ๋ฒ์ด ์ ์ฌํ๋ฉฐ, ์์ ๋ฐฉ๋ฒ์ ์ธ์ ๋ฐฉ์์ผ๋ก ๊ตฌํ ํ ๊ฒ์ด๋ค.
jwt ํ ํฐ์ ์ฃผ๋ก ์๋ฒ๊ฐ ์ํ๋ฅผ ๊ฐ์ง ์์๋ ๋๋ stateless ํน์ง์ด ์๋๋ฐ, ์ฃผ๋ก rest api๋ก ๊ตฌํํ ๋ ์์ฃผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
์ฌ๊ธฐ์๋ ํด๋ผ์ด์ธํธ์์ rest api๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ด ์๋, jsp๋ฅผ ์ด์ฉํ์ฌ ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง ๋ฐฉ์์ ์ฑผํํ์์ผ๋ฏ๋ก
๊ฐ๋จํ ๊ตฌํ์ ์ํด ์ธ์ ๋ฐฉ์์ผ๋ก ์งํํ์๋ค.
๋์ค์ ์๊ฐ์ด ๋๋ค๋ฉด jwt ํ ํฐ ๋ฐฉ์๋ ์์๋ณด๊ฒ ๋ค.
'Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[6์ฃผ์ฐจ] ์คํ๋ง ์คํฐ๋ (0) 2021.05.08 [์๋ฐ ์คํฐ๋] 4์ฃผ์ฐจ ์ ๋ฆฌ (0) 2021.04.03 ์๋ฐ ์คํ๋ง ์คํฐ๋ 3์ฃผ์ฐจ (0) 2021.03.25 [์คํ๋ง ์คํฐ๋] 2์ฃผ์ฐจ (0) 2021.03.20 Java Spring ์คํฐ๋ 1์ฃผ์ฐจ (0) 2021.03.13