SpringBoot/Security

[SpringBoot] Security 회원가입과 로그인

유쾌한고등어 2023. 1. 20. 13:43

[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>

● 로그인

 

<Security authentication 흐름 필기>

 

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);
}