Code N Solve π: Java Spring νλ μμν¬μμμ μΈμ¦ μλ μ리μ λ¬Έμ ν΄κ²°
Spring νλ μμν¬λ μλ° μ΄ν리μΌμ΄μ μμ μΈμ¦κ³Ό μ κ·Ό μ μ΄λ₯Ό μν κ°λ ₯νκ³ μ μ°ν λꡬμ΄λ€.
Spring Securityλ₯Ό μ΄μ©ν μΈμ¦ λ©μ»€λμ¦μ λμ μ리μ μΌλ°μ μΌλ‘ λ°μν μ μλ μΌλ°μ μΈ λ¬Έμ , κ·Έλ¦¬κ³ κ·Έ ν΄κ²° λ°©λ²μ λν΄ μμ보μ.
Spring Security ? π€
- Spring Securityλ Java κΈ°λ° μ΄ν리μΌμ΄μ μμ νμμ μΌλ‘ κ³ λ €λμ΄μΌ ν μμ μ€ νλμ΄λ€.
- μ¬λ°λ₯Έ μ¬μ©μ μλ³κ³Ό μ κ·Ό μ μ΄λ₯Ό ν΅ν΄ μ΄ν리μΌμ΄μ μ μ λ’°μ±μ λμ΄κ³ 보μμ κ°νν μ μλ€.
- νΉν μ¬μ©μ λ°μ΄ν° 보νΈμ κΆν κ΄λ¦¬ κΈ°λ₯μ μ 곡νλ Spring Securityλ κ°λ ₯ν λμ± μ΄ λ μ μλ€.
Spring Authentication μλ μ리
μΈμ¦ κ³Όμ
-
Spring μ΄ν리μΌμ΄μ λ΄μμ μ¬μ©μκ° μμ€ν μ μ κ·ΌνκΈ° μν΄μλ μΈμ¦ κ³Όμ μ μ°μ κ±°μ³μΌ νλ€.
-
μ¬μ©μ μ 보 μ λ ₯
- ν΄λΌμ΄μΈνΈκ° μλ²μ λ‘κ·ΈμΈ μμ²μ 보λ΄λ©΄, μ¬μ©μλ μ΄λ©μΌκ³Ό λΉλ°λ²νΈμ κ°μ μ격 μ¦λͺ μ μ λ ₯νλ€.
-
μΈμ¦ νλ‘μΈμ€ μμ
- μλ²μμλ μ λ ₯λ μ격 μ¦λͺ μ λ°νμΌλ‘μ¬μ©μλ₯Ό μΈμ¦νλ €κ³ μλνλ€.
- μ΄λ
AuthenticationManager
λ₯Ό ν΅ν΄ μ¬μ©μμ μ격 μ¦λͺ μ΄ μ ν¨νμ§ νμΈνλ€. - Spring Securityλ μ΄ κ³Όμ μ ν΅ν΄ μ¬μ©μ μ 보λ₯Ό λ°μ΄ν°λ² μ΄μ€λ λ€λ₯Έ μ μ₯μμμ μ‘°ννμ¬ μ ν¨μ±μ κ²μ¦νλ€.
-
Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
-
κΆν λΆμ¬
- μΈμ¦μ΄ μ±κ³΅νλ©΄, μ¬μ©μλ λ°μ΄ν°λ² μ΄μ€μ μ μ₯λ μν (Role)μ λ°λΌ μ μ ν κΆνμ λΆμ¬λ°λλ€.
- μ΄ν μ ν리μΌμ΄μ μ λ€λ₯Έ λΆλΆμμ κΆνμ΄ κ²μ¦λμ΄ μ¬μ©μκ° νΉμ μμ μ μνν μ μλμ§ κ²°μ λλ€.
-
SecurityContextHolder
- μΈμ¦μ΄ μ±κ³΅νλ©΄, Spring Securityλ
SecurityContextHolder
λ₯Ό μ¬μ©νμ¬ μΈμ¦ μ 보λ₯Ό κ΄λ¦¬νλ€.SecurityContextHolder
λ νμ¬ μΈμ¦λ μ¬μ©μμ μΈλΆ μ 보λ₯Ό μ μ₯νλ 컨ν μ€νΈμ΄λ€.-
SecurityContextHolder.getContext().setAuthentication(authentication);
- μΈμ¦μ΄ μ±κ³΅νλ©΄, Spring Securityλ
μΈμ¦κ³Ό μΈκ°μ μ°¨μ΄ [^1]? π€
-
μΈμ¦(Identification): 'λꡬμΈμ§'λ₯Ό μλ νλ‘μΈμ€λ₯Ό μλ―Έ
-
μΈκ°(Authorization): '무μμ ν μ μλμ§'λ₯Ό κ²°μ
-
λ μ©μ΄λ μ’ μ’ νΌμ©λμ§λ§ λͺ νν ꡬλΆν νμκ° μλ€.
-
μ ν리μΌμ΄μ μ€κ³ μ λ κ°λ μ μ νν μ΄ν΄νκ³ κ΅¬νν΄μΌ νλ€.
λ°μ κ°λ₯ν λ¬Έμ λΆμ λ° μ€λ₯ ν΄κ²° λ°©λ² [^2]
-
μλͺ»λ μ¬μ©μ μ λ ₯ μ²λ¦¬
-
μ¬μ©μκ° μ 곡νλ μ 보λ μ’ μ’ λΆμ νν μ μλ€.
-
μ ν리μΌμ΄μ μ΄ μλͺ»λ μ λ ₯μ κ°μ§νκ³ , λͺ νν μλ¬ λ©μμ§λ₯Ό μ¬μ©μμκ² μ 곡ν΄μΌ νλ€.
-
ν΄κ²° λ°©λ²
- μ ν¨μ± κ²μ¬(validation)λ₯Ό λμ ν΄μΌ νλ€.
- μ ν¨μ± κ²μ¬λ μ¬μ©μκ° μ μΆν λ°μ΄ν°κ° νΉμ κΈ°μ€μ μΆ©μ‘±νλμ§ νμΈνλ κ³Όμ μ΄λ€.
- Javaμμλ
javax.validation.constraints
ν¨ν€μ§λ₯Ό μ¬μ©νμ¬ μ½κ² μ ν¨μ± κ²μ¬λ₯Ό μ€μ ν μ μλ€. -
import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; public class User { @NotBlank(message = "μ΄λ©μΌμ νμ μ λ ₯ μ¬νμ λλ€.") @Email(message = "μ΄λ©μΌ νμμ΄ μλͺ»λμμ΅λλ€.") private String email; @NotBlank(message = "λΉλ°λ²νΈλ νμ μ λ ₯ μ¬νμ λλ€.") private String password; }
-
-
μλ² μ€λ₯ μ§λ¨
- μλ²μμ λ°μνλ μ€λ₯λ μ¬μ©μκ° μ§μ ν΄κ²°νκΈ° μ΄λ €μ΄ λ¬Έμ μ΄λ€.
- μλ₯Ό λ€μ΄, λ°μ΄ν°λ² μ΄μ€ μ°κ²° μ€λ₯κ° λ°μνλ©΄ μλ²λ λ μ΄μ μμ²μ μ²λ¦¬ν μ μλ€.
- μ΄λ° μν©μμλ μλ² λ‘κ·Έμ μ€λ₯λ₯Ό κΈ°λ‘νκ³ , μ΄λ₯Ό λΉ λ₯΄κ² λΆμνμ¬ λ¬Έμ λ₯Ό ν΄κ²°ν μ μμ΄μΌ νλ€.
- ν΄κ²° λ°©λ²
- μμΈ μ²λ¦¬λ₯Ό ν΅ν΄ μ΄λ¬ν μ€λ₯λ₯Ό ν¬μ°©νκ³ , μ€λ₯ λ°μ μ ν΄λΉ μ€λ₯λ₯Ό λ‘κ·Έμ κΈ°λ‘ν΄μΌ νλ€.
- μ΄λ₯Ό ν΅ν΄ κ°λ°μλ μλ²μμ λ°μν λ¬Έμ λ₯Ό λΉ λ₯΄κ² μ§λ¨νκ³ λμν μ μλ€.
-
try { // λ°μ΄ν°λ² μ΄μ€ μ°κ²° μλ connectToDatabase(); } catch (DatabaseConnectionException e) { log.error("λ°μ΄ν°λ² μ΄μ€ μ°κ²°μ μ€ν¨νμ΅λλ€: {}", e.getMessage()); throw new ServerException("μλ² μ€λ₯κ° λ°μνμ΅λλ€. λμ€μ λ€μ μλν΄μ£ΌμΈμ."); }
-
μΈμ¦ μ€ν¨ μ€λ₯
-
μ¬μ©μμ μλͺ»λ μ격 μ¦λͺ , λΉμ μμ μΈ
AuthenticationManager
μ€μ , λλ μ¬μ©μμ λΉλ°λ²νΈκ° ν΄μλ ννλ‘ μ μ₯λμ§ μκ³ νλ¬ΈμΌλ‘ μ μ₯λ κ²½μ° λ±μΌλ‘ λ°μνλ λ¬Έμ μ΄λ€. -
μΈμ¦ μλ ν, "Authentication successful" λ‘κ·Έκ° μΆλ ₯λμ§ μκ³ , μΈμ¦ μ€ν¨λ‘ μΈν΄ μ¬μ©μλ λ‘κ·ΈμΈμ ν μ μκ² λλ€.
-
ν΄κ²° λ°©λ²
- μ¬μ©μμ μ΄λ©μΌκ³Ό λΉλ°λ²νΈκ° μ¬λ°λ₯΄κ² μ
λ ₯λμλμ§ νμΈνκ³ ,
AuthenticationManager
μUserDetailsService
κ° μ¬λ°λ₯΄κ² μ€μ λμλμ§ μ κ²ν΄μΌ νλ€. - λν λΉλ°λ²νΈκ° μ¬λ°λ₯Έ ν΄μ μκ³ λ¦¬μ¦μ μ¬μ©νμ¬ μ μ₯λμλμ§λ νμΈν΄μΌ νλ€.
-
log.info("Login attempt for user: {}", user.getEmail()); try { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( user.getEmail(), user.getPassword())); log.info("Authentication successful for user: {}", user.getEmail()); } catch (Exception e) { log.error("Authentication failed: {}", e.getMessage()); throw new BadCredentialsException("Invalid credentials"); }
- μ¬μ©μμ μ΄λ©μΌκ³Ό λΉλ°λ²νΈκ° μ¬λ°λ₯΄κ² μ
λ ₯λμλμ§ νμΈνκ³ ,
-
-
SecurityContextHolder μ€μ λ¬Έμ
-
μΈμ¦μ΄ μ±κ³΅νμμλ λΆκ΅¬νκ³
SecurityContextHolder
μ μΈμ¦ μ λ³΄κ° μ μ₯λμ§ μμμ μ΄νμ 보μ κ΄λ ¨ μμ μ΄ μ€ν¨νλ κ²½μ°κ° μλ€. -
보ν΅
SecurityContextHolder
λ₯Ό μ¬λ°λ₯΄κ² ꡬμ±νμ§ μμκ±°λ, μΈμ¦ ν μλ΅μ΄ λ°νλκΈ° μ μ 컨ν μ€νΈκ° μ΄κΈ°νλμμ λ λ°μν©λλ€. -
ν΄κ²° λ°©λ²
SecurityContextHolder.getContext().setAuthentication(authentication);
νΈμΆμ΄ μ¬λ°λ₯΄κ² μ΄λ£¨μ΄μ‘λμ§, κ·Έλ¦¬κ³ μ΄ νΈμΆ ν 컨ν μ€νΈκ° λ€λ₯Έ κ³³μμ μ΄κΈ°νλμ§ μμλμ§ νμΈν΄μΌ ν©λλ€.
-
-
λΉλκΈ° μ²λ¦¬μμμ μΈμ¦ λ¬Έμ
-
λΉλκΈ° μμ² μ²λ¦¬ μ€ μΈμ¦ μ λ³΄κ° μ λλ‘ μ λ¬λμ§ μκ±°λ μμ€λλ κ²½μ°κ° μλ€.
-
μλ₯Ό λ€μ΄, μ¬μ©μκ° λ°±κ·ΈλΌμ΄λμμ μ€νλλ μμ μ μμ²νμ λ, μΈμ¦λ μ¬μ©μμΈμ§ νμΈν μ μμ΄ μμ²μ΄ μ€ν¨ν μ μλ€.
-
Spring Security
λ κΈ°λ³Έμ μΌλ‘μ€λ λ λ‘컬(ThreadLocal)
μ μ¬μ©νμ¬SecurityContext
λ₯Ό κ΄λ¦¬νλ€. -
λ°λΌμ λΉλκΈ° νκ²½μμλ μΈμ¦ μ λ³΄κ° μ€λ λ κ°μ μ¬λ°λ₯΄κ² μ λ¬λμ§ μμ μ μμ΅λλ€.
-
ν΄κ²° λ°©λ²
-
λΉλκΈ° νκ²½μμ
SecurityContext
λ₯Ό μ μ§νλ €λ©΄@Async
λ©μλ λλExecutor
λ₯Ό μ¬μ©ν λSecurityContext
λ₯Ό λͺ μμ μΌλ‘ μ λ¬νλ λ°©μμΌλ‘ μ€μ ν΄μΌ νλ€. -
μλ₯Ό λ€μ΄
DelegatingSecurityContextExecutor
λ₯Ό μ¬μ©ν μ μλ€. -
@Bean public Executor taskExecutor() { return new DelegatingSecurityContextExecutor(new SimpleAsyncTaskExecutor()); }
-
-
보μ λ² μ€νΈ νλν°μ€ [^3] [^4]
보μμ±μ λμ΄κΈ° μν΄ κ°λ°μλ λͺ¨λ² μ¬λ‘λ₯Ό μ€μν΄μΌ νλ€.
-
νμΈλ μμ ν λΌμ΄λΈλ¬λ¦¬ μ¬μ©
- νμ μ΅κ·Ό λ²μ μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ μ§νκ³ μλ €μ§ μ·¨μ½μ μ μ£ΌκΈ°μ μΌλ‘ μ κ²ν΄μΌν νμμ±μ΄ μλ€.
-
HTTP 보μ ν€λ μ€μ
- κΈ°λ³Έ HTTP ν€λ μΈ μΆκ° ν€λ κ°μ ν΅ν΄ μ ν리μΌμ΄μ μ λμ± μμ νκ² λ³΄νΈν μ μλ€.
- μλ₯Ό λ€μ΄,
Content-Security-Policy
ν€λλ₯Ό μ¬μ©νλ©΄ μΉ νμ΄μ§μμ μ€νλ μ μλ μλ°μ€ν¬λ¦½νΈμ μΆμ²λ₯Ό μ νν μ μλ€. -
http.headers() .contentSecurityPolicy("script-src 'self'") .and() .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.NO_REFERRER);
-
λ°μ΄ν° λͺ λ Ήμ΄ λΆλ¦¬
- λ°μ΄ν°λ₯Ό μ²λ¦¬νλ λΆλΆκ³Ό λͺ λ Ήμ΄ μν λΆλΆμ μ² μ ν λΆμ°νμ¬ SQL μ£Όμ 곡격과 κ°μ 보μ μνμ μλ°©ν΄μΌ νλ€.
- μλ₯Ό λ€μ΄, SQL 쿼리λ₯Ό μ§μ μμ±νλ λμ ,
PreparedStatement
λ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ₯Ό μμ νκ² μ²λ¦¬ν μ μμ΅λλ€. -
String query = "SELECT * FROM users WHERE email = ?"; PreparedStatement statement = connection.prepareStatement(query); statement.setString(1, userEmail); ResultSet resultSet = statement.executeQuery();
κ²°λ‘
μ€νλ§(Authentication) κΈ°μ μ΄ μ΄λ»κ² μλνλ©° μ°λ¦¬κ° λ§μ£ΌμΉ μ μλ λ¬Έμ λ€ κ·Έλ¦¬κ³ κ·Έ ν΄κ²° λ°©μμ μ΄ν΄λ³΄μλ€.
κ° μ£Όμ λ₯Ό μ’ ν©μ μΌλ‘ κ³ λ €νμ¬ μμ μ μ΄κ³ ν¨μ¨μ μΈ μ½λλ₯Ό μμ±νλ©΄ μ’κ² λ€.
[^1] Marco Behler - Spring Security: Authentication and Authorization In-Depth (https://www.marcobehler.com/guides/spring-security) [^2] Medium - Securing Spring Boot Applications: Best Practices and ... (https://medium.com/@shubhamvartak01/securing-spring-boot-applications-best-practices-and-strategies-3ab731f8b317) [^3] Spring - Getting Started | Securing a Web Application (https://spring.io/guides/gs/securing-web) [^4] Synopsys - Top 10 Spring Security Best Practices for Java Developers (https://www.synopsys.com/blogs/software-security/spring-security-best-practices.html)