관련 화면
관련 코드
1. Spring Security dependency 추가하기
build.gradle에 security와 관련된 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
의존성 추가 후 프로그램을 작동시키면 스프링 시큐리티에서 제공하는 로그인 페이지를 볼 수 있다.
콘솔창을 보고 비밀번호 입력할 수 있다
- Username : user
- Password : 콘솔창
이게 너무 귀찮으면 application.properties에서 기본 스프링 시큐리티 username이랑 password를 설정할 수 있다.
2. Security Config 작성
package kr.co.crud.security;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/* SecurityConfig.java -> Security Filter Chain
* (1) HTTP 요청에 대한 보안 작업
* - UsernamePasswordAuthenticationFilter : 사용자가 입력한 인증 정보로 Authentication 객체 생성
* - http.formLogin() 등 메서드를 통해 설정
* (2) 사용자 인증(로그인 설정), 인가(접근 권한 설정), 로그아웃 설정
* */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
/* 인가(접근권한) 설정 */
http.authorizeHttpRequests().antMatchers("/", "/user/login", "/user/register").permitAll(); // 경로에 대해서 모든 사용자에게 접근을 허용한다는 의미
http.authorizeHttpRequests().antMatchers("/board/**").hasAnyRole("1", "2");
/* 사이트 위변조 요청 방지
* CRSF(Cross-Site Request Forgery) 공격 방지 기능을 비활성화
* disable -> CRSF 토큰을 사용하지 않도록 설정
* */
//http.csrf().disable();
/* 로그인 설정 */
http.formLogin()
.loginPage("/user/login") // 로그인 페이지 경로 설정 (해당 경로를 통해 로그인 진행)
.defaultSuccessUrl("/") // 로그인 성공 시 이동 경로
.failureUrl("/user/login?success=100") // 로그인 실패 시 이동 경로
.usernameParameter("uid") // 로그인 폼에서 사용자 아이디를 입력받는 Input 필드 이름 지정 (name="uid")
.passwordParameter("pass"); // 로그인 폼에서 사용자 비밀번호를 입력받는 Input 필드 이름 지정 (name="pass")
/* 로그아웃 설정 */
http.logout()
.invalidateHttpSession(true) // 로그아웃 시 세션 무효화
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout")) // 로그아웃 요청 URL (POST 요청 URL)
.logoutSuccessUrl("/user/login?success=200"); // 로그아웃 성공 시 이동 경로
/* 로그인 시 세션 유지 */
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
//return http.build(); // build: SecurityConfigurerAdapter의 메서드 중 하나로, HttpSecurity 객체를 반환
}
/* 비밀번호 암호화
* BCrypt 해시 함수를 사용하여 비밀번호를 암호화
* */
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
사용자가 폼을 통해 로그인 시도를 할 때 해당 폼에 입력한 정보를 http requset 로 서버에 전송을 하고 security config에서 security filterchain을 통해 http 요청에 대한 보안 작업을 하고 또한 사용자가 입력한 입력한 인증정보로 authentication 객체를 생성한다.
3. MyUserDetails 작성
package kr.co.crud.security;
import kr.co.crud.entity.UserEntity;
import lombok.Builder;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/*
* AuthenticationManager => UserDetails
* - 생성된 Authentication 객체를 검증 및 인증을 수행
* */
@Data
@Builder
public class MyUserDetails implements UserDetails {
/*
* 직렬화(Serializable)
* - 자바의 객체를 바이트의 배열로 변환하여 DB에 저장
* */
private static final long serialVersionUID = 1L;
@Autowired
private UserEntity user;
/*
* getAuthorities()
* - 사용자가 가지고 있는 권한을 반환하는 메서드
* - 반환타입: Collection<? extends GrantedAuthority>
* */
// <? extends GrantedAuthority>: 와일드카드('?')를 사용하여 GrantedAuthority가 확장한 타입이 될 수 있음을 명시
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// GrantedAuthority 객체들을 저장하기 위한 리스트 생성
List<GrantedAuthority> authorities = new ArrayList<>();
// 사용자의 권한을 나타내는 SimpleGrantedAuthority 객체를 생성하여 리스트에 추가
// 일반적으로 Spring Security의 규약에 따라 "ROLE_" 접두어를 붙여 권한 생성
authorities.add(new SimpleGrantedAuthority("ROLE_"+user.getGrade()));
return authorities;
}
/*
* getUsername()
* - 사용자의 아이디를 반환하는 메서드
* */
@Override
public String getUsername() {
return user.getUid();
}
/*
* getPassword()
* - 사용자의 비밀번호를 반환하는 메서드
* */
@Override
public String getPassword() {
return user.getPass();
}
public String getNickname() {
return user.getName();
}
/* 그외 */
// 계정 만료 여부 (true: 만료X, false: 만료)
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정 잠김 여부 (true: 잠김X, false: 잠김)
@Override
public boolean isAccountNonLocked() {
return true;
}
// 계정 비밀번호 만료 여부 (true: 만료X, false: 만료)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정 활성화 여부 (true: 활성화, false: 비활성화)
@Override
public boolean isEnabled() {
return true;
}
}
만들어진 authentication 객체를 검증 및 인증을 수행한다.
4. SecurityUserService
package kr.co.crud.security;
import kr.co.crud.entity.UserEntity;
import kr.co.crud.repository.UserRepo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.Service;
/*
* UserDetailsService
* - UserDetails 객체를 사용해 인증 및 권한 부여
* */
@Service
@Slf4j
public class SecurityUserService implements UserDetailsService {
// 레포지토리를 통해 데이터베이스에서 사용자 정보를 조회
@Autowired
private UserRepo repo;
// loadUserByUsername: 사용자의 아이디(username)을 기반으로 사용자 정보를 가져온다
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.warn("SecurityUserService loadUserByUsername");
// 데이터베이스에서 사용자 정보를 조회
UserEntity user = repo.findById(username).get();
if(user == null) {
throw new UsernameNotFoundException(username);
}
// 조회한 사용자 정보로 UserDetails 객체 생성
UserDetails myUser = MyUserDetails.builder()
.user(user)
.build();
return myUser;
}
}
3번에서 통과한 userdetails 객체를 사용해서 인증 및 권한을 검사한다.
5. 로그인 페이지 HTML
<form class="loginForm" th:action="@{/user/login}" method="post">
<div class="content_main">
<div class="input_id_row">
<div class="img_id"><img src="/images/id.png"></div>
<input type="text" class="input_id" name="uid" placeholder="아이디">
</div>
<div class="input_pw_row">
<div class="img_pw"><img src="/images/pw.png"></div>
<input type="password" class="input_pw" name="pass" placeholder="비밀번호">
</div>
<div class="login_btn_row">
<input type="submit" class="login_btn" value="로그인">
</div>
</div>
</form>
Security Configdptj 작성했던 설정으로 로그인 화면 구현한다.
'project > personal project' 카테고리의 다른 글
[CRUD] 기능구현 - 단일 파일첨부 업로드 및 다운로드 (0) | 2024.01.10 |
---|---|
[CRUD] 기능구현 - 게시글 작성 (네이버 스마트 에디터) (0) | 2024.01.10 |
[CRUD] 기능구현 - Daum (KaKao) 우편번호 API (0) | 2024.01.10 |
[CRUD] DB설정 (0) | 2023.12.30 |
[CRUD] 화면 구현 (0) | 2023.12.30 |