Search

์•„์ด๋”” ์ €์žฅ

์•„์ด๋”” ์ €์žฅ

Spring Security 6

์ด์ „ ํŽ˜์ด์ง€

์ด์ „ ํŽ˜์ด์ง€ ๋‚ด์šฉ์— ์ด์–ด์„œ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Code

Preview

1.
๋กœ๊ทธ์ธ ํ™”๋ฉด
2.
๋ฉ”์ธ ํ™”๋ฉด

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2.
ํ”„๋กœ์ ํŠธ ์„ค์ •
3.
์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •
4.
์š”์ฒญ ๊ฒฝ๋กœ ๋งคํ•‘

Preview

๋กœ๊ทธ์ธ ํ™”๋ฉด

๋ฉ”์ธ ํ™”๋ฉด

์ž‘์—… ํ”„๋กœ์„ธ์Šค

1.
ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2.
ํ”„๋กœ์ ํŠธ ์„ค์ •
3.
๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ฒ˜๋ฆฌ ํด๋ž˜์Šค ์ƒ์„ฑ
a.
์•„์ด๋”” ์ €์žฅ ์ฒดํฌ ์—ฌ๋ถ€ ํ™•์ธ
i.
์•„์ด๋”” ์ฟ ํ‚ค ์ƒ์„ฑ
ii.
์•„์ด๋”” ์ฟ ํ‚ค ์‚ญ์ œ
4.
๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์†Œ๋“œ
a.
์•„์ด๋”” ์ฟ ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

build.gradle

spring boot 3.x.x
spring security 6.x.x
plugins { id 'java' id 'war' id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' } group = 'com.aloha' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }
Java
๋ณต์‚ฌ

ํ”„๋กœ์ ํŠธ ์„ค์ •

application.properties

spring.application.name=form-custom # ๋ฐ์ดํ„ฐ ์†Œ์Šค - MySQL spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/aloha?serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&useSSL=false&autoReconnection=true&autoReconnection=true spring.datasource.username=aloha spring.datasource.password=123456 # Mybatis ์„ค์ • mybatis.configuration.map-underscore-to-camel-case=true mybatis.type-aliases-package=com.aloha.security6.domain mybatis.mapper-locations=classpath:mybatis/mapper/**/**.xml
Markdown
๋ณต์‚ฌ

๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ฒ˜๋ฆฌ ํด๋ž˜์Šค ์ƒ์„ฑ

1.
์•„์ด๋”” ์ €์žฅ ์ฒดํฌ ์—ฌ๋ถ€ ํ™•์ธ
a.
์•„์ด๋”” ์ฟ ํ‚ค ์ƒ์„ฑ
b.
์•„์ด๋”” ์ฟ ํ‚ค ์‚ญ์ œ

LoginSuccessHandler.java

/** * ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ฒ˜๋ฆฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ */ @Slf4j @Component public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { /** * ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์†Œ๋“œ * ๐Ÿช ์•„์ด๋”” ์ €์žฅ ์ฟ ํ‚ค ์ƒ์„ฑ * ๐Ÿ” ๋กœ๊ทธ์ธ ํ›„ ์ด์ „ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ */ @Override public void onAuthenticationSuccess(HttpServletRequest request , HttpServletResponse response , Authentication authentication) throws ServletException, IOException { log.info("๋กœ๊ทธ์ธ ์„ฑ๊ณต..."); // ์•„์ด๋”” ์ €์žฅ String rememberId = request.getParameter("remember-id"); // โœ… ์•„์ด๋”” ์ €์žฅ ์—ฌ๋ถ€ String username = request.getParameter("id"); // ๐Ÿ‘ฉโ€๐Ÿ’ผ ์•„์ด๋”” log.info("rememberId : " + rememberId); log.info("username : " + username); // ์•„์ด๋”” ์ €์žฅ ์ฒดํฌ โœ… if( rememberId != null && rememberId.equals("on") ) { Cookie cookie = new Cookie("remember-id", username); // ์ฟ ํ‚ค์— ์•„์ด๋”” ๋“ฑ๋ก cookie.setMaxAge(60 * 60 * 24 * 7); // ์œ ํšจ๊ธฐ๊ฐ„ : 7์ผ cookie.setPath("/"); response.addCookie(cookie); } // ์•„์ด๋”” ์ €์žฅ ์ฒดํฌ โŒ else { Cookie cookie = new Cookie("remember-id", username); // ์ฟ ํ‚ค์— ์•„์ด๋”” ๋“ฑ๋ก cookie.setMaxAge(0); // ์œ ํšจ๊ธฐ๊ฐ„ : 0 (์‚ญ์ œ) cookie.setPath("/"); response.addCookie(cookie); } // ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž ์ •๋ณด CustomUser customUser = (CustomUser) authentication.getPrincipal(); Users user = customUser.getUser(); log.info("์•„์ด๋”” : " + user.getUsername()); log.info("๋น„๋ฐ€๋ฒˆํ˜ธ : " + user.getPassword()); log.info("๊ถŒํ•œ : " + user.getAuthList()); super.onAuthenticationSuccess(request, response, authentication); } }
Java
๋ณต์‚ฌ

๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์†Œ๋“œ

1.
์•„์ด๋”” ์ฟ ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ

HomeController.java

/** * ๋กœ๊ทธ์ธ ํ™”๋ฉด * @return */ @GetMapping("/login") public String login(@CookieValue(value="remember-id", required = false) Cookie cookie ,Model model ) { // @CookieValue(value="์ฟ ํ‚ค์ด๋ฆ„", required = ํ•„์ˆ˜์—ฌ๋ถ€) // - required=true (default) : ์ฟ ํ‚ค๋ฅผ ํ•„์ˆ˜๋กœ ๊ฐ€์ ธ์™€์„œ ์—†์œผ๋ฉด ์—๋Ÿฌ // - required=false : ์ฟ ํ‚ค ํ•„์ˆ˜ โŒ โžก ์ฟ ํ‚ค๊ฐ€ ์—†์œผ๋ฉด null, ์—๋ŸฌโŒ log.info(":::::::::: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ::::::::::"); String username = ""; boolean rememberId = false; if( cookie != null ) { log.info("CookieName : " + cookie.getName()); log.info("CookieValue : " + cookie.getValue()); username = cookie.getValue(); rememberId = true; } model.addAttribute("username", username); model.addAttribute("rememberId", rememberId); return "/login"; }
Java
๋ณต์‚ฌ

login.html

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>๋กœ๊ทธ์ธ</title> <!-- bootstrap css --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container col-12 col-md-6 col-lg-4"> <div class="px-4 py-5 mt-5 text-center"> <h1 class="display-5 fw-bold text-body-emphasis">๋กœ๊ทธ์ธ</h1> </div> <!-- ๋กœ๊ทธ์ธ ์˜์—ญ --> <main class="form-signin login-box w-100 m-auto"> <form action="/login" method="post"> <!-- CSRF TOKEN --> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> <div class="form-floating"> <input type="text" class="form-control" id="username" name="id" value="" placeholder="์•„์ด๋””" autofocus th:value="${username}"> <label for="username">์•„์ด๋””</label> </div> <div class="form-floating"> <input type="password" class="form-control" id="password" name="pw" placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ"> <label for="password">๋น„๋ฐ€๋ฒˆํ˜ธ</label> </div> <div class="form-check text-start my-3 d-flex justify-content-around"> <div class="item"> <input class="form-check-input" type="checkbox" name="remember-id" id="remember-id-check" th:checked="${rememberId}"> <label class="form-check-label" for="remember-id-check">์•„์ด๋”” ์ €์žฅ</label> </div> <div class="item"> <input class="form-check-input" type="checkbox" name="auto-login" id="remember-me-check"> <label class="form-check-label" for="remember-me-check">์ž๋™ ๋กœ๊ทธ์ธ</label> </div> </div> <!-- ๋กœ๊ทธ์ธ ์—๋Ÿฌ --> <th:block th:if="${param.error}"> <p class="text-center text-danger">์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค.</p> </th:block> <!-- ๋กœ๊ทธ์•„์›ƒ ์™„๋ฃŒ --> <th:block th:if="${param.logout}"> <p class="text-center text-success">์ •์ƒ์ ์œผ๋กœ ๋กœ๊ทธ์•„์›ƒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.</p> </th:block> <!-- ๋ฒ„ํŠผ --> <div class="d-grid gap-2"> <button class="btn btn-lg btn-primary w-100 py-2" type="submit">๋กœ๊ทธ์ธ</button> <a href="/join" class="btn btn-lg btn-success w-100 py-2">ํšŒ์›๊ฐ€์ž…</a> <hr> </div> </form> </main> </div> <!-- bootstrap --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script> </body> </html>
HTML
๋ณต์‚ฌ