๐ Spring์์ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ(Authorization)๋ ๋ณด์์ ํต์ฌ ๊ฐ๋ ์ผ๋ก, ์ฌ์ฉ์์ ์ ์ ํ์ธ๊ณผ ๊ถํ ๊ด๋ฆฌ๋ฅผ ๋ด๋นํ๋ค.
๐ ์ธ์ฆ: ๋ ๋๊ตฌ์ผ?
๐ ์ธ๊ฐ: ๋ ์ด๊ฑฐ ํ ์ ์์ด?
๐ ์ธ์ฆ์ ์ฌ์ฉ์๊ฐ ๋๊ตฌ์ธ์ง ํ์ธํ๋ ๊ณผ์ ์ด๋ค. -> ๋ก๊ทธ์ธ
์ผ๋ฐ์ ์ผ๋ก ์์ด๋(ID)์ ๋น๋ฐ๋ฒํธ(Password)๋ฅผ ์ ๋ ฅํ์ฌ ๋ณธ์ธ์ด ๋๊ตฌ์ธ์ง ์ฆ๋ช ํ๋ค.
JWT, OAuth2, Session/Cookie ๋ฑ์ ์ด์ฉํด ์ธ์ฆํ ์ ์๋ค.
Spring Security: ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ์ธ์ฆ ํ๋ ์์ํฌ
JWT (JSON Web Token): ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ ๋ฐฉ์
Session & Cookie: ์๋ฒ์์ ์ธ์ ์ ๊ด๋ฆฌํ๋ ๋ฐฉ์
OAuth 2.0: Google, Facebook ๋ฑ์ ์ธ๋ถ ์๋น์ค ๋ก๊ทธ์ธ ์ฐ๋
์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ ์์ฒญ (ID, PW)
AuthenticationManager
๊ฐ UserDetailsService
๋ฅผ ํตํด ์ฌ์ฉ์ ์ ๋ณด ์กฐํ
๋น๋ฐ๋ฒํธ ๊ฒ์ฆ ํ, ์ฑ๊ณตํ๋ฉด SecurityContextHolder
์ Authentication
๊ฐ์ฒด ์ ์ฅ
์ธ์ฆ ์ฑ๊ณต ์, JWT ํ ํฐ ๋๋ ์ธ์ ์ ์์ฑํ์ฌ ๋ฐํ
๐ ์ธ๊ฐ(๊ถํ ๋ถ์ฌ)๋ ํน์ ์ฌ์ฉ์๊ฐ ์ด๋ค ์์ ์ ์ํํ ์ ์๋์ง ๊ฒฐ์ ํ๋ ๊ณผ์ ์ด๋ค. -> ๊ถํ
์๋ฅผ ๋ค์ด, ์ผ๋ฐ ์ฌ์ฉ์๋ ์ฃผ๋ฌธ์ ํ ์ ์์ง๋ง, ๊ฐ๊ฒ ์ฌ์ฅ๋์ ๊ฐ๊ฒ ์ ๋ณด๋ฅผ ์์ ํ ์ ์๋ค.
์ฆ, ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ํน์ ๋ฆฌ์์ค(URI, ๋ฉ์๋, ๋ฐ์ดํฐ)์ ์ ๊ทผํ ์ ์๋์ง ํ์ธํ๋ ๊ณผ์ ์ด๋ค.
@PreAuthorize("hasRole('ADMIN')")
โ ํน์ ์ญํ ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ์ ๊ทผ ํ์ฉ
@Secured("ROLE_USER")
โ ํน์ ์ญํ ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ๋ฉ์๋ ์คํ ๊ฐ๋ฅ
@RolesAllowed("ROLE_OWNER")
โ ์ง์ ๋ ์ญํ ์ด ์์ด์ผ ์ ๊ทผ ๊ฐ๋ฅ
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated();
@PreAuthorize("hasRole('OWNER')")
public void updateStoreInfo() { ... }
๐
JwtFilter
๋ Spring Security์ Filter ์ค ํ๋๋ก, ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ ์์ฒญ์์ JWT(ํ ํฐ)๋ฅผ ์ถ์ถํ๊ณ ๊ฒ์ฆํ๋ ์ญํ ์ ํ๋ค.
(Authorization: Bearer <token>)
์์ JWT๋ฅผ ๊ฐ์ ธ์ด.User ID
Role
๊ธฐํ ์ฌ์ฉ์ ์ ๋ณด (ํ์ ์ ์ถ๊ฐ ๊ฐ๋ฅ)
String token = request.getHeader("Authorization").substring(7); // "Bearer " ์ ๊ฑฐ
Claims claims = jwtUtil.parseToken(token);
String userId = claims.getSubject();
String role = claims.get("role", String.class);
JwtFilter์์ JWT ์ ๋ณด๋ฅผ ์ถ์ถํ์ง๋ง, ์ปจํธ๋กค๋ฌ์์ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ๋ ค๋ฉด?
๐ ์ปจํธ๋กค๋ฌ์์
@LoginUser
๊ฐ์ ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ ๋ฐ๋ก ์ ์ ์ ๋ณด๋ฅผ ๋ฐ๋๋ก ์ฒ๋ฆฌ ๊ฐ๋ฅ!
@Getter
@AllArgsConstructor
public class LoginUser {
private Long id;
private String email;
private String role;
}
LoginUser loginUser = new LoginUser(userId, email, role);
request.setAttribute("loginUser", loginUser); // Request์ ์ ์ฅ
@Component
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
return webRequest.getAttribute("loginUser", RequestAttributes.SCOPE_REQUEST);
}
}
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/profile")
public ResponseEntity<?> getUserProfile(@LoginUser LoginUser loginUser) {
return ResponseEntity.ok("Hello, " + loginUser.getEmail());
}
}
์ปจํธ๋กค๋ฌ๊ฐ JwtFilter
์ ๊ตฌํ ๋ฐฉ์์ ์์กดํ์ง ์์
ํ ์คํธ ๋ฐ ์ ์ง๋ณด์๊ฐ ์ฌ์
@LoginUser
๊ฐ์ ์ด๋
ธํ
์ด์
์ ํตํด ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง