목표
1. Password Encode 구현
2. 로그인 했을 때/안했을 때에 따른 로그인 구현
register.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link th:href="@{https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
<title>회원가입</title>
</head>
<body class="text-center">
<form class="form-signin" th:action="@{/account/register}" method="post">
<a th:href="@{/}"><img class="mb-4" src="https://getbootstrap.com/docs/4.5/assets/brand/bootstrap-solid.svg"
alt="" width="72" height="72"></a>
<h1 class="h3 mb-3 font-weight-normal">회원가입</h1>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
<!-- <div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div> -->
<button class="btn btn-lg btn-primary btn-block" type="submit">회원가입</button>
<p class="mt-5 mb-3 text-muted">© 2017-2020</p>
</form>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
</body>
</html>
username과 password를 입력 후 회원가입 버튼을 누르면 /account/register 요청 발생
AccountController
@Controller
@RequestMapping("/account")
public class AccountController {
@Autowired
private UserService userService;
@GetMapping("/login")
public String login(){
return "account/login";
}
@GetMapping("/register")
public String register(){
return "account/register";
}
@PostMapping("/register")
public String register(User user){
userService.save(user);
return "redirect:/";
}
}
PostMapping(추가)로 요청 받아서 userService의 save메소드로 user 전달
localhost:8080으로 이동(redirext:/)
UserService
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public User save(User user){
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
user.setEnabled(true);
Role role = new Role();
role.setId(1l);
user.getRoles().add(role);
return userRepository.save(user);
}
}
PasswordEncoder를 Autowired 해서 encode 메소드를 통해 비밀번호 암호화 구현
role은 하드코딩으로 1l을 설정하여 return
그럼 이렇게 들어간다!
암호화 더 복잡한 줄 알았는데.. 생각보다 간단하게 구현돼서 당황
다만 복호화는 할 수 없는 일방향 암호화이다. enabled=1은 활성화된 유저임을 의미한다.
로그인 상태에 따라 nav의 버튼이 다르게 뜨고, 로그인 되지 않으면 게시판을 볼 수 없게 설정..하는 과정에서 맨 마지막에 문제가 생겼다. dependency를 추가했는데 구글링해서 온갖 해결법을 다 해봐도 dependency not found가 뜬다..
springsecurity5 빼고는 다 잘 되는데.. 하.. 어쩔 수 없이 일단 진행했다
login.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link th:href="@{https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
<title>Login</title>
</head>
<body class="text-center">
<form class="form-signin" th:action="@{/account/login}" method="post">
<a th:href="@{/}"><img class="mb-4" src="https://getbootstrap.com/docs/4.5/assets/brand/bootstrap-solid.svg"
alt="" width="72" height="72"></a>
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<div th:if="${param.error}" class="alert alert-danger" role="alert">
Invalid username and password.
</div>
<div th:if="${param.logout}" class="alert alert-primary" role="alert">
You have been logged out.
</div>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
<!-- <div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div> -->
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2020</p>
</form>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
</body>
</html>
username과 passowrd를 입력해서 error가 발생하면 arror가 파라미터로 넘어가서 param.error 부분에 의해 alert 메세지가 출력된다. 로그인에 성공하면 /account/login 요청 발생
WebSecurityConfig
: Spring Security 구현
package com.javalab.myhome.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Autowired
private DataSource dataSource;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/","/account/register", "/css/**").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/account/login")
.permitAll()
)
.logout((logout) -> logout.permitAll());
return http.build();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username, password, enabled "
+ "from user "
+ "where username = ?")
.authoritiesByUsernameQuery("select u.username, r.name "
+ "from user_role ur join user u on ur.user_id = u.id "
+ "join role r on ur.role_id = r.id "
+ "where u.username = ?");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@Configuration : Spring Security 구성 정의
@EnaleWebSecurity : Spring Security 활성화
DB와 연결하기 위해 DataSource Autowired
SecurityFilterChain : HttpSecurity 객체를 매개변수로 받아서 보안 구성 설정
.authorizeHttpRequests
.requestMatchers : 이곳에 정의한 url mapping은 모든 사용자에게 허용(permitAll)
.anyRequest().authenticated() : 그 외 모든 요청은 인증된 사용자에게만 허용.
.formLogin : 로그인에 성공하면 이동할 페이지
.logout : 로그아웃 구성
configureGlobal(AuthenticationManagerBuilder auth) : AuthenticationManagerBuilder를 통해 인증 관련 설정
jdbcAuthentication : JDBC 기반 사용자 인증 활성화
dataSource : DataSource 설정
passwordEncoder : 비밀번호의 인코딩 방식 설정
userByUsernameQuery : 사용자 정보 조회 쿼리
authoritiesByUsernameQuery : 사용자의 권한 정보 조회
passwordEncoder : PasswordEncoder Bean 정의
common.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head th:fragment="head(title)">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link href="css/starter-templates.css" th:href="@{/css/starter-templates.css}" rel="stylesheet">
<title th:text="${title}">Hello, Spring Boot!</title>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:fragment="menu(menu)">
<a class="navbar-brand" href="#">Spring Boot Tutorial</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault"
aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item" th:classappend="${menu == 'home'} ? 'active'">
<a class="nav-link" href="#" th:href="@{/}">홈<span class="sr-only"
th:if="${menu == 'home'}">(current)</span></a>
</li>
<li class="nav-item" th:classappend="${menu == 'board'} ? 'active'">
<a class="nav-link" href="#" th:href="@{/board/list}">게시판<span class="sr-only"
th:if="${menu == 'board'}">(current)</span></a>
</li>
</ul>
<a class="btn btn-secondary my-2 mr-2 my-sm-0" th:href="@{/account/login}"
sec:authorize="!isAuthenticated()">로그인</a>
<a class="btn btn-secondary my-2 my-sm-0" th:href="@{/account/register}"
sec:authorize="!isAuthenticated()">회원가입</a>
<form class="form-inline my-2 my-lg-0" th:action="@{/logout}" method="POST"
sec:authorize="isAuthenticated()">
<!-- <input class="form-control mr-sm-2" type="text" placeholder="Search" area-label="Search"> -->
<span class="text-white" sec:authentication="name">사용자</span>
<span class="text-white mx-2" sec:authentication="principal.authorities">권한</span>
<button class="btn btn-secondary my-2 my-sm-0" type="submit">Logout</button>
</form>
</div>
</nav>
</body>
</html>
인증되지 않은 사용자는 로그인, 회원가입 버튼이 보이고
인증된 사용자는 사용자, 권한 정보와 로그아웃 버튼이 보인다
ㅋㅋ 어떻게든 dependency 문제 해결하고 싶어 구글링하다.. 찾아서 해결했다
내가 쓰는 버전이 spring-boot-starter-parent 3.2.2버전이기 때문에 Spring Security6를 사용해야 했다
강의보고 그냥 따라하기만 하니.. 이 문제일줄은 몰랐음 ^^!
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
'온라인 강좌 > 유튜브 강의' 카테고리의 다른 글
Spring Boot 11. JPA로 조회방법(FetchType) 설정하기 (0) | 2024.02.20 |
---|---|
Spring Boot 10. JPA를 이용하여 @OneToMany 관계 설정하기 (0) | 2024.02.20 |
Spring Boot 8. JPA를 이용한 페이지 처리 및 검색 (0) | 2024.02.19 |
Spring Boot 7. JPA를 이용한 RestfulAPI 작성 (0) | 2024.02.18 |
Spring Boot 6. thymeleaf에서 form 전송하기 (0) | 2024.02.17 |