
์คํ๋ง ํ๋ ์์ํฌ ๊ธฐ๋ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ณด์์ ๊ตฌํํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ๋ณด์ ํ๋ ์์ํฌ์ ๋๋ค.
์ธ์ฆ(Authentication): ์ฌ์ฉ์์ ์ ์์ ํ์ธํ๊ณ ์ธ์ฆํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋๊ตฐ๊ฐ๊ฐ ์์ ์ด ์ฃผ์ฅํ๋ ์ฌ๋์์ ์ฆ๋ช ํฉ๋๋ค.(์ ๋ถ์ฆ)
์ธ๊ฐ(Authorization): ์ธ์ฆ๋ ์ฌ์ฉ์์ ๋ํ ๊ถํ ๋ถ์ฌ ๋ฐ ์ ๊ทผ ์ ์ด๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. ์ ๊ทผ ์ ์ด, ๋ฉ์๋ ๋จ์์ ๋ณด์ ์ค์ ๋ฑ ๋ค์ํ ๋ฐฉ์์ ์ธ๊ฐ๋ฅผ ์ง์ํฉ๋๋ค. ๋๊ตฐ๊ฐ๊ฐ ํน์ ๋ฆฌ์์ค๋ ์์ ์ ์ ๊ทผํ ์ ์๋์ง ํ์ธํฉ๋๋ค. ์ผ๋จ ์ฌ์ฉ์๊ฐ ์ธ์ฆ์ ๋ฐ์ผ๋ฉด, ํ ๋ช ์ด์์ ๊ฐ์ธ์๊ฒ ํ์ฉ๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฆฌ์์ค ์์ ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
๋ณด์ ํํฐ ์ฒด์ธ(Security Filter Chain): HTTP ์์ฒญ์ ๋ํ ๋ณด์ ํํฐ ์ฒด์ธ์ ๊ตฌ์ฑํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ๋ณด์ ๊ฒ์ฌ๋ฅผ ์ํํฉ๋๋ค. ๋ค์ํ ๋ณด์ ํํฐ๋ฅผ ์กฐํฉํ์ฌ ์์ฒญ์ ๋ํ ๋ณด์ ์ธก๋ฉด์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์ธ์ ๊ด๋ฆฌ(Session Management): ์ฌ์ฉ์์ ์ธ์ ์ ๊ด๋ฆฌํ๊ณ , ์ธ์ ๊ณ ์ ๊ณต๊ฒฉ, ์ธ์ ํ์์์ ์ค์ , ์ธ์ ์์ฑ ๋ฐ ๋ง๋ฃ ์ฒ๋ฆฌ ๋ฑ์ ์ง์ํฉ๋๋ค.
Remember-Me ๊ธฐ๋ฅ: ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธํ ์ํ๋ฅผ ๊ธฐ์ตํ์ฌ, ์ธ์ ์ด ๋ง๋ฃ๋์ด๋ ๋ค์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ํ ๋ ์๋์ผ๋ก ๋ก๊ทธ์ธ๋๋๋ก ํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
CSRF(Cross-Site Request Forgery) ๋ฐฉ์ด: CSRF ๊ณต๊ฒฉ์ ๋ฐฉ์ดํ๊ธฐ ์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
๋จ์ผ ๋ฉ์๋ loadUserByUsername (String username)์ด ์๋ ์ธํฐํ์ด์ค๋ก UserDetails ์ธํฐํ์ด์ค๋ฅผ ์ถฉ์กฑํ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ณ ์ด ์ธํฐํ์ด์ค์์ ์ฃผ์ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค.
@Component
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final BCryptPasswordEncoder encoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username.equals("son")){
String encoded = encoder.encode("son7");
return new User("son",encoded, List.of(new SimpleGrantedAuthority("user")));
}else if (username.equals("lee")){
String encoded = encoder.encode("lee9");
return new User("lee",encoded, List.of(new SimpleGrantedAuthority("admin"),
new SimpleGrantedAuthority("user")));
} else{
throw new UsernameNotFoundException(username + " not found");
}
}
}
UserDetailsService ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ฌ loadUserByUsername ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํฉ๋๋ค. ์ด ๋ฉ์๋๋ ์ฌ์ฉ์ ์ด๋ฆ(username)์ ๋ฐ์ ํด๋น ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ก๋ํ๊ณ UserDetails ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ๋ง์ฝ ํด๋น ์ฌ์ฉ์๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ฉด UsernameNotFoundException์ ๋ฐ์์ํต๋๋ค.
์ด ์์ ์์๋ ์ฌ์ฉ์ ์ด๋ฆ์ด "son"์ธ ๊ฒฝ์ฐ์ "lee"์ธ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์์ต๋๋ค. "son" ์ฌ์ฉ์๋ "user" ๊ถํ์ ๊ฐ์ง๋ฉฐ, "lee" ์ฌ์ฉ์๋ "admin"๊ณผ "user" ๊ถํ์ ๊ฐ์ง๋๋ค. ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๋ ๊ฐ๊ฐ "son7"๊ณผ "lee9"์ด๋ฉฐ, BCryptPasswordEncoder๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํฉ๋๋ค.
BCryptPasswordEncoder๋ ์คํ๋ง ์ํ๋ฆฌํฐ์์ ์ ๊ณตํ๋ ๋น๋ฐ๋ฒํธ ์ธ์ฝ๋ ์ค ํ๋๋ก, ์ธ์ฝ๋๋ฅผ ์ฌ์ฉํด ์ฌ์ฉ์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํํ๋ ๋ฐฉ์์
๋๋ค. ๋น๋ฐ๋ฒํธ ์ธ์ฝ๋์ encode() ๋ฉ์๋์ ์ผ๋ฐ ํ
์คํธ๋ก ๋ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ฌ ํ ํธ์ถํ๋ฉด, ์ํธํ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค.
@Component ์ด๋
ธํ
์ด์
์ ํตํด ์คํ๋ง์๊ฒ ์ด ํด๋์ค๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋์ด์ผ ํจ์ ์๋ ค์ค๋๋ค. ๋ํ @RequiredArgsConstructor ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ํ๋ ์ฃผ์
๋ฐฉ์์ผ๋ก BCryptPasswordEncoder๋ฅผ ์ฃผ์
๋ฐ์ต๋๋ค. ์ด๋ฅผ ํตํด ์์ฑ์ ์ธ์ ์
์ ์๋์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์ด์ ์ด ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ์ํํ ์ ์์ต๋๋ค. ์คํ๋ง ์ํ๋ฆฌํฐ ์ค์ ์์ ์ด CustomUserDetailsService ๋น์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ก๋ํ๊ณ ์ธ์ฆ์ ํ์ฉํ ์ ์์ต๋๋ค.
SecurityFilterChain : SecurityFilterChain ๋น์ ์์ฑํด HttpSecurity๋ฅผ ์ค์ ํฉ๋๋ค.@Configuration
@EnableMethodSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable());
http.formLogin(form -> form.loginPage("/login"));
return http.build();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
HttpSecurity ๊ฐ์ฒด๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์์ Spring Security์ ์ค์ ์ ๊ตฌ์ฑํฉ๋๋ค. http.csrf(csrf -> csrf.disable())๋ CSRF ๋ณดํธ ๊ธฐ๋ฅ์ ๋นํ์ฑํํฉ๋๋ค. http.formLogin(form -> form.loginPage("/login"))์ ๋ก๊ทธ์ธ ํผ์ ๊ตฌ์ฑํฉ๋๋ค. form.loginPage("/login")์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์ง ์์ ๊ฒฝ์ฐ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํ ๊ฒฝ๋ก๋ฅผ ์ง์ ํฉ๋๋ค. ์ด ์ฝ๋๋ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์ง ์์ ์์ฒญ์ ๋ณด๋ด๋ฉด Spring Security๊ฐ ์๋์ผ๋ก ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํฉ๋๋ค.return http.build()์ HttpSecurity ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ SecurityFilterChain์ ๊ตฌ์ฑํ๊ณ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ๋ฐํํฉ๋๋ค. * CSRF(Cross-Site Request Forgery)๋ ์น ์ดํ๋ฆฌ์ผ์ด์
์ ๋ณด์ ์ทจ์ฝ์ ์ค ํ๋๋ก, ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ์๋ํ์ง ์์ ์์ฒญ์ ์
์์ ์ธ ๊ณต๊ฒฉ์์ ์ํด ์ ์ก๋๋ ๊ฒ์ ๋ฐฉ์งํ๋ ๋ณด์ ๊ธฐ๋ฅ์
๋๋ค.
Spring Security์ ์ค์ ํด๋์ค์ @EnableMethodSecurity ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด Spring Security์ ๋ฉ์๋ ์์ค ๋ณด์์ ํ์ฑํํ ์ ์์ต๋๋ค.
1. @PreAuthorize ์ด๋
ธํ
์ด์
์ฌ์ฉ:
@PreAuthorize ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ํน์ ๋ฉ์๋์ ๋ํ ์ ๊ทผ ๊ถํ์ ์ง์ ํ ์ ์์ต๋๋ค. 2. JSP ํ์ผ์์ Spring Security ํ๊ทธ ์ฌ์ฉ:
<sec:authorize> ํ๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ์ธ์ฆ ์ํ๋ ๊ถํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ปจํ
์ธ ๋ฅผ ๋ณด์ฌ์ค ์ ์์ต๋๋ค.@Controller
public class Controller38 {
@GetMapping("/login")
public String login(){
return "login";
}
@GetMapping("/path1")
@PreAuthorize("isAuthenticated()")
public void path1(){
System.out.println("Controller38.path1");
}
@GetMapping("/path2")
@PreAuthorize("hasAnyAuthority('user')")
public void path2(){
System.out.println("์ ์ ๊ถํ์ด๋ฉด ์คํ ๊ฐ๋ฅ");
}
@GetMapping("/path4")
public void path4(){
}
}
login() ๋ฉ์๋๋ GET ์์ฒญ์ ์ฒ๋ฆฌํ๋ฉฐ, ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ค๋๋ค.path1: ๋ก๊ทธ์ธ์ด ํ์ํ ๊ฒฝ๋ก๋ก, @PreAuthorize("isAuthenticated()") ์ด๋
ธํ
์ด์
์ด ๋ถ์ด ์์ด ์ธ์ฆ๋ ์ฌ์ฉ์๋ง ํด๋น ๊ฒฝ๋ก์ ์ ๊ทผํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ง์ด Controller38.path1์ด ์ถ๋ ฅ๋๋ ๋ฉ์๋๋ฅผ ์คํํ ์ ์์ต๋๋ค.path2: hasAnyAuthority('user') ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง์ด ํด๋น ๊ฒฝ๋ก์ ์ ๊ทผํ ์ ์์ต๋๋ค. ์ฆ, 'user' ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๊ฐ ํด๋น ๋ฉ์๋๋ฅผ ์คํํ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ 'user' ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง์ด "์ ์ ๊ถํ์ด๋ฉด ์คํ ๊ฐ๋ฅ"์ด ์ถ๋ ฅ๋๋ ๋ฉ์๋๋ฅผ ์คํํ ์ ์์ต๋๋ค.path4: path4.jsp์์ ์ ๊ทผ์ ์ ํํฉ๋๋ค.path4.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>path4.jsp</h3>
<div>๋๊ตฌ๋ ๋ณด๋ ์ปจํ
์ธ </div>
<sec:authorize access="isAuthenticated()">
<div>๋ก๊ทธ์ธ ํด์ผ ๋ณด์ด๋ ์ปจํ
์ธ </div>
</sec:authorize>
<sec:authorize access="not isAuthenticated()">
<div>๋ก๊ทธ์ธ ์ํด์ผ ๋ณด์ด๋ ์ปจํ
์ธ </div>
</sec:authorize>
<sec:authorize access="hasAnyAuthority('admin')">
<div>์ด๋๋ฏผ๋ง ๋ณด์ด๋ ์ปจํ
์ธ </div>
</sec:authorize>
</body>
</html>
"/path4" ๊ฒฝ๋ก๋ก ๋ค์ด์จ ์์ฒญ์ ๋ํ ์๋ต์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
<sec:authorize> ํ๊ทธ:
access ์์ฑ์ ์ฌ์ฉํ์ฌ ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ปจํ
์ธ ๋ฅผ ํ์ํ๊ฑฐ๋ ์จ๊น๋๋ค.isAuthenticated():
Authentication ๊ฐ์ฒด๊ฐ ์กด์ฌํ ๋) ์ฐธ์ด ๋๋ ํํ์์
๋๋ค. not isAuthenticated():
hasAnyAuthority('admin'):