[SpringBoot] Security 회원가입과 로그인
[SpringBoot] Security 회원가입과 로그인
● 회원가입
1. View Resolover 파일설정
// WebMvcConfig.java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry){
MustacheViewResolver resolver = new MustacheViewResolver();
resolver.setCharset("UTF-8");
resolver.setContentType("text/html; charset=UTF-8");
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
registry.viewResolver(resolver);
}
}
2. SecurityConfig 파일설정
// SecurityConfig.java
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 됨.
public class SecurityConfig {
// @Bean: 해당 메소드의 리턴되는 오브젝트를 IOC로 등록 해준다.
@Bean
public BCryptPasswordEncoder encodePwd(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
{
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") //login 주소가 호출되면 Security가 낚아채 로그인 진행해준다.
.defaultSuccessUrl("/");
return http.build();
}
}
3. 컨트롤러 주소 맵핑
// IndexController.java
@GetMapping("/joinForm")
public String joinForm(){
return "joinForm";
}
@PostMapping("/join")
public @ResponseBody String join(User user){
System.out.println(user);
user.setRole("ROLE_USER");
// 비밀번호 해쉬화
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);
return "redirect:/loginForm";
}
4. UserRepository(기본 CRUD) 생성
// UserRepository.java
// 기본적 CRUD
// @Repository 어노테이션 없어도 IOC됨. (JpaRepository 상속했기 때문)
public interface UserRepository extends JpaRepository<User,Integer> {
}
5. 회원가입 페이지(form action)
<!--joinForm.html-->
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>회원가입 페이지</title>
</head>
<body>
<h1>회원가입 페이지</h1>
<hr/>
<form action="/join" method="POST">
<input type="text" name="username" placeholder="Username"/> <br/>
<input type="password" name="password" placeholder="Password"/> <br/>
<input type="email" name="email" placeholder="Email"/><br/>
<button>회원가입</button>
</form>
<hr/>
</body>
</html>
● 로그인
6. 로그인 컨트롤러
// IndexController.java
@GetMapping("/loginForm")
public String loginForm(){
return "loginForm";
}
7. 로그인페이지 구성 (form action="/login" method="POST")
<!--loginForm.html-->
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr/>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username"/> <br/>
<input type="password" name="password" placeholder="Password"/> <br/>
<button>로그인</button>
</form>
<a href="/joinForm">회원가입을 아직 하지 않으셨나요?</a>
<hr/>
</body>
</html>
8. PrincipalDetails (UserDetails) Implement
- 시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
- 로그인 진행완료시 Security Session을 만들어준다. (Security ContextHolder)
- 오브젝트 => Authentication 타입 객체
- Authentication 안에 User 정보가 있어야 됨.
- User 오브젝트 타입 => UserDetails 타입 객체
- Security Session영역 => Authentication 객체 => UserDetails 타입객체 (implements UserDetails)
// PrincipalDetails.java
public class PrincipalDetails implements UserDetails {
private User user;
// 생성자
public PrincipalDetails(User user) {
this.user = user;
}
// 해당 User의 권한을 리턴하는곳
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return user.getRole();
}
});
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
// true설정
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
// 1년동안 회원로그인X시 휴먼계정
// 현재시간 - 로긴시간 => 1년초과시 return false;
return true;
}
}
9. PrincipalDetailsService (UserDetailsService) Override
- 시큐리티 설정에서 loginProcessingUrl("/login");
- login 요청이 오면 자동으로 UserDetailsService 타입으로 IOC 되어있는 loadUserByUsername 함수가 실행된다. (규칙)
- ※ 주의 : loginForm의 name명 같게 해야함(name="username"!) -다르다면 SecurityConfig에서 usernameParameter("username2") 설정해주어야 한다.
// PrincipalDetailsService.java
@Service // 서비스 띄우기
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// Security Session(내부 Authentication(내부 UserDetails))
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userEntity = userRepository.findByUsername(username);
if(userEntity!=null){
// 리턴될때 authentication 만들어지면서 자동으로 session안에 넣어줌
return new PrincipalDetails(userEntity);
}
return null;
}
}
10. JPA Query Method 작성
// UserRepository.java
// 기본적 CRUD
// @Repository 어노테이션 없어도 IOC됨. (JpaRepository 상속했기 때문)
public interface UserRepository extends JpaRepository<User,Integer> {
// ※ JPA Query Method
// findBy 규칙 , Username 문법
// select * from user where username = ?
User findByUsername(String username);
// select * from user where email = ?
//public User findByEmail(String email);
}