[Security] 폼 로그인 구현

  • 목표 : 폼 로그인을 통해 인증을 완료하고 권한설정을 통해 특정 페이지에 엑세시 할 수 있도록 합니다.
    • admin은 admin 페이지만 superadmin은 admin, superadmin페이지에 접근할 수 있습니다.
  • 생성파일
    • config
      • SecurityConfig.java
    • Controller
      • AdminController.java
      • LoginController.java
    • Model
      • Role.java
      • AdminUser.java
    • Service
      • CustomUserDetailsService
    • Repository
      • AdminUserRepository

 

Role.java 생성

@AllArgsConstructor
@Getter
public enum Role {
    SUPERADMIN("ROLE_SUPERADMIN,ROLE_ADMIN"),
    ADMIN("ROLE_ADMIN");

    private String value;
}

 

AdminUser.java 생성

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class AdminUser implements UserDetails {


    @Id
    private Long id;
    @Setter
    private String adminId;
    @Setter
    private String password;
    @Setter
    private String role;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();

        for(String s: role.split(",")){
            authorities.add(new SimpleGrantedAuthority(s));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return adminId;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

 

AdminUserRepository.java 생성

public interface AdminUserRepository extends JpaRepository<AdminUser, Long> {
    Optional<AdminUser> findByAdminId(String adminId);
}

 

CustomUserDetailsService.java 생성

@Slf4j
@RequiredArgsConstructor
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final AdminRepository adminRepository;
    private final BCryptPasswordEncoder passwordEncoder;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        
        Optional<AdminUser> adminUser = adminRepository.findByAdminId(username);

        if(adminUser.isPresent()){
            AdminUser admin = adminUser.get();

            AdminUser authUser = AdminUser.builder()
                    .id(admin.getId())
                    .adminId(admin.getAdminId())
                    .password(passwordEncoder.encode(admin.getPassword()))
                    .role(admin.getRole())
                    .build();

            return authUser;
        }else{
            return AdminUser.builder()
                    .id(1l)
                    .adminId("qwe")
                    .password(passwordEncoder.encode("qwe"))
                    .role(Role.ADMIN.getValue())
                    .build();
        }
    }
}

 

LoginController.java 생성

@Controller
public class LoginController {

    @GetMapping("/")
    public String loginForm(){
        return "loginForm";
    }
    
}

AdminController.java 생성

@RequiredArgsConstructor
@Controller
public class AdminController {
    
    @GetMapping("/admin")
    public String admin(){
        return "admin";
    }
    @GetMapping("/superadmin")
    public String superadmin(){
        return "superadmin";
    }

    @GetMapping("/accessDenied")
    public String accessDenied(){
        return "accessDenied";
    }

}

 

SecurityConfig.java 생성

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        http.csrf().disable()
                .headers().frameOptions().sameOrigin()
                .and()
                .authorizeRequests()
                .antMatchers("/","/h2-console","/register").permitAll()
                .antMatchers("/superadmin", "/admin").hasRole("SUPERADMIN")
                .antMatchers("/admin").hasRole("ADMIN")
                .and()
                .formLogin()
                .loginPage("/")
                .defaultSuccessUrl("/admin")
                .usernameParameter("adminId")
                .passwordParameter("password")
                .and()
                .logout()
                .logoutSuccessUrl("/")
                .invalidateHttpSession(true).deleteCookies("JSESSIONID") //세션 날리기
                .and()
                .exceptionHandling()
                .accessDeniedPage("/accessDenied");

        return http.build();
    }
}

 

loginForm.html 생성

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="/" method="post">
    <input type="text" name="adminId" placeholder="myadmin">
    <input type="password" name="password" placeholder="password">
    <button type="submit">Login</button>
</form>

</body>
</html>

admin.html 생성

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> Admin Page </h1>
<form action="/logout">
  <button type="submit">logout</button>
</form>
</body>
</html>

superadmin.html 생성

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> Super Page </h1>
<form action="/logout">
  <button type="submit">logout</button>
</form>

</body>
</html>

accessdenied.html 생성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> AccessDenied Page </h1>
<form action="/logout">
  <button type="submit">logout</button>
</form>

</body>
</html>

'Spring Boot' 카테고리의 다른 글

Spring Security 기본 사용자 비활성화  (0) 2024.04.06
[Security] Security Exception  (0) 2023.10.17
[Security] 스프링 시큐리티 아키텍처  (0) 2023.10.12