1. 스프링 시큐리티 설정 추가하기
security dependency 추가하기
해당 의존성을 추가하고 라이브러리를 받아옵니다.
//pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
애플리케이션을 실행하고 기존에 작성했던 localhost:8080/community/freeboard URL에 접근했을 때, 스프링 시큐리티에서 제공하는 로그인 페이지로 이동됩니다.
스프링 시큐리티에서 기본적으로 제공하는 아이디는 user이고, 비밀번호는 애플리케이션을 실행할 때마다 콘솔창에 출력해서 보여줍니다.
스프링 시큐리티를 추가하는 것만으로도 모든 요청이 인증을 필요로 하게 됩니다. 하지만 매번 스프링 시큐리티 콘솔창에서 비밀번호를 찾아 입력할 수 없기 때문에 회원 가입 기능과 각 페이지마다 필요한 권한처리를 하도록 하겠습니다.
인증이 필요없는 경우: 게시물조회
인증이 필요한 경우: 게시물작성, 댓글작성
스프링 시큐리티 설정하기
설정을 관리할 config라는 패키지를 만들고 스프링시큐리티를 설정을 처리할 SecurityConfig 클래스를 만들겠습니다.
package com.wine.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
- @EnableWebSecurity: WebSecurityConfigurerAdapter를 상속받는 클래스에 해당 어노테이션을 선언하면 SpringSecurityFilterChain이 자동으로 포함됩니다.
- void configure(HttpSecurity http): http 요청에 대한 보안을 설정합니다.
- PasswordEncoder passwordEncoder(): 비밀번호를 그대로 저장하지 않고 BCryptPasswordEncoder의 해시 함수를 이용하여 암호화처리합니다.
2. 회원가입 기능 구현하기
사용자 정보를 나타내는 엔티티를 작성합니다. 이 클래스는 데이터베이스의 'user' 테이블과 연결되어 있으며, 사용자의 이메일, 비밀번호, 역할 등을 포함합니다.
UserEntity생성
package com.wine.demo.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Collection;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
private String email;
@Column(length = 64)
private String resetToken;
private boolean enabled;
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
}
UserRepository 생성
User엔티티를 만들었다면 데이터베이스에 저장 할수 있도록 UserRepository를 만들겠습니다.
package com.wine.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
// 사용자 이름으로 사용자를 찾는 메서드
User findByUsername(String username);
// 이메일로 사용자를 찾는 메서드
User findByEmail(String email);
}
- User findByUser(String username): 회원 가입시 중복된 회원이 있는지 검사하기 위해 ID로 회원을 검사하는 메소드를 작성합니다.
- Member findByEmail(String email): 회원 가입시 중복된 회원이 있는지 검사하기 위해 이메일로 회원을 검사하는 메소드를 작성합니다.
UserService 생성
service 패키지를 만들고 UserService 클래스를 작성합니다.
package com.wine.demo.service;
import com.wine.demo.model.User;
import com.wine.demo.repository.UserRepository;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 새로운 사용자를 저장하거나 기존 사용자 정보를 업데이트합니다.
public void save(User user, boolean encodePassword) {
if (encodePassword) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
}
userRepository.save(user);
}
// 사용자의 현재 비밀번호가 맞는지 확인합니다.
public boolean checkCurrentPassword(String currentPassword) {
User user = getAuthenticatedUser();
if (user == null) {
throw new UsernameNotFoundException("사용자를 찾을 수 없습니다.");
}
return passwordEncoder.matches(currentPassword, user.getPassword());
}
@Autowired 어노테이션이 없이 의존성 주입이 가능합니다.
UserController생성
controller패키지 안에 UserController를 작성합니다. ID중복확인, 인증이메일 전송 조건을 처리하고 모든 조건이 충족되면 새사용자를 저장하고 로그인 페이지로 redirect 합니다. 만약 조건이 충족되지않으면 다시 회원가입 페이지로 돌아갑니다.
package com.wine.demo.controller;
@Controller
public class UserController {
// 회원가입 페이지
@GetMapping("/register")
public String register() {
return "login/register";
}
// id 중복확인
@PostMapping("/checkUsername")
@ResponseBody
public ResponseEntity<Boolean> checkUsername(@RequestBody Map<String, String> payload) { //json형식
String username = payload.get("username");
boolean isAvailable = userService.checkUsernameAvailability(username);
return new ResponseEntity<>(isAvailable, HttpStatus.OK);
}
// 인증이메일 전송처리
@PostMapping("/sendVerificationEmail")
@ResponseBody
public ResponseEntity<String> sendVerificationEmail(@RequestBody Map<String, String> payload) {
String email = payload.get("email");
try {
userService.sendVerificationEmail(email);
} catch (MailException e) {
return new ResponseEntity<>("이메일 전송에 실패했습니다: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
} catch (Exception e) {
return new ResponseEntity<>("서버 에러: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>("이메일 전송에 성공했습니다. 이메일을 확인해주세요!", HttpStatus.OK);
}
// 회원가입 요청을 처리
@PostMapping("/register")
@ResponseBody // HTTP 요청 본문의 내용을 자바 객체로 변환하여 매개변수에 바인딩.
이 경우에는 요청 본문은 Map<String, String> 형태로 변환
public String processRegister(@RequestBody Map<String, String> payload) {
String username = payload.get("username");
String password = payload.get("password");
String passwordConfirm = payload.get("passwordConfirm");
String email = payload.get("email");
String verificationCode = payload.get("verificationCode");
if (!password.equals(passwordConfirm)) {
return "register";
}
VerificationCode code = userService.findVerificationCode(verificationCode, email);
if (code != null) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setEmail(email);
user.setEnabled(true);
user.setRole("USER");
userService.save(user, true);
return "redirect:/login";
} else {
return "register";
}
}
register.html 작성
Controller에서 작성한 메소드 리턴값에 맞게 경로를 지정합니다.
저는 resources하위인 templatese에서 아래에 login 폴더를 만들고 그 아래에 register.html을 작성했습니다. 부트스트랩 register 를 검색하면 다양한 폼이 있어서 다양하게 꾸밀 수 있습니다.
<!DOCTYPE html>
<html lang="ko" class="signup">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>KOWASA 회원가입</title>
<!-- Font Icon -->
<link rel="stylesheet" href="fonts/material-icon/css/material-design-iconic-font.min.css">
<!-- Main css -->
<link rel="stylesheet" href="css/style_register.css">
</head>
<body>
<div class="main">
<section >
<div class="container">
<div class="signup-content">
<form method="POST" id="signup-form" class="signup-form">
<h2 class="form-title">회원가입</h2>
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center;">
<input type="text" class="form-input" name="name" id="username" placeholder="아이디" style="flex-grow: 1; margin-right: 20px;"/>
<input type="button" name="submit" id="checkUsername" class="form-submit" value="아이디중복확인" style="width: 150px;"/>
</div>
<div id="usernameResult"></div>
<div class="form-group">
<input type="password" class="form-input" name="password" id="password" placeholder="비밀번호"/>
<span toggle="#password" class="zmdi zmdi-eye field-icon toggle-password"></span>
</div>
<div class="form-group">
<input type="password" class="form-input" name="re_password" id="passwordConfirm" placeholder="비밀번호 재확인"/>
</div>
<div class="form-group" style="display: flex; justify-content: space-between; align-items: center;">
<input type="email" class="form-input" name="email" id="email" placeholder="이메일" style="flex-grow: 1; margin-right: 20px;"/>
<input type="button" name="sendVerificationCode" id="sendVerificationCode" class="form-submit" value="인증번호전송" style="width: 150px;"/>
</div>
<div class="form-group">
<input type="text" class="form-input" name="verificationCode" id="verificationCode" placeholder="인증번호를 입력해주세요"/>
</div>
<div class="form-group">
<input type="submit" name="submit" id="submit" class="form-submit" value="회원가입"/>
</div>
</form>
<p class="loginhere">
이미 가입을 하셨나요? <a href="/login" class="loginhere-link">로그인</a>
</p>
</div>
</div>
</section>
</div>
<!-- JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="js/main_register.js"></script>
<script src="js/register.js"></script>
</body>
</html>
'[JAVA PROJECT]' 카테고리의 다른 글
JAVA Spring Boot 프로젝트- 5. 자유게시판 구현(페이징, 조회수) (1) | 2023.12.29 |
---|---|
JAVA Spring Boot 프로젝트- 4. 와인 검색 페이지 구현 (0) | 2023.12.28 |
JAVA Spring Boot 프로젝트- 3. 아이디/비밀번호 찾기 기능 구현하기 (0) | 2023.12.27 |
JAVA Spring Boot 프로젝트-2. Oauth 2.0 구글로그인 구현하기 (0) | 2023.12.27 |
JAVA Spring Boot 프로젝트- 1. 스프링시큐리티를 이용한 회원가입 및 로그인 구현하기(2) (1) | 2023.12.23 |