[Security] Security-basic
Categories: Spring
๐ ๊ฐ์ธ์ ์ธ ๊ณต๊ฐ์ผ๋ก ๊ณต๋ถ๋ฅผ ๊ธฐ๋กํ๊ณ ๋ณต์ตํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ธ๋ก๊ทธ์
๋๋ค.
์ ํํ์ง ์์ ์ ๋ณด๊ฐ ์์ ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค :๐ธ
[ํ๋ฆฐ ๋ด์ฉ์ ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๋ฉด ๋ณต๋ฐ์ผ์ค๊ฑฐ์์]
Spring Security ๋?
์ ํ๋ฆฌ์ผ์ด์ ์ Security๋ฅผ ์ ์ฉํ์ง ์์ผ๋ฉด
- ๋ก๊ทธ์ธ ๊ธฐ๋ฅ (์ธ์ฆ,Authentication)์ด ์์.
-
API ์ ๋ํ ๊ถํ ๋ถ์ฌ(์ธ๊ฐ, Athorization)๊ธฐ๋ฅ์ด ์์.
์ธ์ฆ๊ณผ ์ธ๊ฐ ์ค์ ๋ญ๊ฐ ๋จผ์ ์งํ? โ ์ธ์ฆ์ด ๋จผ์ โก๏ธ ๊ทธ๋ค์ ์ธ๊ฐ์ฒ๋ฆฌ๋ฅผ ํ๊ณ ๋ณด๋
- ์น ๋ณด์ ์ทจ์ฝ์ ์ ๋ํ ๋๋น๊ฐ ์ ํ ์ด๋ฃจ์ด์ง์ง ์์์,
์ฆ, ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํํ๋ ์ธ์ ๊ณ ์ ๊ณต๊ฒฉ, ํด๋ฆญ์ฌํน ๊ณต๊ฒฉ, CSRF ๋ฑ์ ๋ณด์ ์ทจ์ฝ์ ์ ๋ํ
๊ณ ๋ ค๊ฐ ์ ํ ์ด๋ฃจ์ด์ง์ง ์์ ์ํ์ด๋ค.
โ๏ธ Spring Security
Spring MVC ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ธ์ฆ(Authentication)๊ณผ ์ธ๊ฐ(Authorization or ๊ถํ ๋ถ์ฌ) ๊ธฐ๋ฅ์ ์ง์ํ๋ ๋ณด์ ํ๋ ์์ํฌ๋ก์จ,</br> Spring MVC ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์์ ์ ์ฉํ๊ธฐ ์ํ ์ฌ์ค์์ ํ์ค
Spring์์ ์ง์ํ๋ Interceptor๋ Servlet Filter๋ฅผ ์ด์ฉํด์ ๋ณด์ ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํํ ์ ์์ง๋ง
์น ์ ํ๋ฆฌ์ผ์ด์
๋ณด์์ ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ Spring Security์์ ์์ ์ ์ผ๋ก ์ง์ํ๊ณ ์์ผ๋ฏ๋ก
๊ตฌ์กฐ์ ์ผ๋ก ์ ๋ง๋ค์ด์ง ๊ฒ์ฆ๋ Spring Security๋ฅผ ์ด์ฉํ๋ ๊ฒ์ด ์์ ํ ์ ํ
โ๏ธ Interceptor์ Filter
- interceptor๋ ์ปจํธ๋กค๋ฌ ๋ฉ์๋๋ก ๋์ด๊ฐ๊ธฐ ์ง์ ์ ๊ฐ๋ก์ฑ์ ์ฒ๋ฆฌํ๊ณ ๋ณด๋ด์ค. (๋ณด์๊ด๋ จ ์ด์์ฒ๋ฆฌ, ์ธ์ฆ-์ธ๊ฐ)
- ๊ทธ๋ฐ๋ฐ interceptor ์ฌ์ฉ ์ํ๊ณ filter๋ฅผ ์ฌ์ฉํ ๊ฒ.
(์๋๋ฉด ๋ณด์์ ์ด๊ธฐ ๋จ๊ณ์์ ์์ฒญ๋์ผ ํ๋๋ฐ filter ๊ฐ ๋ ์ผ์ฐ ์์ฒญ์ ๊ฐ๋ก์ฑ์ด์ ์ฒ๋ฆฌ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.-์ ์ญ์์ ์ ์ฉ๊ฐ๋ฅ) - filter๋ ๋ธ๋ผ์ฐ์ ์์ ๋์คํจ์ณ ์๋ธ๋ฆฟ์ผ๋ก ๋์ด๊ฐ๊ธฐ ์ง์ ์ ์ฒ๋ฆฌํ๋ค.
- ๋ณด์ ๊ด๋ จ ์์ ์ ์ํด Spring Security์์๋ ์ฃผ๋ก Filter๊ธฐ๋ฐ์ผ๋ก ์ฌ์ฉํ๋ค.
- Spring Security์ ํํฐ๊ฐ ๊ณผ๊ฑฐ์๋ DispatcherServlet ์ ์ ๋์ํ๋ฉด์ Spring ์ปจํ ์ด๋์ ๋น๋ค์ ์ง์ ํ์ฉํ๊ธฐ ์ด๋ ค์ ์ง๋ง, ํ์ฌ๋ ์ด๋ฌํ ์ ์ฝ์ด ํด๊ฒฐ๋์ด ํํฐ๋ Spring์ ๋น์ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์๋ค.
- 6๋ฒ์ ๋ถํฐ๋ ๋๋ค์์ ์ฌ์ฉ , ์์ง 6๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ณณ์ด ๋ง์ง ์์์ 5.8๋ฒ์ ์ผ๋ก ๊ณต๋ถํ ์์ .
โ๏ธ Spring Security๋ก ํ ์ ์๋ ๋ณด์ ๊ฐํ ๊ธฐ๋ฅ
Spring Security๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ผ๋ค์ ํ ์ ์๋ค.
- ๋ค์ํ ์ ํ(ํผ ๋ก๊ทธ์ธ ์ธ์ฆ, ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ, OAuth 2 ๊ธฐ๋ฐ ์ธ์ฆ, LDAP ์ธ์ฆ)์ ์ฌ์ฉ์ ์ธ์ฆ ๊ธฐ๋ฅ ์ ์ฉ
- ์ ํ๋ฆฌ์ผ์ด์ ์ฌ์ฉ์์ ์ญํ (Role)์ ๋ฐ๋ฅธ ๊ถํ ๋ ๋ฒจ ์ ์ฉ
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ ๊ณตํ๋ ๋ฆฌ์์ค์ ๋ํ ์ ๊ทผ ์ ์ด
- ๋ฏผ๊ฐํ ์ ๋ณด์ ๋ํ ๋ฐ์ดํฐ ์ํธํ
- SSL ์ ์ฉ
- ์ผ๋ฐ์ ์ผ๋ก ์๋ ค์ง ์น ๋ณด์ ๊ณต๊ฒฉ ์ฐจ๋จ
์ด ์ธ์๋ SSO, ํด๋ผ์ด์ธํธ ์ธ์ฆ์ ๊ธฐ๋ฐ ์ธ์ฆ, ๋ฉ์๋ ๋ณด์, ์ ๊ทผ ์ ์ด ๋ชฉ๋ก(Access Control List) ๊ฐ์ ๋ณด์์ ์ํ ๊ธฐ๋ฅ๋ค์ ์ง์ํ๋ค.
โ๏ธ Spring Security์์ ์ฌ์ฉํ๋ ์ฉ์ด ์ ๋ฆฌ
- Principal(์ฃผ์ฒด) โ ๋๊ฐ(who?)๋ผ๊ณ ์๊ฐํ๋ฉด ๋จ.
- Spring Security์์ ์ฌ์ฉ๋๋
Principal
์ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์์ ์ ์ํํ ์ ์๋ ์ฌ์ฉ์, ๋๋ฐ์ด์ค ๋๋ ์์คํ ๋ฑ์ด ๋ ์ ์์ผ๋ฉฐ,
์ผ๋ฐ์ ์ผ๋ก ์ธ์ฆ ํ๋ก์ธ์ค๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ํ๋ ์ฌ์ฉ์์ ๊ณ์ ์ ๋ณด๋ฅผ ์๋ฏธํ๋ค. - ์ ์ ์ฆ๋ช ์ด๋์ ๋ณ๊ฐ โ ์ด๊ฑด credential์ด๋ผ๊ณ ํจ .
- Spring Security์์ ์ฌ์ฉ๋๋
- Authentication(์ธ์ฆ)
- ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉํ๋ ์ฌ์ฉ์๊ฐ ๋ณธ์ธ์ด ๋ง์์ ์ฆ๋ช ํ๋ ์ ์ฐจ๋ฅผ ์๋ฏธ
- ์ ์์ ์ผ๋ก ์ํํ๊ธฐ ์ํด์๋ ์ฌ์ฉ์๋ฅผ ์๋ณํ๊ธฐ ์ํ ์ ๋ณด๊ฐ ํ์ํ๋ฐ ์ด๋ฅผ
Credential
(์ ์ ์ฆ๋ช ์ ๋ณด)์ด๋ผ๊ณ ํ๋ค. Credential
์ ์ผ์์์ ๋ํ์ ์ธ ์๋ก ์ฃผ๋ฏผ๋ฑ๋ก์ฆ, ๊ทธ๋ฆฌ๊ณ ํน์ ์ฌ์ดํธ์์ ๋ก๊ทธ์ธ์ ์ํด ์ ๋ ฅํ๋ ํจ์ค์๋๋ ๋ก๊ทธ์ธ ์์ด๋๋ฅผ ์ฆ๋ช ํ๊ธฐ์ํ Credential์ด๋ค.
- Authorization(์ธ๊ฐ ๋๋ ๊ถํ ๋ถ์ฌ)
- Authentication์ด ์ ์์ ์ผ๋ก ์ํ๋ ์ฌ์ฉ์์๊ฒ ํ๋ ์ด์์ ๊ถํ(authority)์ ๋ถ์ฌํ์ฌ
ํน์ ์ ํ๋ฆฌ์ผ์ด์ ์ ํน์ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๊ฒ ํ๊ฐํ๋ ๊ณผ์ ์ ์๋ฏธํ๋ค. - ๋ฐ๋์ Authentication ๊ณผ์ ์ดํ ์ํ๋์ด์ผ ํ๋ฉฐ ๊ถํ์ ์ผ๋ฐ์ ์ผ๋ก ์ญํ (Role) ํํ๋ก ๋ถ์ฌ๋๋ค.
- Authentication์ด ์ ์์ ์ผ๋ก ์ํ๋ ์ฌ์ฉ์์๊ฒ ํ๋ ์ด์์ ๊ถํ(authority)์ ๋ถ์ฌํ์ฌ
- Access Control(์ ๊ทผ ์ ์ด)
- ์ฌ์ฉ์๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ํ์๋ฅผ ์ ์ดํ๋ ๊ฒ์ ์๋ฏธ
โ๏ธ Spring Security๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ์ด์
- ๊ฐ์ฅ ํฐ ์ด์ ๋ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํ ์๋ฃจ์
์ผ๋ก Spring Security ๋ง ํ ๋ค๋ฅธ ํ๋ ์์ํฌ๊ฐ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ
Spring Security๊ฐ ๋ ์ธ๊ฐ์ง ๋ณด์ ํ๋ ์์ํฌ๋ฅผ ๋ถ์ธ ๊ฒ๋ณด๋ค ๋ ๋์ ๊ฒฝ์ฐ๊ฐ ๋๋ค์. - Spring Security๋ ํน์ ๋ณด์ ์๊ตฌ ์ฌํญ์ ๋ง์กฑํ๊ธฐ ์ํ ์ปค์คํฐ๋ง์ด์ง์ด ์ฉ์ดํ๊ณ , ์ ์ฐํ ํ์ฅ์ด ๊ฐ๋ฅํ๋ค. โ ์ปค์คํฐ๋ง์ด์ง์ด ์ด๋ ค์ด ๊ฒฝ์ฐ๊ฐ ๋ง์.
Spring Security์ ์ธ์ฆ์ฒ๋ฆฌ ํ๋ฆ
SSR๋ก ๊ตฌํ, ์์ํ ๊ฐ์ ํด ๋๊ฐ ์์
โ๏ธ SSR๊ณผ CSR
- SSR(Server Side Rendering) ๋ฐฉ์์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ธ์
๊ธฐ๋ฐ์ ํผ ๋ก๊ทธ์ธ ๋ฐฉ์์ ์ ์ฉํ๊ธฐ์
๊ฐ์ฅ ์ ํฉํ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ฉฐ,
๋ํ ํผ ๋ก๊ทธ์ธ ๋ฐฉ์์ Spring Security์ ์ฒ์ ์ ๋ฌธํ๋ ์ฌ๋๋ค์ด ์ดํดํ๊ธฐ์ ๊ฐ์ฅ ์ ํฉํ ์ธ์ฆ ๋ฐฉ์
โญ CSR(Client Side Rendering) ๋ฐฉ์์ผ๋ก ์ด๋๋์ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌํ ํด์์ง๋ง,
Spring Security๋ฅผ ์ ๋ฌธํ๊ธฐ ์ํด SSR ๋ฐฉ์ ์ฌ์ฉ
โ๏ธ Hello Spring Security ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์กฐ
HTML ๋ทฐ๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด ํ์๋ฆฌํ(Thymeleaf)๋ผ๋ ํ ํ๋ฆฟ ์์ง์ ์ฌ์ฉ
-
Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<html xmlns:th="<http://www.thymeleaf.org>" xmlns:sec="<http://www.thymeleaf.org/thymeleaf-extras-springsecurity5>"> <!-- (1) --> <body> <div align="right" th:fragment="header"> <a href="/members/register" class="text-decoration-none">ํ์๊ฐ์ </a> | <span sec:authorize="isAuthenticated()"> <!-- (2) ์ธ์ฆ์ด ๋์์ผ๋ฉด ๋ณด์ฌ์ง๋ ๊ฒ๋ค. --> <span sec:authorize="hasRole('USER')"> <!-- (3) --> <a href="/members/my-page" class="text-decoration-none">๋ง์ดํ์ด์ง</a> | </span> <a href="/logout" class="text-decoration-none">๋ก๊ทธ์์</a> <!-- (4) --> <span th:text="${#authentication.name}">ํ๊ธธ๋</span>๋ <!-- (5) --> </span> <span sec:authorize="!isAuthenticated()"> <!-- (6) ์ธ์ฆ์ด ๋์ง ์์์ผ๋ฉด --> <a href="/auths/login-form" class="text-decoration-none">๋ก๊ทธ์ธ</a> </span> </div> </body> </html>
-
ํ์๊ฐ์ HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
<!DOCTYPE html> <html xmlns:th="<http://www.thymeleaf.org>" xmlns:layout="<http://www.ultraq.net.nz/thymeleaf/layout>" layout:decorate="layouts/common-layout"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello Spring Security Coffee Shop</title> </head> <body> <hr /> <div class="container" layout:fragment="content"> <!-- (1) ํ์ ๊ฐ์ ํผ --> <form action="/members/register" method="post"> <div class="row"> <div class="col-xs-2"> <input type="text" name="fullName" class="form-control" placeholder="User Name"/> </div> </div> <div class="row" style="margin-top: 20px"> <div class="col-xs-2"> <input type="email" name="email" class="form-control" placeholder="Email"/> </div> </div> <div class="row" style="margin-top: 20px"> <div class="col-xs-2"> <input type="password" name="password" class="form-control" placeholder="Password"/> </div> </div> <button class="btn btn-outline-secondary" style="margin-top: 20px">ํ์ ๊ฐ์ </button> </form> </div> </body> </html>
-
๋ก๊ทธ์ธ
๋ก๊ทธ์ธ ํ๋ฉด ์ญ์ HTML form ํ๊ทธ๋ก ๊ตฌ์ฑ์ด ๋์ด ์์ผ๋ฉฐ,
HTML์ form ๋ฐฉ์์ผ๋ก ๋ก๊ทธ์ธ ์ธ์ฆ์ ์งํํ๊ธฐ ๋๋ฌธ์
Spring Security์์๋ ์ด๋ฌํ ์ธ์ฆ ๋ฐฉ์์ ํผ ๋ก๊ทธ์ธ ์ธ์ฆ -
์๋ธ์ฌ์ด๋๋ ๋๋ง ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด๋ฅผ ๋ณด๋ด์ง ์๋๋ค.
๋ฌธ์ ์
-
๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ด ๊ตฌ์ฒด์ ์ผ๋ก ๊ตฌํ๋์ด ์์ง ์์. โ ์ธ์ฆ, ์ธ๊ฐ๋ฅผ ๊ตฌํํด์ผ ํจ,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
package com.springboot.auth; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/auths") public class AuthController { @GetMapping("/login-form") public String loginForm() { return "login"; } @GetMapping("/access-denied") public String accessDenied() { return "access-denied"; } // (1) @PostMapping("/login") public String login() { System.out.println("Login successfully!"); return "home"; } }
๋ก๊ทธ์ธ ์ธ์ฆ์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง์ง ์๊ธฐ ๋๋ฌธ์ ์ปคํผ๋ฅผ ์ฃผ๋ฌธํ๋ ํ์์ด๋ , ๋งค์ฅ์์ ์ปคํผ๋ฅผ ๋ง๋ค์ด์ ์ ๊ณตํ๋ ์นดํ ๊ด๊ณ์์ด๋ ์๊ด์์ด ๋ชจ๋ ํ๋ฉด์ ์์ ๋กญ๊ฒ ์ ๊ทผํ ์ ์๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ Spring Security ์ ์ฉ
-
gradle์ ์ฃผ์์ฒ๋ฆฌ ๋์ด์๋ ์ธ์ฆ์ธ๊ฐ๋ฅผ ํด์ ํ์ฌ ์์กด์ฃผ์ ํ๋ค.
-
์๋ HomeController์์ ๊ธฐ๋ณธ url๋ก ์ด๋์ home์ผ๋ก ๊ฐ๊ฒ ์ค์ ๋์ด์๋๋ฐ
์คํ๋ง ์ํ๋ฆฌํฐ๋ฅผ ์ ์ฉํ๋ฉด ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํด์ฃผ๋ ๊ธฐ๋ฅ์ ์ฒซ ํ ํ๋ฉด์ด ๋ก๊ทธ์ธ ํ๋ฉด์ผ๋ก ๋ฐ๋๋ค. -
๋ก๊ทธ์ธํ ๋ ์์ด๋๋ ๊ธฐ๋ณธ์ผ๋ก user์ด๊ณ ๋น๋ฐ๋ฒํธ๋ ํ๋ก๊ทธ๋จ ์คํ์ ์ ๊ณต๋๋ค(๋งค๋ฒ ๋ฌ๋ผ์ง)
-
๋ด ๋ง์๋๋ก ์์ด๋์ ๋น๋ฐ ๋ฒํธ๋ฅผ ์ ์ผ๋ฉด ์คํจ๊น์ง ์๋ ค์ค๋ค.
- ์ค๋ฌด์ ์ ์ฉํ๊ธฐ ํ๋ฌ โ ์ ์ํ ๋๋ง๋ค ๋น๋ฐ๋ฒํธ๊ฐ ๋ฐ๋๊ฒ ๋๊ณ , ๋ก๊ทธ์ธ ํด์ ๋ค์ด๊ฐ๋ฉด ๋ก๊ทธ์ธ ํ๋ฉด์ด ๋ ์๋ค(๋ด๊ฐ ๊ตฌํํ ๊ฒ)
- ์คํ๋ง์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํด์ฃผ๋ ์ฒซ ํ๋ฉด์ ๋ก๊ทธ์ธํ๋ฉด๊ณผ ๋ด๊ฐ ๊ตฌํํ ๋ก๊ทธ์ธ ํ๋ฉด ๋ ๊ฐ๊ฐ ์ค๋ณตํด์ ์๊ธฐ๊ฒ ๋จ.
1๏ธโฃ ๋น๋ฐ๋ฒํธ ์ค์ ๋ณ๊ฒฝ
โญ Spring Security Configuration ์ ์ฉ โ ํด๋์ค์์ Spring Security์์ ์ง์ํ๋ ์ธ์ฆ๊ณผ ๊ถํ ๋ถ์ฌ ์ค์ ํ๊ธฐ
- ํ๋ก๊ทธ๋จ ์ค์ ์ ์์ฑ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์๋
- ๋งค๋ฒ ๋น๋ฐ๋ฒํธ๊ฐ ๋ฐ๋๋ ๊ฒ์ ๋ค์ ์ค์ ํ ์์ , db๋ ์ธ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ
SecurityConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
@Configuration //Configuration๊ณผ ๋น์ด ์์ผ๋ฉด ๋ฐํํ๋ ๊ฐ์ฒด๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๊ฒ ๋จ.
public class SecurityConfiguration {
@Bean //์คํ๋ง ์ปจํ
์ด๋์ ๋์์ ๋ฐ๊ธฐ ์ํจ
public UserDetailsManager userDetailService(){
UserDetails userDetails =
User.withDefaultPasswordEncoder()
.username("jerry@gmail.com") //์ผ๋ฐ์ ์ผ๋ก ํต์ฉ๋๋ ์์ด๋์ ๊ฐ๋
.
.password("wpfl")
.roles("USER") //์ธ๊ฐ : ๊ถํ์ ๋ญ ์ค ๊ฒ์ธ์ง?
.build(); // 3๊ฐ์ง๋ฅผ ๋ด๊ณ ๋น๋ํ๋ฉด
return new InMemoryUserDetailsManager(userDetails);
//์ ์ ๊ฐ์ฒด๊ฐ ๋์จ๋ค.
// ๋ฉ์๋๋ช
์ ๋ณด๋ฉด UserDetalis๋ฅผ ๊ด๋ฆฌํ๋ ์ญํ ์ ํ๋ ๋งค๋์ ๋ก ๋ฐํํด์ผ ํ๋๋ฐ
// ์ธ๋ฉ๋ชจ๋ฆฌ ๋งค๋์ ๋ก ๋ฐํํจ. ์ธํฐํ์ด์ค๋ก ๊ตฌํ๋์ด์๊ณ ์ถ์ํ ๋์ด์๊ธฐ ๋๋ฌธ
// ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ค์ ๋ก ์ฌ์ฉํ์ง ์๊ณ ์๊ธฐ ๋๋ฌธ์ ์ธ๋ฉ๋ชจ๋ฆฌDB์ ๊ตฌํ์ฒด๋ก ์ฌ์ฉ
// ๋น์ userDetailService๋ผ๋ ๋ฉ์๋ ๋ช
์ผ๋ก ๋ฑ๋กํ๊ฒ ๋๋ค.
}
}
- UserDetails ์ธํฐํ์ด์ค๋ ์ธ์ฆ๋ ์ฌ์ฉ์์ ํต์ฌ ์ ๋ณด๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉฐ,
UserDetails ๊ตฌํ์ฒด์ธ User ํด๋์ค๋ฅผ ์ด์ฉํด์ ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์์ฑํ๋ค.-
UserDetails
-
User
-
์ฌ๊ธฐ์ username์ ์ฌ๋์ ์ด๋ฆ์ด ์๋ ๊ณ ์ ํ ์ฌ์ฉ์๋ฅผ ์๋ณํ ์ ์๋ ์ฌ์ฉ์ ์์ด๋ ๊ฐ์ ๊ฐ
-
User class์ ์๋ static Method โ withDefaultPasswordEncorder( )
- withDefaultPasswordEncorder( ) ๋ก ์ธํด ์ํธํ ๋๋ค.
API ๋ฌธ์์์ ํด๋น ๋ฉ์๋์๋ ๋์ด์ ์ฌ์ฉํ์ง๋ง๋ผ๋ @Deprecated ์ด ๋ฌ๋ ค์์ง๋งโ ์ค์ ์๋น์คํ๊ฒฝ์์๋ ์ฌ์ฉํ์ง ๋ง๋ผ๋ ์๋ฏธ๋ก ์ ๋ํ ์ด์ ์ด ๋ฌ๋ ค์๋ ๊ฒ - ์ง๊ธ ์ฐ์ต๋จ๊ณ๋ผ์ ์๋ฏธ ๋ ํ์ ์๊ณ ํ๋์ฉ ๊ฐ์ ์์
-
-
- ๋ฐํ์ UserDetailsManager๋ก ํด์ผํ๋๋ฐ, InMemoryUserDetailsManager๋ก ๋ฐํํจ.
-
์ธํฐํ์ด์ค๋ก ๊ตฌํ๋์ด์๊ณ ์ถ์ํ ๋์ด์๊ธฐ ๋๋ฌธ
-
2๏ธโฃ ์ปค์คํ ๋ก๊ทธ์ธ ํ์ด์ง ์ฌ์ฉ, ํผ๋ก๊ทธ์ธ ๊ตฌํ (HTTP ๋ณด์ ๊ตฌ์ฑ)
SecurityConfiguration์ filterChain ๋ฉ์๋๋ฅผ ์์ฑ
โ HttpSecurity๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ง๊ณ , SecurityFilterChain์ ๋ฆฌํดํ๋ ํํ์ ๋ฉ์๋๋ฅผ ์ ์ํ๋ฉด HTTP ๋ณด์ ์ค์ ์ ๊ตฌ์ฑํ ์ ์๋ค
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//httpsecurity : ์น๋ณด์์ ์ํด์ ์ฌ์ฉ๋๋ ํด๋์ค
//๋ก๊ทธ์ธ์ด ๋ ์์ผ๋๊น ๋ด๊ฐ ๊ตฌํํ ๋ก๊ทธ์ธํ์ด์ง๋ก ์ฌ์ฉํ๋๋ก ํ๊ฒ ๋ณ๊ฒฝํ๊ธฐ ์ํด์ ๋ฉ์๋๋ง๋ฌ
//ํผ๋ก๊ทธ์ธ์ผ๋ก ๊ตฌํ๋์ด ์๊ธฐ ๋๋ฌธ์ ํผ๋ก๊ทธ์ธ์ผ๋ก ์ฌ์ฉ์์
//(1)
//ํ
์คํธ ํ๊ฒฝ์์ DISABLE ์ฌ์ฉ ์ฒ๋ฆฌ,
// **CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ์ ๋ํ Spring Security์ ๋ํ ์ค์ ์ ๋นํ์ฑํ**
http.csrf().disable()
.formLogin() //(2)
.loginPage("/auths/login-form") //(3)
//๋ก๊ทธ์ธํ์ด์ง ์ฌ์ฉํ๋ ์ค์
.loginProcessingUrl("process_login")
//๋ก๊ทธ์ธ์ ์ค์ ๋ก ์ํํ ์ฃผ์ (์ธ์ฆ)
.failureUrl("/atuhs/login-form?error")
//์ค์ ๋ก๊ทธ์ธ์ด ์คํจํ์ ๊ฒฝ์ฐ ์ด๋ค ํ์ด์ง๋ฅผ ๋ณด๋ผ ๊ฑด์ง. ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฑด์ง ์ค
.and()
.authorizeHttpRequests() //ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์ ๊ทผ ๊ถํ์ ํ์ธ
.anyRequest()
.permitAll();
//ํด๋ผ์ด์ธํธ์ ๊ถํ์ ๋ณผ๊ฑด๋ฐ ์ ์ฒด ๋ค ๊ฐ๋ฅ
return http.build();
}
- (1) ์์ CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ์ ๋ํ Spring Security์ ๋ํ ์ค์ ์ ๋นํ์ฑํ
- Spring Security๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฌด ์ค์ ์ ํ์ง ์์ผ๋ฉด csrf() ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํด๋ผ์ด์ธํธ๋ก๋ถํฐ CSRF Token์ ์์ ํ, ๊ฒ์ฆํ๋ค.
- but ์ฐ๋ฆฌ๋ ๋ก์ปฌํ๊ฒฝ์ด๋ผ์ ๊ณต๊ฒฉ์ ๋ํ ์ค์ ์ด ๋ฐ๋ก ํ์ํ์ง ์์
- (2) ์์ ๊ธฐ๋ณธ์ ์ธ ์ธ์ฆ ๋ฐฉ๋ฒ์ ํผ๋ก๊ทธ์ธ ๋ฐฉ์์ผ๋ก ์ง์
- (3)์ .loginPage(โ/auths/login-formโ) ์์ ํด๋น ํ๋ผ๋ฏธํฐ์ URL์ AuthController์ loginForm() ํธ๋ค๋ฌ ๋ฉ์๋์ ์์ฒญ์ ์ ์กํ๋ URL์ด๋ค.
- ๋ง์ฐฌ๊ฐ์ง๋ก loginProcessingUrl(โ/process_loginโ) ๋ฉ์๋๋ฅผ ํตํด ๋ก๊ทธ์ธ ์ธ์ฆ ์์ฒญ์ ์ํํ ์์ฒญ URL์ ์ง์
โ โ/process_loginโ์ ์ฐ๋ฆฌ๊ฐ ๋ง๋ค์ด ๋ login.html์์ form ํ๊ทธ์ action ์์ฑ์ ์ง์ ํ URL๊ณผ ๋์ผ- ์ปค์คํ
๋ก๊ทธ์ธ ํ๋ฉด์์ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ๋ฉด
formํ๊ทธ์ action์์ฑ์ ์ง์ ๋ /process_login URL๋ก ์ฌ์ฉ์ ์ธ์ฆ์ ์ํ ์ ๋ณด๋ฅผ ์ ์ก(์ด๋ฉ์ผ๊ณผ ํจ์ค์๋)
- ์ปค์คํ
๋ก๊ทธ์ธ ํ๋ฉด์์ ๋ก๊ทธ์ธ ๋ฒํผ์ ํด๋ฆญํ๋ฉด
3๏ธโฃ ์ปค์คํ ๋ก๊ทธ์ธ ํ์ด์ง(login.html)์ ์ธ์ฆ์คํจ ๋ฉ์ธ์ง ์ถ๊ฐ
${param.error}
์ ๊ฐ์ ํตํด ๋ก๊ทธ์ธ ์ธ์ฆ ์คํจ ๋ฉ์์ง ํ์ ์ฌ๋ถ๋ฅผ ๊ฒฐ์
${param.error}
๋ Spring Security Configuration์์
failureUrl(โ/auths/login-form?errorโ)์ ?error ๋ถ๋ถ์ ํด๋นํ๋ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์๋ฏธ
1
2
3
<div class="row alert alert-danger center" role="alert" th:if="${param.error != null}">
<div>๋ก๊ทธ์ธ ์ธ์ฆ์ ์คํจํ์ต๋๋ค.</div>
</div>
4๏ธโฃ ์์ฒญ URI์ ๋ํ ์ ๊ทผ ๊ถํ ๋ถ์ฌ
SecurityConfiguration์์ filterChain๋ฉ์๋ ์์
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/auths/login-form")
.loginProcessingUrl("/process_login")
.failureUrl("/auths/login-form?error")
.and()
.exceptionHandling().accessDeniedPage("/auths/access-denied")
.and()
.authorizeHttpRequests(authorize -> authorize
.antMatchers("/orders/**").hasRole("ADMIN")
.antMatchers("/members/my-page").hasRole("USER")
.antMatchers("/**").permitAll()
);
return http.build();
}
2๏ธโฃ์์ ๊ตฌํ๋ filterChain()์์ .authorizeHttpRequests().anyRequest().permitAll(); ์ค์ ์ ํตํด ๋ก๊ทธ์ธ ์ธ์ฆ์ ์ฑ๊ณตํ ๊ฒฝ์ฐ,
๋ชจ๋ ํ๋ฉด์ ์ ๊ทผํ ์ ์๋๋ก ํ๋ ๋ถ๋ถ์ ์ฌ์ฉ์์ Role ๋ณ๋ก request URI์ ์ ๊ทผ ๊ถํ์ด ๋ถ์ฌ๋๋๋ก ์์
- exceptionHandling().accessDeniedPage(โ/auths/access-deniedโ)๋ฅผ ํตํด
๊ถํ์ด ์๋ ์ฌ์ฉ์๊ฐ ํน์ request URI์ ์ ๊ทผํ ๊ฒฝ์ฐ ๋ฐ์ํ๋ 403(Forbidden) ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ์ด์ง๋ฅผ ์ค์ - exceptionHandling () : Exception ์ฒ๋ฆฌ๋ฅผ ์ํ
- ๋ฆฌํดํ๋ ExceptionHandlingConfigure ๊ฐ์ฒด๋ฅผ ํตํด ๊ตฌ์ฒด์ ์ธ Exception ์ฒ๋ฆฌ๊ฐ๋ฅ
- accessDeniedPage() ๋ฉ์๋๋ 403 ์๋ฌ ๋ฐ์ ์, ํ๋ผ๋ฏธํฐ๋ก ์ง์ ํ URL๋ก ๋ฆฌ๋ค์ด๋ ํธ ๋๋๋ก ํด์ค๋ค.
- authorizeHttpRequests() ๋ฉ์๋์์ ๋๋ค์์ผ๋ก request URI์ ๋ํ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌ
antMatchers()
๋ฉ์๋๋ ์ด๋ฆ ๊ทธ๋๋ก ant๋ผ๋ ๋น๋ ํด์์ ์ฌ์ฉ๋๋Path Pattern
์ ์ด์ฉํด์ ๋งค์น๋๋ URL์ ํํ**
๋ /orders๋ก ์์ํ๋ ๋ชจ๋ ํ์ URL์ ํฌํจorders/*
๋ผ๋ URL์ ์ง์ ํ๋ค๋ฉด/orders/1
๊ณผ ๊ฐ์ด /orders์ ํ์ URL์ depth๊ฐ 1์ธ URL๋ง ํฌํจantMatchers("/members/my-page").hasRole("USER")
์USER
Role์ ๋ถ์ฌ๋ฐ์ ์ฌ์ฉ์๋ง
/members/my-page
URL์ ์ ๊ทผํ ์ ์๋ค..antMatchers("/**").permitAll()
์ ์์์ ์ง์ ํ URL ์ด์ธ์ ๋๋จธ์ง ๋ชจ๋ URL์ Role์ ์๊ด์์ด ์ ๊ทผ์ด ๊ฐ๋ฅํจ์ ์๋ฏธ
- antMatchers()๋ฅผ ์ด์ฉํ ์ ๊ทผ ๊ถํ ๋ถ์ฌ ์, ์ฃผ์ ์ฌํญ
- ๋ง์ฝ์ authorizeHttpRequests() ๋ฉ์๋์์
.antMatchers("/**").permitAll()
์ด ์ ์ผ ์์ ์์นํ๊ฒ ๋๋ฉด
Role์ ์๊ด์์ด ๋ชจ๋ requestURL์ ์ ๊ทผ ํ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ค์์ ์ค๋ ์ ๊ทผ ๊ถํ ์กฐ๊ฑด์ ์ ๊ธฐ๋ฅ์ ํ์ง ๋ชปํ๊ฒ ๋๋ค. - ํญ์ โญ ๋ ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋ก๋ถํฐ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ ๋ค์ ๋ ๊ตฌ์ฒด์ ์ธ URL ๊ฒฝ๋ก์ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ๋ ์ต๊ด ๋ค์ด๊ธฐ.
์ฐธ๊ณ ๋ก Ant๋ Maven๊ณผ Gradle์ ๋ฐ๋ ค์ ๊ฑฐ์ ์ฌ์ฉ๋์ง ์๋ ๋น๋ ํด,
but, Ant์์ ์ฌ์ฉ๋๋ Ant Pattern์ URL ๊ฒฝ๋ก ๋ฑ์ ์ง์ ํ๊ธฐ ์ํ Pattern ํํ์์ผ๋ก ์ฌ๋ฌ ์คํ ์์ค์์ ์ฌ์ฉ๋๊ณ ์๋ค.
Maven๋ xml์ ์ฌ์ฉํด์ผํด์ gradle๋ก ๋ง์ด ๋์ด๊ฐ๋ ์ถ์ธ - ๋ง์ฝ์ authorizeHttpRequests() ๋ฉ์๋์์
5๏ธโฃ ๊ด๋ฆฌ์ ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์ ์ถ๊ฐ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
@Configuration
public class SecurityConfiguration {
...
...
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("kevin@gmail.com")
.password("1111")
.roles("USER")
.build();
UserDetails admin =
User.withDefaultPasswordEncoder()
.username("admin@gmail.com")
.password("2222")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
6๏ธโฃ ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์ ์์ด๋ ํ์ ๋ฐ ์ฌ์ฉ์ ๋ก๊ทธ์์.
๋ก๊ทธ์ธ ํ ์ฌ์ฉ์ ํ์ & ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์ ๋ก๊ทธ์์ ํ๊ธฐ & ๋ง์ดํ์ด์ง ๋งํฌ๋ ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์์๊ฒ๋ง ๋ณด์ด๊ฒ ํ๊ธฐ
-
header.html ์์ โ ๋ก๊ทธ์์ ๋ฐ ๊ถํ ๋ณ ๋ฉ๋ดํ์ํ๊ธฐ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<html xmlns:th="<http://www.thymeleaf.org>" xmlns:sec="<http://www.thymeleaf.org/thymeleaf-extras-springsecurity5>"> <!-- (1) --> <body> <div align="right" th:fragment="header"> <a href="/members/register" class="text-decoration-none">ํ์๊ฐ์ </a> | <span sec:authorize="isAuthenticated()"> <!-- (2) --> <span sec:authorize="hasRole('USER')"> <!-- (3) --> <a href="/members/my-page" class="text-decoration-none">๋ง์ดํ์ด์ง</a> | </span> <a href="/logout" class="text-decoration-none">๋ก๊ทธ์์</a> <!-- (4) --> <span th:text="${#authentication.name}">ํ๊ธธ๋</span>๋ <!-- (5) --> </span> <span sec:authorize="!isAuthenticated()"> <!-- (6) --> <a href="/auths/login-form" class="text-decoration-none">๋ก๊ทธ์ธ</a> </span> </div> </body> </html>
- ํ์๋ฆฌํ ๊ธฐ๋ฐ์ HTML ํ
ํ๋ฆฟ์์ ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ ๊ถํ ์ ๋ณด๋ฅผ ์ด์ฉํด ์ด๋ค ๋ก์ง์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋
๋จผ์ (1)๊ณผ ๊ฐ์ดsec
ํ๊ทธ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ XML ๋ค์์คํ์ด์ค๋ฅผ ์ง์ , ์ง์ ํ์ง ์์๋ ๋์ํ์ง๋ง ํ๊ทธ์ ์๋ฌ ํ์ ๋จ. - (2) : ํ๊ทธ ๋ด๋ถ์์
sec:authorize="isAuthenticated()"
๋ฅผ ์ง์ ํ๋ฉด ํ์ฌ ํ์ด์ง์ ์ ๊ทผํ ์ฌ์ฉ์๊ฐ ์ธ์ฆ์ ์ฑ๊ณตํ ์ฌ์ฉ์์ธ์ง๋ฅผ ์ฒดํฌ- โ
isAuthenticated()
์ ๊ฐ์ดtrue
์ด๋ฉด ํ๊ทธ ํ์์ ํฌํจ๋ ์ฝํ ์ธ ๋ฅผ ํ๋ฉด์ ํ์
- โ
- (3) : ์ธ์ฆ์ ์ฑ๊ณตํ ์ฌ์ฉ์๋ผ๋ฉด ๋ง์ดํ์ด์ง๋ USER Role์ ๊ฐ์ง ์ฌ์ฉ์์๊ฒ๋ง ํ์ ๋๋๋ก ํ๋ค.
- (4) : (2)์์ ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ ์ฌ์ฉ์๋ [๋ก๊ทธ์ธ] ๋ฉ๋ด ๋์ [๋ก๊ทธ์์]์ด ํ์๋๋ค.
href="/logout"
์์ โ/logoutโ URL์ SecutiryConfiguration ํด๋์ค์์ ์ค์ ํ ๊ฐ๊ณผ ๊ฐ์์ผ ํ๋ค.
- (5) :
th:text="${#authentication.name}"
๋ฅผ ํตํด ๋ก๊ทธ์ธ ์ฌ์ฉ์์ username์ ํ์ โ ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์๋ username์ด ํ์๋๊ณ - (6) : sec:authorize=โ!isAuthenticated()โ๋ฅผ ํตํด ๋ก๊ทธ์ธ ํ ์ฌ์ฉ์๊ฐ ์๋๋ผ๋ฉด [๋ก๊ทธ์ธ] ๋ฒํผ์ด ํ์๋๋๋ก ํ๋ค.
- sec ํ๊ทธ ์ฌ์ฉ์ build.gradle์์ ์์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ ํด์ผํจ
- implementation โorg.thymeleaf.extras:thymeleaf-extras-springsecurity5โ
- ํ์๋ฆฌํ ๊ธฐ๋ฐ์ HTML ํ
ํ๋ฆฟ์์ ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ ๊ถํ ์ ๋ณด๋ฅผ ์ด์ฉํด ์ด๋ค ๋ก์ง์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋
-
SecurityConfiguration ํด๋์ค์ ๋ก๊ทธ์์ ์ค์ ์ถ๊ฐ
- ๋ก๊ทธ์์์ ๋๋ ์ ๋๋ ์คํ๋ง ์ ๋ก๊ทธ์์ url์ ์ค์ ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ถํ์ด ์ฌ๋ผ์ง๊ฒ ์ค์ ๋์ด ์์. โ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํ๊ฒ ๋์ด์์,
-
์ฐ๋ฆฌ๊ฐ ๋ก๊ทธ์์์ ์ค์ ํ๋ฉด ์ฑ๊ณตํ์ ๋ ๋ฃจํธ๋ก ์ด๋ํ๊ฒ ์ฝ๋ ์ถ๊ฐ.
- logout() ๋ฉ์๋๋ ๋ก๊ทธ์์ ์ค์ ์ ์ํ
LogoutConfigurer
๋ฅผ ๋ฆฌํด - logoutUrl(โ/logoutโ)์ ํตํด ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์์์ ์ํํ๊ธฐ ์ํ request URL์ ์ง์
โ โญ ์ฌ๊ธฐ์ ์ค์ ํ URL์ header.html์ ๋ก๊ทธ์์ ๋ฉ๋ด์ ์ง์ ํhref="/logout"
๊ณผ ๋์ผํด์ผ ํ๋ค. - .logoutSuccessUrl(โ/โ) : ๋ก๊ทธ์์์ ์ฑ๊ณต์ ์ผ๋ก ์ํํ ์ดํ ๋ฆฌ๋ค์ด๋ ํธ ํ URL์ ์ง์ , ์ฌ๊ธฐ์๋ ๋ฉ์ธํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ ํ๋๋ก ์ง์ .
-
๋ก๊ทธ์์ ํด๋ฆญ ์ ํค๋๊ฐ ๋ฐ๋๊ฒ ๋จ.
ํ์๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ
์ฌ๊ธฐ๊น์ง Security filterChain์ผ๋ก ์ธ์ฆ์ธ๊ฐ๋ฅผ ๊ตฌํ โ ์ด์ ์ธ๋ฉ๋ชจ๋ฆฌDB๋ก ๋ณ๊ฒฝ
์ด UserDetails๊ฐ์ฒด๊ฐ ์๋ก ์์ฑ๋์ด์ผํจ
์์งDB๋ ์ฌ์ฉํ์ง ์์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ InMemoryUserDetailsManager๋ก ๋ฐํํ ์์
[ํ์ ๊ฐ์ ํผ์ ํตํ InMemory User ๋ฑ๋ก]
ํ์ ๊ฐ์ ํผ์ ํตํด InMemory User๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํ ์์ ์์
- PasswordEncoder Bean ๋ฑ๋ก
- MemberService Bean ๋ฑ๋ก์ ์ํ JavaConfiguration ๊ตฌ์ฑ
- InMemoryMemberService ํด๋์ค ๊ตฌํ
1๏ธโฃ PasswordEncoder Bean ๋ฑ๋ก
PasswordEncoder๋ Spring Security์์ ์ ๊ณตํ๋ ํจ์ค์๋ ์ํธํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ปดํฌ๋ํธ์ด๋ค.
PasswordEncoder๋ ๋ค์ํ ์ํธํ ๋ฐฉ์์ ์ ๊ณตํ๋ฉฐ,
Spring Security์์ ์ง์ํ๋ PasswordEncoder์ ๋ํดํธ ์ํธํ ์๊ณ ๋ฆฌ์ฆ์ bcrypt์ด๋ค.
- ํ์ ๊ฐ์
ํผ์ ํตํด ์ ํ๋ฆฌ์ผ์ด์
์ ์ ๋ฌ๋๋ ํจ์ค์๋๋ ์ํธํ๋์ง ์์ ํ๋ ์ธ ํ
์คํธ(Plain Text)
โ ์ํธํํ๊ธฐ์ํด passwordEncorder์ฌ์ฉ - ํ์ ๊ฐ์
ํผ์์ ์ ๋ฌ๋ฐ์ ํจ์ค์๋๋ InMemory User๋ก ๋ฑ๋กํ๊ธฐ ์ ์ ์ํธํ๋์ด์ผ ํ๋ค.
SecurityConfiguration ํด๋์ค์์ PasswordEncoder๋ฅผ Bean์ผ๋ก ๋ฑ๋ก
1
2
3
4
5
6
7
8
9
@Configuration
public class SecurityConfiguration {
...
@Bean
public PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
PasswordEncoderFactories.createDelegatingPasswordEncoder();
๋ฅผ ํตํด DelegatingPasswordEncoder
๋ฅผ ๋จผ์ ์์ฑํ๋๋ฐ,
์ด DelegatingPasswordEncoder
๊ฐ ์ค์ง์ ์ผ๋ก PasswordEncoder ๊ตฌํ ๊ฐ์ฒด๋ฅผ ์์ฑํด ์ค๋ค.
์ฐธ๊ณ ) ๊ฐ๋ฐํ๊ฒฝ์์๋ง ์ธ๋ฉ๋ชจ๋ฆฌ DB ์ฌ์ฉํ ๊ฑฐ๊ณ @configuration์ผ๋ก ๋น๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฒ์. @Configuration+@bean์ ์ฌ์ฉํ์ง์๊ณ @Component ์ฌ์ฉ ํด๋ ๋จ.
2๏ธโฃ MemberService Bean ๋ฑ๋ก์ ์ํ JavaConfiguration ๊ตฌ์ฑ
-
MemberSercice
ํ์ ๊ฐ์ ํผ์์ ์ ๋ฌ๋ฐ์ ์ ๋ณด๋ฅผ ์ด์ฉํด ์๋ก์ด ์ฌ์ฉ์๋ฅผ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ๋ง ์์ผ๋ฉด ๋๋ฏ๋ก
createMember()
ํ๋๋ง ๊ตฌํํ๋ ๊ตฌํ์ฒด๊ฐ ์์ผ๋ฉด ๋๋ค.1 2 3 4 5 6
package com.springboot.member; public interface MemberService { Member createMember(Member member); }
-
InMemoryMemberService โ MemberService ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ตฌํ ํด๋์ค
1 2 3 4 5 6 7 8 9
package com.springboot.member; public class InMemoryMemberService implements MemberService { public Member createMember(Member member) { return null; // ๋ค์์ ๊ตฌํ ํ ์์ ์ด๋ค. } }
-
DBMemberService ํด๋์ค โ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ User๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํ MemberService ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค
1 2 3 4 5 6 7 8 9 10 11
package com.springboot.member; import org.springframework.transaction.annotation.Transactional; @Transactional public class DBMemberService implements MemberService { public Member createMember(Member member) { return null; // InMemory User ๋ฑ๋ก์ด ๋๋ ๋ค์ ํ์ต๋ด์ฉ, ์ด์ด์ ๋ฐ๋ก ๊ตฌํ } }
-
JavaConfiguration ๊ตฌ์ฑ : MemberService Bean ๋ฑ๋ก์ ์ํ JavaConfiguration ๊ตฌ์ฑ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
package com.springboot.config; import com.springboot.member.InMemoryMemberService; import com.springboot.member.MemberService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.UserDetailsManager; @Configuration public class JavaConfiguration { @Bean public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) { return new InMemoryMemberService(userDetailsManager, passwordEncoder); } }
- MemberService ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค์ธ InMemoryMemberService๋ฅผ Spring Bean์ผ๋ก ๋ฑ๋ก
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋ ์์ด ๋ฉ๋ชจ๋ฆฌ์ Spring Security์ User๋ฅผ ๋ฑ๋กํด์ผ ํ๋ฏ๋ก
UserDetailsManager
๊ฐ์ฒด๊ฐ ํ์ - User ๋ฑ๋ก ์, ํจ์ค์๋๋ฅผ ์ํธํํ ํ์ ๋ฑ๋กํด์ผ ํ๋ฏ๋ก Spring Security์์ ์ ๊ณตํ๋
PasswordEncoder
๊ฐ์ฒด๊ฐ ํ์ - ๋ ๊ฐ์ฒด๋ฅผ InMemoryMemberService ๊ฐ์ฒด ์์ฑ ์, DI ํด ์ฃผ์ด์ผ ํ๋ค.
3๏ธโฃ InMemoryMemberService ๊ตฌํ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.springboot.member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
//(1) @Service ์ ๋ํ
์ด์
์ ์ฌ๊ธฐ์ ๋ฌ์ง ์๋๋ค, ์ค๋ณต์๋จ. ๋ฐ๋ก ์ JavaConfiguration์์ ๋ฐํ๋๋
// InMemoryMemberService๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋์๊ธฐ ๋๋ฌธ.
public class InMemoryMemberService implements MemberService {
//(2)
private final UserDetailsManager userDetailsManager;
private final PasswordEncoder passwordEncoder;
public InMemoryMemberService(UserDetailsManager userDetailsManager, PasswordEncoder passwordEncoder) {
this.userDetailsManager = userDetailsManager;
this.passwordEncoder = passwordEncoder;
}
@Override
public Member createMember(Member member) {
//(3)
List<GrantedAuthority> authorities = createAuthories(Member.MemberRole.ROLE_USER.name());
//์ผ๋ฐ์ ์ธ ํ์๊ฐ์
์ user๋ก ๊ถํ ์ค์ ํด์ค
//ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด์จ member๋ dto์์ ๋ณํ ๋ ๊ฒ , ๋น๋ฐ๋ฒํธ๋ ์์ง ํ๋ฌธ์ด๋ค. ์ํธํํด์ผ ํจ.
//์ธ๊ฐ๋ List๋ก ๊ด๋ฆฌ ํ๋ค..
String encryptedPassword = passwordEncoder.encode(member.getPassword());
//(4)
UserDetails userDetails = new User(member.getEmail(), encryptedPassword, authorities);
//์ฐ๋ฆฌ๊ฐ ๋ง๋ ์ ์ ์๋.
userDetailsManager.createUser(userDetails);
//UserDetailsManager์ createUser() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ User๋ฅผ ๋ฑ๋ก
return member;
}
//(5)
private List<GrantedAuthority> createAuthories(String ...roles){
return Arrays.stream(roles)
.map(role-> new SimpleGrantedAuthority(role))
.collect(Collectors.toList());
//๊ถํ์ ๋ฆฌ์ค๋ฅดํ ๋ฐ๋ ์ด์
//ํ ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ ๊ฐ์ ๊ถํ์ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ
//์๋ฅผ ๋ค์ด, ํ ์ฌ์ฉ์๊ฐ ROLE_USER์ ROLE_ADMIN ๊ถํ์ ๋์์ ๊ฐ์ง ์ ์๋ค.
// ๋ฐ๋ผ์ ๊ถํ์ ๋ฆฌ์คํธ๋ก ๊ด๋ฆฌํ์ฌ ์ ์ฐํ๊ฒ ์ฌ๋ฌ ๊ถํ์ ๋ถ์ฌํ ์ ์๋ค.
}
}
- (1) ์์ @Service ์ ๋ํ
์ด์
์ ๋ฌ์ง ์๋๋ค โ ๋น์ ์ค๋ณต์ด ๋๋ฉด ์๋๋ค.
๋ฐ๋ก ์ JavaConfiguration์์ ๋ฐํ๋๋InMemoryMemberService๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋์๊ธฐ ๋๋ฌธ. - (2) ์์
UserDetailsManager
์PasswordEncoder
๋ฅผ DI ๋ฐ๋๋ค.- UserDetailsManager๋ Spring Security์ User๋ฅผ ๊ด๋ฆฌํ๋ ๊ด๋ฆฌ์ ์ญํ ์ ํ๋ค..
- SecurityConfiguration์์ ๋น ์ค์ ์ ํ ์ด์ ๋ ์ฌ๊ธฐ์ ์ฃผ์
ํ๋ ค๊ณ
โ PSA (Policy Service Abstraction) ์์น์ ๋ฐ๋ผ ์ถ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ์๋ ์ด๋ฆ์ ๋ฐ๊พธ์ง ์๊ณ ๋ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ์ฃผ์ ๊ฐ๋ฅ - UserDetailsManager ์ธํฐํ์ด์ค์ ํ์ ํ์
์
InMemoryUserDetailsManager
๋ผ๋ ์ฌ์ค ๊ธฐ์ตํ๊ธฐ.
- (3) : Spring Security์์ User๋ฅผ ๋ฑ๋กํ๊ธฐ ์ํด์๋ ํด๋น User์ ๊ถํ(Authority)์ ์ง์ ํด ์ฃผ์ด์ผ ํ๋ค.
createAuthorities(Member.MemberRole.ROLE_USER.name());
๋ฅผ ์ด์ฉํด User์ ๊ถํ ๋ชฉ๋ก์List<GrantedAuthority>
๋ก ์์ฑ-
Member ํด๋์ค์๋
MemberRole
์ด๋ผ๋ enum์ด ์ ์๋์ด์๋ค. - โญ Spring Security์์๋
SimpleGrantedAuthority
๋ฅผ ์ฌ์ฉํด Role ๋ฒ ์ด์ค ํํ์ ๊ถํ์ ์ง์ ํ ๋
โROLE_โ + ๊ถํ ๋ช
ํํ๋ก ์ง์ ํด ์ฃผ์ด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ ์ ์ ํ ๊ถํ ๋งคํ์ด ์ด๋ฃจ์ด์ง์ง ์๋๋ค๋ ์ฌ์ค ๊ธฐ์ตํ๊ธฐ
-
(4)์์๋ Spring Security User๋ก ๋ฑ๋กํ๊ธฐ ์ํด
UserDetails
๋ฅผ ์์ฑํ๋ค.โญ Spring Security์์๋ Spring Security์์ ๊ด๋ฆฌํ๋ User ์ ๋ณด๋ฅผ
UserDetails
๋ก ๊ด๋ฆฌํ๋ค๋ ๊ฒ ๊ผญ ๊ธฐ์ตํ๊ธฐ - (5) ์ createAuthories ๋ฉ์๋ : Stream API๋ฅผ ์ด์ฉํด ์์ฑ์ ํ๋ผ๋ฏธํฐ๋ก ํด๋น User์ Role์ ์ ๋ฌํ๋ฉด์
SimpleGrantedAuthority
๊ฐ์ฒด๋ฅผ ์์ฑํ ํ,List<SimpleGrantedAuthority>
ํํ๋ก ๋ฆฌํด
๋ฐ์ดํฐ ๋ฒ ์ด์ค ์ฐ๋์ ํตํ ๋ก๊ทธ์ธ ์ธ์ฆ
ํ ์คํธ ํ๊ฒฝ์ด๋ฏ๋ก DB๋ ์ธ๋ฉ๋ชจ๋ฆฌ๋ก ์ฌ์ฉํ ์์
Custom UserDetailsService๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
Spring Security์์๋ User์ ์ธ์ฆ ์ ๋ณด๋ฅผ ํ ์ด๋ธ์ ์ ์ฅํ๊ณ , ํ ์ด๋ธ์ ์ ์ฅ๋ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ด์ฉํด ์ธ์ฆ ํ๋ก์ธ์ค๋ฅผ ์งํํ ์ ์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๊ณ ๊ทธ ์ค ํ๋๊ฐ Custom UserDetailsService๋ฅผ ์ฌ์ฉํ๋ ๊ฒ.
1๏ธโฃ SecurityConfiguration์ ์ค์ ๋ณ๊ฒฝ ๋ฐ ์ถ๊ฐ
-
H2๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ์์ ํ๊ธฐ ์ํด ๋ทฐ๋จ์์ SameOrigin์ค์ ํด์ผ ๋ณผ ์ ์์.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
@Configuration public class SecurityConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http. headers().frameOptions().sameOrigin() //๊ฐ์ ํ์ด์ง์ผ ๊ฒฝ์ฐ ํ์ด์ง๋ ๋๋ง์ ํ์ฉํ๊ฒ ๋ค // ์ค๋ฌด์์๋ ์๋ง ์์ธ ํ๋ฅ ์ด ๋์. .and() .csrf().disable() .formLogin() ... ... return http.build();
-
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋์ด ์๋ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ผ์ InMemory User๋ฅผ ์ํ ์ค์ ๋ค์ ๋ ์ด์ ํ์ ์์ผ๋ฏ๋ก ์ ๊ฑฐ,
์๋ ์ฝ๋๋ ๋ค ์ ๊ฑฐํ๋ฉด ๋๋ค.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Bean //์คํ๋ง ์ปจํ ์ด๋์ ๋์์ ๋ฐ๊ธฐ ์ํจ => ์ฌ์ค์ javaconfiguration์์ ์ฃผ์ ํ๊ธฐ ์ํจ public UserDetailsManager userDetailService(){ // UserDetails user = // User.withDefaultPasswordEncoder() // .username("jerry@gmail.com") // .password("wpfl") // .roles("USER") // .build(); UserDetails admin = User.withDefaultPasswordEncoder() .username("admin@gmail.com") //์ผ๋ฐ์ ์ผ๋ก ํต์ฉ๋๋ ์์ด๋์ ๊ฐ๋ . .password("ehowl") .roles("ADMIN") //์ธ๊ฐ : ๊ถํ์ ๋ญ ์ค ๊ฒ์ธ์ง? .build(); return new InMemoryUserDetailsManager(admin); }
2๏ธโฃ JavaConfiguration์ Bean ๋ฑ๋ก ๋ณ๊ฒฝ
๋ฐ์ดํฐ๋ฒ ์ด์ค์ User์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ธฐ ์ํด MemberService ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค๋ฅผ inMemoryMemberService์์ DBMemberService๋ก ๋ณ๊ฒฝ
1
2
3
4
5
6
7
8
9
10
...
@Configuration
public class JavaConfiguration {
@Bean
public MemberService inMemoryMemberService(UserDetailsManager userDetailsManager,
PasswordEncoder passwordEncoder) {
return new InMemoryMemberService(userDetailsManager, passwordEncoder);
}
}
โฌ๏ธโฌ๏ธ โฌ๏ธโฌ๏ธ
1
2
3
4
5
6
7
8
9
@Configuration
public class JavaConfiguration {
// (1)
@Bean
public MemberService dbMemberService(MemberRepository memberRepository,
PasswordEncoder passwordEncoder) {
return new DBMemberService(memberRepository, passwordEncoder); (1-1)
}
}
DBMemberService๋ ๋ด๋ถ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๊ณ ,
ํจ์ค์๋๋ฅผ ์ํธํํด์ผ ํ๋ฏ๋ก MemberRepository
์ PasswordEncoder
๊ฐ์ฒด๋ฅผ DI ํด์ค๋ค.
3๏ธโฃ DBMemberService ๊ตฌํ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Transactional
public class DBMemberService implements MemberService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
public DBMemberService(MemberRepository memberRepository,
PasswordEncoder passwordEncoder) {
this.memberRepository = memberRepository;
this.passwordEncoder = passwordEncoder;
}
public Member createMember(Member member) {
verifyExistsEmail(member.getEmail());
String encryptedPassword = passwordEncoder.encode(member.getPassword()); // (1)
member.setPassword(encryptedPassword); // (2)
Member savedMember = memberRepository.save(member);
System.out.println("# Create Member in DB");
return savedMember;
}
...
...
}
- (1)์์ PasswordEncoder๋ฅผ ์ด์ฉํด ํจ์ค์๋๋ฅผ ์ํธํํ๋ค.
- (2)์์ ์ํธํ๋ ํจ์ค์๋๋ฅผ password ํ๋์ ๋ค์ ํ ๋นํ๋ค.
4๏ธโฃ Custom UserDetailsService ๊ตฌํ - V1
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ User์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ธ์ฆ์ ์ฒ๋ฆฌ
โญ UserDetailsService
Spring Security์์ ์ ๊ณตํ๋ ์ปดํฌ๋ํธ ์ค ํ๋์ธ
UserDetailsService
๋ User ์ ๋ณด๋ฅผ ๋ก๋(load)ํ๋ ํต์ฌ ์ธํฐํ์ด์ค์ฌ๊ธฐ์ ๋ก๋(load)์ ์๋ฏธ๋ ์ธ์ฆ์ ํ์ํ User ์ ๋ณด๋ฅผ ์ด๋๊ฐ์์ ๊ฐ์ง๊ณ ์จ๋ค๋ ์๋ฏธ์ด๋ฉฐ, ์ฌ๊ธฐ์ ๋งํ๋ โ
์ด๋๊ฐ
โ๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ ์๋ ์๊ณ , DB ๋ฑ์ ์๊ตฌ ์ ์ฅ์๊ฐ ๋ ์๋ ์์ต๋๋ค.์ฐ๋ฆฌ๊ฐ InMemory User๋ฅผ ๋ฑ๋กํ๋ ๋ฐ ์ฌ์ฉํ๋ InMemoryUserDetailsManager๋ UserDetailsManager ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์ด๊ณ , UserDetailsManager๋ UserDetailsService๋ฅผ ์์ํ๋ ํ์ฅ ์ธํฐํ์ด์ค
-
HelloUserDetailsService
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ธ์ฆ ์ ๋ณด๋ก ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ Custom UserDetailsService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
@Component public class HelloUserDetailsServiceV1 implements UserDetailsService { // (1) private final MemberRepository memberRepository; private final HelloAuthorityUtils authorityUtils; // (2) public HelloUserDetailsServiceV1(MemberRepository memberRepository, HelloAuthorityUtils authorityUtils) { this.memberRepository = memberRepository; this.authorityUtils = authorityUtils; } // (3) @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<Member> optionalMember = memberRepository.findByEmail(username); Member findMember = optionalMember.orElseThrow(() -> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND)); // (4) Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember.getEmail()); // (5) ๊ฐ์ ํ๋ฉด ์ข์ ํฌ์ธํธ return new User(findMember.getEmail(), findMember.getPassword(), authorities); } }
- (1) :
UserDetailsService
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค. - (2) : ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ User๋ฅผ ์กฐํํ๊ณ , ์กฐํํ User์ ๊ถํ(Role) ์ ๋ณด๋ฅผ ์์ฑํ๊ธฐ ์ํด
MemberRepository
์HelloAuthorityUtils
ํด๋์ค๋ฅผ DI ๋ฐ๋๋ค. UserDetailsService
์ธํฐํ์ด์ค๋ฅผ implements ํ๋ ๊ตฌํ ํด๋์ค๋ (3)๊ณผ ๊ฐ์ดloadUserByUsername(String username)
์ด๋ผ๋ ์ถ์ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.HelloAuthorityUtils
๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ํ์์ ์ด๋ฉ์ผ ์ ๋ณด๋ฅผ ์ด์ฉํด Role ๊ธฐ๋ฐ์ ๊ถํ ์ ๋ณด(GrantedAuthority
) ์ปฌ๋ ์ ์ ์์ฑํ๋ค.- (4) ์์ ์์ฑํ ๊ถํ ์ ๋ณด๋ฅผ Spring Security์์๋ ์์ง ์์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ Spring Security์ ์ด ์ ๋ณด๋ค์ ์ ๊ณตํด ์ฃผ์ด์ผ ํ๋ฉฐ, (5) ์์๋
UserDetails
์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์ธUser
ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ํตํด ์ ๊ณต - (5)์ ๊ฐ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ User ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ฉด Spring Security๊ฐ ์ด ์ ๋ณด๋ฅผ ์ด์ฉํด ์ธ์ฆ ์ ์ฐจ๋ฅผ ์ํํ๋ค.
โญ ์ฆ, ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ User์ ์ธ์ฆ ์ ๋ณด๋ง Spring Security์ ๋๊ฒจ์ฃผ๊ณ , ์ธ์ฆ ์ฒ๋ฆฌ๋ Spring Security๊ฐ ๋์ ํด ์ค๋ค
โญ UserDetails
UserDetails
๋ UserDetailsService์ ์ํด ๋ก๋(load)๋์ด ์ธ์ฆ์ ์ํด ์ฌ์ฉ๋๋ ํต์ฌ User ์ ๋ณด๋ฅผ ํํํ๋ ์ธํฐํ์ด์ค์ด๋ค.UserDetails
์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๋ Spring Security์์ ๋ณด์ ์ ๋ณด ์ ๊ณต์ ๋ชฉ์ ์ผ๋ก ์ง์ ์ฌ์ฉ๋์ง๋ ์๊ณ ,Authentication
๊ฐ์ฒด๋ก ์บก์ํ๋์ด ์ ๊ณต๋๋ค. - (1) :
-
HelloAuthorityUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
package com.springboot.auth.utils; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.stereotype.Component; import java.util.List; @Component public class HelloAuthorityUtils { // (1) @Value("${mail.address.admin}") private String adminMailAddress; // (2) private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"); // (3) private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER"); public List<GrantedAuthority> createAuthorities(String email) { // (4) if (email.equals(adminMailAddress)) { return ADMIN_ROLES; } return USER_ROLES; } }
-
(1)์ application.yml์ ์ถ๊ฐํ ํ๋กํผํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ํํ์
- ์ด๊ฑธ ๋ฃ๋๋ค๊ณ ์๋์ผ๋ก ๊ด๋ฆฌ์๊ถํ์ ๋ฉ์ผ์ด ์์ฑ๋์ง ์๋๋ค
- ์ด๊ฑธ ์ค์ ํ๋ฉด (4)์์ ๊ฐ์
ํ ์ฌ๋์ ์ด๋ฉ์ผ์ ๊ฒ์ฆํ๋ค.
โ admin์ผ๋ก ๋ฃ์ด๋ ์ด๋ฉ์ผ๊ณผ ๋์ผํ์ง, ๋ง์ฝ ๋์ผํ๋ค๋ฉด ADMIN_ROLES๋ฅผ ๋ถ์ฌ ,
์๋๋ผ๋ฉด USER_ROLES โ ์ธ๊ฐ๋ ๋ก๊ทธ์ธ ํ๊ณ ๋์ ์ธ๊ฐ๊ฐ ์ด๋ฃจ์ด์ง.
โญ ์ค๋ฌด์์๋ ๋น์ฐํ ํ์ ๊ฐ์ ์, ์๋ฌด๋ฐ ์ธ์ฆ ์ฅ์น๋ ์์ด ์ด๋ฉ์ผ ์ฃผ์๋ง ์ ๋ ฅํด์ ๊ด๋ฆฌ์ ๊ถํ์ ๋ถ์ฌํ์ง๋ ์๋๋ค.
๊ด๋ฆฌ์ ๊ถํ์ ์๋ฌด๋ฆฌ ๊ฐ์กฐํด๋ ์ง๋์น์ง ์์ ์ ๋๋ก ์ค์!
๊ด๋ฆฌ์๋ก์ ๋ฑ๋กํ๊ธฐ ์ํ ์ถ๊ฐ์ ์ธ ์ธ์ฆ ์ ์ฐจ๊ฐ ์์ ๊ฒ
-
(4)์์
List<GrantedAuthority>
๋ฅผ ๋ฆฌํดํ๋ฉด ํ์์๊ฒ ์ธ๊ฐ๊ฐ ๋ถ์ฌ ๋จ.
-
-
H2 ์น ์ฝ์์์ ๋ฑ๋กํ ํ์ ์ ๋ณด ํ์ธ ๋ฐ ๋ก๊ทธ์ธ ์ธ์ฆ ํ ์คํธ
-
ํ์๊ฐ์ ๋ฐ ๋ก๊ทธ์ธ ์ ๋จ.
-
h2 console ํ์ธํ๋ฉด ์๋์ ๊ฐ์ด ์ ๋ณด ๋ค์ด์ด
PASSWORD ์ด์ ๋ณด๋ฉด ํ์ ๊ฐ์ ๋ฉ๋ด์์ ์ ๋ ฅํ๋ ํจ์ค์๋ ์ ๋ณด๊ฐ ์ํธํ ๋์ด์๋ค
-
5๏ธโฃ HelloUserDetailsServiceV2
์์ฑ
V1์ ์๋ @Component ๋ ์ญ์ ํด์ผ ํจ. ํ๋๋ง ์์ด์ผ ํ๋ค.
UserDetails์ ๊ตฌํ ํด๋์ค์ธ User์ ๊ฐ์ฒด๋ฅผ ์ง์ ์ ์ผ๋ก ์์ฑํด์ ๋ฆฌํดํ๋ ๋ถ๋ถ์ ๊ฐ์ ํ ๊ฒ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.springboot.auth;
import com.springboot.exception.BusinessLogicException;
import com.springboot.exception.ExceptionCode;
import com.springboot.member.Member;
import com.springboot.member.MemberRepository;
import com.springboot.utils.HelloAuthorityUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@Component
public class HelloUserDetailsServiceV2 implements UserDetailsService {
private final MemberRepository memberRepository;
private final HelloAuthorityUtils helloAuthorityUtils;
public HelloUserDetailsServiceV2(MemberRepository memberRepository, HelloAuthorityUtils helloAuthorityUtils) {
this.memberRepository = memberRepository;
this.helloAuthorityUtils = helloAuthorityUtils;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//UserDetailsService๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ํ์๋ก ์์ด์ผ ํ๋ ๋ฉ์๋
Optional<Member> optionalMember = memberRepository.findByEmail(username);
Member findMember = optionalMember.orElseThrow(()-> new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND));
//List<GrantedAuthority> authorities=helloAuthorityUtils.createAuthorities(findMember.getEmail());
//return new User(findMember.getEmail(), findMember.getPassword(), authorities);
return new HelloUserDetails(findMember);
}
//------------------------์ถ๊ฐํ ์ฝ๋ --------------------------------------
//์ด๋ ํด๋์ค ์์ฑ , ์ธํฐํ์ด์ค ๊ตฌํํ๋ฉด์ ์ํฐํฐ๋ฅผ ์์. ์์์ด ๋จผ์ ์์ผ ํ๋ค.
private final class HelloUserDetails extends Member implements UserDetails{
//์๋ ํด๋์ค๋ ๋ฉค๋ฒ๋ฅผ ์์ํ๊ธฐ ๋๋ฌธ์ ํ๋์ ์์ด๋ ๋ค ๊ฐ์ง๊ณ ์๋ค (Member ๊ฐ superํด๋์ค์ ํด๋น)
// (private๋ก ์ ์ธ๋ ๊ฒ์ ์ ์ธํ๊ณ , getter setter๋ ๋ค public ์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ ธ์ค๋ ๊ฒ์ด ๊ฐ๋ฅ)
HelloUserDetails(Member member){
setMemberId(member.getMemberId());
setEmail(member.getEmail());
setFullName(member.getFullName());
setPassword(member.getPassword());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() { //์ธ๊ฐ์ ๋ณด๋ฅผ ๋ง๋ค์ด ์ฃผ์ด์ผ ํจ.
return helloAuthorityUtils.createAuthorities(this.getEmail());//HelloUserDetails์ ์ด๋ฉ์ผ์ ๊ฐ์ ธ์ค๋ ๊ฒ-> this
}
@Override
public String getUsername() {
return this.getEmail();
}
//ํฌ๊ฒ ์ค์ํ์ง ์์์ ๋ชจ๋ true๋ก ๋ณ๊ฒฝ
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
}
- ๊ธฐ์กด์๋ loadUserByUsername() ๋ฉ์๋์ ๋ฆฌํด ๊ฐ์ผ๋ก
new User(findMember.getEmail(), findMember.getPassword(), authorities);์ ๋ฆฌํดํ์ง๋ง
๊ฐ์ ๋ ์ฝ๋์์๋ (1)๊ณผ ๊ฐ์ด new HelloUserDetails(findMember);๋ผ๋
Custom UserDetails ํด๋์ค์ ์์ฑ์๋ก findMember๋ฅผ ์ ๋ฌ - (2)์ HelloUserDetails ํด๋์ค๋ UserDetails ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์๊ณ
๋ํ Member ์ํฐํฐ ํด๋์ค๋ฅผ ์์ํ๊ณ ์๋ค.- loadUserByUsername() ๋ฉ์๋ ๋ด๋ถ์์ User์ ๊ถํ ์ ๋ณด๋ฅผ ์์ฑํ๋ ์ฝ๋๋ ์์ผ๋ก ๋ค์ด๊ฐ.
โ Collection<? extends GrantedAuthority> authorities = authorityUtils.createAuthorities(findMember);๋
HelloUserDetails ํด๋์ค ๋ด๋ถ๋ก ์ด๋
- loadUserByUsername() ๋ฉ์๋ ๋ด๋ถ์์ User์ ๊ถํ ์ ๋ณด๋ฅผ ์์ฑํ๋ ์ฝ๋๋ ์์ผ๋ก ๋ค์ด๊ฐ.
๐ก ์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ํ์ ์ ๋ณด๋ฅผ Spring Security์ User ์ ๋ณด๋ก ๋ณํํ๋ ๊ณผ์ ๊ณผ User์ ๊ถํ ์ ๋ณด๋ฅผ ์์ฑํ๋ ๊ณผ์ ์ ์บก์ํํ ์ ์๋ค.
๐ก ๋ํ HelloUserDetails
ํด๋์ค๋ Member
์ํฐํฐ ํด๋์ค๋ฅผ ์์ํ๊ณ ์์ผ๋ฏ๋ก HelloUserDetails๋ฅผ ๋ฆฌํด ๋ฐ์ ์ฌ์ฉํ๋ ์ธก์์๋ ๋ ๊ฐ ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ๋ค ์์ฝ๊ฒ ์บ์คํ
ํด์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค
(2-3)์์๋ HelloAuthorityUtils
์ createAuthorities()
๋ฉ์๋๋ฅผ ์ด์ฉํด User์ ๊ถํ ์ ๋ณด๋ฅผ ์์ฑ
์ด ์ฝ๋๋ ๊ธฐ์กด์๋ loadUserByUsername() ๋ฉ์๋ ๋ด๋ถ์ ์์์ง๋ง ์ง๊ธ์ HelloUserDetails
ํด๋์ค ๋ด๋ถ์์ ์ฌ์ฉ๋๋๋ก ์บก์ํ๋์๋ค.
- (2-4)์์๋ Spring Security์์ ์ธ์ํ ์ ์๋ username์ Member ํด๋์ค์ email ์ฃผ์๋ก ์ฑ์ฐ๊ณ ์๊ธฐ ๋๋ฌธ์
getUsername()
์ ๋ฆฌํด ๊ฐ์ null์ผ ์ ์๋ค. - ๊ธฐํ UserDetails ์ธํฐํ์ด์ค์ ์ถ์ ๋ฉ์๋๋ฅผ ๊ตฌํํ ๋ถ๋ถ์ ์ง๊ธ์ ํฌ๊ฒ ์ค์ํ์ง ์์ ๋ถ๋ถ์ด๋ฏ๋ก ๋ชจ๋
true
๊ฐ์ ๋ฆฌํดํ๊ฒ ํ๋ค.
6๏ธโฃ User์ Role์ DB์์ ๊ด๋ฆฌํ๊ธฐ
- ์ผ๋ฐ์ ์ผ๋ก User์ ์ธ์ฆ ์ ๋ณด ๊ฐ์ ๋ณด์๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ์๊ตฌ ์ ์ฅ์์ ์์ ํ๊ฒ ๋ณด๊ด
- but, ํ์ฌ๊น์ง User์ ๊ถํ ์ ๋ณด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ด๋ฆฌํ๋ ๊ฒ์ด ์๋๋ผ
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ User ์ ๋ณด๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฝ๋์์์ ์กฐ๊ฑด์ ๋ง๊ฒ ์์ฑ - User์ ๊ถํ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ด๋ฆฌํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ ๊ณผ์ ํ์
- User์ ๊ถํ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ํ ์ด๋ธ ์์ฑ
- ํ์ ๊ฐ์ ์, User์ ๊ถํ ์ ๋ณด(Role)๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ ์์
- ๋ก๊ทธ์ธ ์ธ์ฆ ์, User์ ๊ถํ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ๋ ์์
- User์ ๊ถํ ์ ๋ณด ํ
์ด๋ธ ์์ฑ
- ๊ถํ๊ด๋ จ ์ ๋ณด๋ ์ค๋ฌด์์ ์ค์ ๋ก ํ
์ด๋ธ์ ๋ฐ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌํ๋ค.
(๊ธฐ๋ณธํค 1๋ฒ์ admin ๋๋จธ์ง๋ user) - ํ๋์ ๋ฉค๋ฒ๋ ํ๋์ ๊ถํ? no . ํ๋์ ๊ถํ์ ํ๋์ ๋ฉค๋ฒ๋ง? no โ ๋ค๋๋ค
- ๊ทธ๋ฌ๋ ์ผ๋๋ค๋ก ๊ตฌํํ ์์ ์, member๋ ์คํ์ํ๋ฆฌํฐ๊ฐ ๊ด๋ฆฌํ๊ธฐ๋๋ฌธ์ ๋ฉค๋ฒ์๊ฒ์๋ง ๊ด๊ณ๊ตฌํ ํ๋ฉด ๋จ.
-
Member์ ์ฝ๋ ์ถ๊ฐ
1 2
@ElementCollection(fetch = FetchType.EAGER) //๊ถํ ๊ด๋ จ ํ ์ด๋ธ์ ์ค์ -> ์ ๋ํ ์ด์ ๋ง ๋ฌ์๋ ์๋์ผ๋ก ํ ์ด๋ธ์ด ์์ฑ๋จ.
- List, Set ๊ฐ์ ์ปฌ๋ ์
ํ์
์ ํ๋๋
@ElementCollection
์ ๋ํ ์ด์ ์ ์ถ๊ฐํ๋ฉด
User ๊ถํ ์ ๋ณด์ ๊ด๋ จ๋ ๋ณ๋์ ์ํฐํฐ ํด๋์ค๋ฅผ ์์ฑํ์ง ์์๋ ๊ฐ๋จํ๊ฒ ๋งคํ ์ฒ๋ฆฌ๊ฐ ๋๋ค. -
์ด๋๋ก ์คํํ๋ฉด ์๋์ผ๋ก ํ ์ด๋ธ ์์ฑ ๋์ด์๋ ๊ฑธ ๋ณผ ์ ์๋ค.
- List, Set ๊ฐ์ ์ปฌ๋ ์
ํ์
์ ํ๋๋
- ๊ถํ๊ด๋ จ ์ ๋ณด๋ ์ค๋ฌด์์ ์ค์ ๋ก ํ
์ด๋ธ์ ๋ฐ๋ก ๋ง๋ค์ด์ ๊ด๋ฆฌํ๋ค.
-
ํ์ ๊ฐ์ ์, User์ ๊ถํ ์ ๋ณด(Role)๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ
๐ DBMemberService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
@Transactional public class DBMemberService implements MemberService { ... private final HelloAuthorityUtils authorityUtils; ... public Member createMember(Member member) { verifyExistsEmail(member.getEmail()); String encryptedPassword = passwordEncoder.encode(member.getPassword()); member.setPassword(encryptedPassword); // (1) Role์ DB์ ์ ์ฅ List<String> roles = authorityUtils.createRoles(member.getEmail()); member.setRoles(roles); Member savedMember = memberRepository.save(member); return savedMember; } ... ... }
๐
createRoles()
๋ฉ์๋๊ฐ ์ถ๊ฐ๋HelloAuthorityUtils
ํด๋์ค์ ์ฝ๋1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
package com.springboot.utils; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; import java.util.List; import java.util.stream.Collectors; @Component public class HelloAuthorityUtils { @Value("${mail.address.admin}") private String adminMailAddress; private final List<GrantedAuthority> ADMIN_ROLES= AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"); private final List<GrantedAuthority> USER_ROLES =AuthorityUtils.createAuthorityList("ROLE_USER"); private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER"); private final List<String> USER_ROLES_STRING = List.of("USER"); //๋ฉ๋ชจ๋ฆฌ์ role์ ๊ธฐ๋ฐ์ผ๋ก ๊ถํ ์ ๋ณด ์์ฑ public List<GrantedAuthority> createAuthorities(String email){ if(email.equals(adminMailAddress)){ return ADMIN_ROLES; }else { return USER_ROLES; } } //DB์ ์ ์ฅ๋ Rolse๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ถํ ์ ๋ณด ์์ฑ public List<GrantedAuthority>createAuthorities(List<String> roles){ List<GrantedAuthority> authorities = roles.stream().map(role-> new SimpleGrantedAuthority("ROLE_"+role)) .collect(Collectors.toList()); return authorities; } //DB์ ์ ์ฅํ๊ธฐ ์ํ ํํ์ roles๋ฅผ ๋ฐํํ๋ ๋ฉ์๋ public List<String>createRoles(String email) { if (email.equals(adminMailAddress)) { return ADMIN_ROLES_STRING; } else { return USER_ROLES_STRING; } } }
๐ ๋ก๊ทธ์ธ ์ธ์ฆ ์, User์ ๊ถํ ์ ๋ณด๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ๋ ์์
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
@Component public class HelloUserDetailsServiceV3 implements UserDetailsService { ... private final class HelloUserDetails extends Member implements UserDetails { HelloUserDetails(Member member) { setMemberId(member.getMemberId()); setFullName(member.getFullName()); setEmail(member.getEmail()); setPassword(member.getPassword()); setRoles(member.getRoles()); // (1) } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // (2) DB์ ์ ์ฅ๋ Role ์ ๋ณด๋ก User ๊ถํ ๋ชฉ๋ก ์์ฑ return authorityUtils.createAuthorities(this.getRoles()); } ... ... } }
- (1) : HelloUserDetails๊ฐ ์์ํ๊ณ ์๋ Member(
extends Member
)์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํList<String> roles
๋ฅผ ์ ๋ฌ - (2) : ๋ค์ Member(
extends Member
)์ ์ ๋ฌํ Role ์ ๋ณด๋ฅผ authorityUtils.createAuthorities() ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํด์
๊ถํ ๋ชฉ๋ก(List<GrantedAuthority>
)์ ์์ฑ -
createAuthorities(List<String> roles)
๋ฉ์๋๊ฐ ์ถ๊ฐ๋HelloAuthorityUtils
ํด๋์ค1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
... @Component public class HelloAuthorityUtils { @Value("${mail.address.admin}") private String adminMailAddress; private final List<GrantedAuthority> ADMIN_ROLES = AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"); private final List<GrantedAuthority> USER_ROLES = AuthorityUtils.createAuthorityList("ROLE_USER"); private final List<String> ADMIN_ROLES_STRING = List.of("ADMIN", "USER"); private final List<String> USER_ROLES_STRING = List.of("USER"); // ๋ฉ๋ชจ๋ฆฌ ์์ Role์ ๊ธฐ๋ฐ์ผ๋ก ๊ถํ ์ ๋ณด ์์ฑ. public List<GrantedAuthority> createAuthorities(String email) { if (email.equals(adminMailAddress)) { return ADMIN_ROLES; } return USER_ROLES; } // (1) DB์ ์ ์ฅ๋ Role์ ๊ธฐ๋ฐ์ผ๋ก ๊ถํ ์ ๋ณด ์์ฑ public List<GrantedAuthority> createAuthorities(List<String> roles) { List<GrantedAuthority> authorities = roles.stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // (2) .collect(Collectors.toList()); return authorities; } ... ... }
๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ง๊ณ ์จ Role ๋ชฉ๋ก(
List<String> roles
)์ ๊ทธ๋๋ก ์ด์ฉํด์ ๊ถํ ๋ชฉ๋ก(authorities
)์ ๋ง๋ ๋ค.๐ก ์ฃผ์ํด์ผ ํ ๊ฒ : (2)์ ๊ฐ์ด
SimpleGrantedAuthority
๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ์์ฑ์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ฒจ์ฃผ๋ ๊ฐ์ด
โUSER
โ ๋๋ โADMIN
โ์ผ๋ก ๋๊ฒจ์ฃผ๋ฉด ์ ๋๊ณ โROLE_USER
โ ๋๋ โROLE_ADMIN
โ ํํ๋ก ๋๊ฒจ์ฃผ์ด์ผ ํ๋ค
- (1) : HelloUserDetails๊ฐ ์์ํ๊ณ ์๋ Member(
7๏ธโฃ Custom AuthenticationProvider๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- Custom UserDetailsService๋ฅผ ์ฌ์ฉํด ๋ก๊ทธ์ธ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ Spring Security๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ธ์ฆ์ ๋์ ์ฒ๋ฆฌํด ์ฃผ๋ ๋ฐฉ์
-
Custom AuthenticationProvider๋ฅผ ์ด์ฉํด ์ฐ๋ฆฌ๊ฐ ์ง์ ๋ก๊ทธ์ธ ์ธ์ฆ์ ์ฒ๋ฆฌํ ๊ฒ.
- HelloUserAuthenticationProvider ํด๋์ค
- AuthenticationProvider๋ฅผ ๊ตฌํํ ๊ตฌํ ํด๋์ค๊ฐ Spring Bean์ผ๋ก ๋ฑ๋ก๋์ด ์๋ค๋ฉด
ํด๋น AuthenticationProvider๋ฅผ ์ด์ฉํด์ ์ธ์ฆ์ ์งํ- ๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ ์ชฝ์์ ๋ก๊ทธ์ธ ์ธ์ฆ์ ์๋ํ๋ฉด ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ HelloUserAuthenticationProvider๊ฐ ์ง์ ์ธ์ฆ์ ์ฒ๋ฆฌ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
package com.springboot.utils; import com.springboot.auth.HelloUserDetailsServiceV2; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Optional; @Component public class HelloUserAuthenticationProvider implements AuthenticationProvider { private final HelloUserDetailsServiceV2 helloUserDetailsServiceV2; private final PasswordEncoder passwordEncoder; public HelloUserAuthenticationProvider(HelloUserDetailsServiceV2 helloUserDetailsServiceV2, PasswordEncoder passwordEncoder) { this.helloUserDetailsServiceV2 = helloUserDetailsServiceV2; this.passwordEncoder = passwordEncoder; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UsernamePasswordAuthenticationToken authToken = (UsernamePasswordAuthenticationToken) authentication; String username = authToken.getName(); Optional.ofNullable(username).orElseThrow(()-> new UsernameNotFoundException("Invalid User name or User Password")); // (2) ํ์๊ฐ์ ์ ํ์ง ์๊ณ ๋ก๊ทธ์ธํ๋ฉด ์๊ธฐ๋ ๋ฌธ์ ์ ๊ฐ์ try { UserDetails userDetails = helloUserDetailsServiceV2.loadUserByUsername(username); String password = userDetails.getPassword(); //์ํธํ ์๋์ด์์ verifyCredentials(authToken.getCredentials(), password); //๋ก๊ทธ์ธ ์ ๋ณด์ ํฌํจ๋ ํจ์ค์๋์ DB์ ์ ์ฅ๋ ํจ์ค์๋ ์ ๋ณด๊ฐ ์ผ์นํ๋์ฆ //verifyCredentials ์์ ์ํธํ ์ด๋ฃจ์ด์ง(๋จ๋ฐฉํฅ ์ํธํ - ์ผ๋ฐ์ ์ผ๋ก ๋ณต๊ตฌ๊ฐ ๋ถ๊ฐ๋ฅ) Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities(); return UsernamePasswordAuthenticationToken.authenticated(username, password, authorities); }catch (Exception e){ throw new UsernameNotFoundException(e.getMessage()); } } //support๋ฉ์๋๋ ์ฐ๋ฆฌ๊ฐ ๋ง๋ ํ๋ก๋ฐ์ด๋๊ฐ User๊ธฐ๋ฐ์ password ๋ฐฉ์์ด๋ผ๊ณ ์คํ๋ง ์ํ๋ฆฌํฐ์ ์๋ ค์ฃผ๋ ๊ฒ, // ์ ์ด์ ๊ตฌํ๋์ด ์๋ ๋ฉ์๋๋ผ์ ๋ฐฉ์์ ๋ง์ถฐ์ ์ฌ์ฉํ๊ธฐ. //supports() ๋ฉ์๋์ ๋ฆฌํด๊ฐ์ด true์ผ ๊ฒฝ์ฐ, Spring Security๋ ํด๋น AuthenticationProvider์ authenticate() ๋ฉ์๋๋ฅผ ํธ์ถํด์ ์ธ์ฆ์ ์งํ @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.equals(authentication); } private void verifyCredentials(Object credentials, String password){ if(!passwordEncoder.matches((String) credentials, password)){ //์ธ์ฝ๋ฉ ํ๋ค์์ ๋๋ค ์ํธํ๋ฅผ ํ๋ค.(๋จ๋ฐฉํฅ ์ํธํ - ์ผ๋ฐ์ ์ผ๋ก ๋ณต๊ตฌ๊ฐ ๋ถ๊ฐ๋ฅ) // ๊ฐ์ผ๋ฉด true, ๋ค๋ฅด๋ฉด false throw new BadCredentialsException("Invaild User name or User Password"); } } }
- AuthenticationProvider๋ฅผ ๊ตฌํํ ๊ตฌํ ํด๋์ค๊ฐ Spring Bean์ผ๋ก ๋ฑ๋ก๋์ด ์๋ค๋ฉด
- Custom AuthenticationProvider๋ฅผ ์ด์ฉํ ๊ฒฝ์ฐ ํ์๊ฐ์
์ ๋ก๊ทธ์ธ ํ๋ฉด ๋ํ๋ ๋ฌธ์ ์
-
ํ์๊ฐ์ ์ ํ์ง ์์ ์ฑ๋ก ๋ก๊ทธ์ธ์ ํ๋ฉด ์๋์ ๊ฐ์ ํ๋ฉด์ด ๋ํ๋๋ค.
- ์ปจํธ๋กค๋ฌ๋ฅผ ๊ฑฐ์น์ง ์์์ BusinessLogicException์ ์ฒ๋ฆฌํ์ง ๋ชปํจ.
- filterChain์ ๊ฐ์ฅ ๋๊น์ง ์ฌ๋ผ๊ฐ ํ ์๋ธ๋ฆฟ์ ์ง๋ ํด๋ผ์ด์ธํธ์ ์์ฒญ ์ต์๋จ์ผ๋ก ๊ฐ๊ฒ ๋จโ ์ฆ, ํฐ์บฃ์์ ๋ฐ์ํ ์์ธ์ด๋ค
๊ฐ์ ํ๋ ค๋ฉด .Cusotm AuthenticationProvider์์ Exception์ด ๋ฐ์ํ ๊ฒฝ์ฐ, ์ด Exception์ catch ํด์ AuthenticationException์ผ๋ก rethrow๋ฅผ ํด์ฃผ๋ฉด ๋๋ค.
โ(2) ์์ try-catch๋ก ๊ฐ์ผ ์ด์ ๋ ์์ธ๋ฅผ ๋์ ธ ๊ฐ์ ํ๊ธฐํจ -
- HelloUserAuthenticationProvider ํด๋์ค
Leave a comment