[스프링부트 시큐리티] 2. SpringBoot Security : Session

문정준's avatar
Jul 25, 2025
[스프링부트 시큐리티] 2. SpringBoot Security : Session

Session을 활용한 Spring Security 인증

  • Authentication 내부에 유저 정보 및 권한 정보를 담음 (복제되지 않음 : Singleton)
  • 인증이 성공할 경우 Authentication에 접근할 수 있는 키를 사용자에게 제공
 

Codes

  • SecurityConfig.java
    • sameOrigin 옵션 : 권한 설정으로 인해 h2-console 접근 거부 해결
    • CSRF Filter는 해당 사용자의 주소가 허용되어야 하기 때문에 예제에서는 OFF
    • 권한이 없는 유저가 다른 주소로 접근하려고 하면 자동으로 로그인 페이지로 이동 (세션 체크)
    • 로그인 성공 시 main으로 redirect
    • main은 인증이 필요
      • /user/** 는 USER 권한이 있어야만 접속 가능
      • /admin/** 는 ADMIN 권한이 있어야만 접속 가능
@Configuration public class SecurityConfig { @Bean public BCryptPasswordEncoder encodePwd() { return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())); http.csrf(configure -> configure.disable()); http.formLogin(form -> form .loginPage("/login-form") .loginProcessingUrl("/login") // username=ssar&password=1234 .defaultSuccessUrl("/main")); http.authorizeHttpRequests( authorize -> authorize .requestMatchers("/main").authenticated() .requestMatchers("/user/**").hasRole("USER") .requestMatchers("/admin/**").hasRole("ADMIN") .anyRequest().permitAll() ); return http.build(); } }
 
  • User.java
    • UserDetails 인터페이스를 상속
      • User는 이제 User이자 UserDetails → Spring Security에서 사용하는 UserDetails 정보를 대신할 수 있음
      • id, username, password, email 외에 roles 추가
        • 유저의 권한 지정
        • 해당 예제에서는 유저에게 여러 권한을 주는 방식 채택
          • (USER, ADMIN) 또는 (USER)
    • getAuthorities : 로그인 시 Session에 User를 저장할 때 권한도 같이 불러와서 저장
@NoArgsConstructor @Getter @Entity @Table(name = "user_tb") public class User implements UserDetails { @GeneratedValue(strategy = GenerationType.IDENTITY) @Id private Integer id; private String username; private String password; private String email; private String roles; @Builder public User(Integer id, String username, String password, String email, String roles) { this.id = id; this.username = username; this.password = password; this.email = email; this.roles = roles; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); String[] roleList = roles.split(","); for (String role : roleList) { authorities.add(() -> "ROLE_" + role); } return authorities; } }
 
  • UserController.java
    • /user : USER 권한 확인 용 health checker
    • /main
      • @AuthenticationPrincipal 어노테이션이 붙어있을 경우 해당 유저의 정보를 인증 정보로 사용
      • Authentication에 저장
@Controller public class UserController { private UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/user") public @ResponseBody String user() { return "<h1>user page</h1>"; } @GetMapping("/main") public String main(@AuthenticationPrincipal User user) { System.out.println(user.getUsername()); return "main"; } @GetMapping("/join-form") public String joinForm() { return "user/join-form"; } @GetMapping("/login-form") public String loginForm() { return "user/login-form"; } @PostMapping("/join") public String join(String username, String password, String email) { userService.회원가입(username, password, email); return "redirect:/main"; } }
 
  • AdminController.java
    • /admin : ADMIN 권한 확인 용 health checker
@Controller public class AdminController { @GetMapping("/admin") public @ResponseBody String adminMain() { return "<h1>admin page</h1>"; } }
 
  • UserService.java
    • UserDetailsService 인터페이스를 상속 : UserDetailsService 메서드도 함께 사용 가능
    • 회원가입 : 비밀번호가 입력되면 BCrypt에 의해 비밀번호가 해시로 암호화되어 저장
    • loadUserByUsername : 로그인 시 해당 username을 사용한 User 객체 검색 메서드
      • 메서드 재정의
@Service public class UserService implements UserDetailsService { private UserRepository userRepository; private BCryptPasswordEncoder bCryptPasswordEncoder; public UserService(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userRepository = userRepository; this.bCryptPasswordEncoder = bCryptPasswordEncoder; } @Transactional public void 회원가입(String username, String password, String email) { String encPassword = bCryptPasswordEncoder.encode(password); String roles = "USER"; userRepository.save(username, encPassword, email, roles); } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } }
 
  • UserRepository.java
    • em → userRepository → userService → userController 순의 IoC 구조
    • 저장, 검색 등 DB와 직접 통신하는 메서드만 작성
@Repository public class UserRepository { private EntityManager em; public UserRepository(EntityManager em) { this.em = em; } public void save(String username, String password, String email, String roles) { em.createNativeQuery("insert into user_tb (username, password, email, roles) values (?, ?, ?, ?)") .setParameter(1, username) .setParameter(2, password) .setParameter(3, email) .setParameter(4, roles) .executeUpdate(); } public User findByUsername(String username) { try { Query query = em.createNativeQuery("select * from user_tb where username = ?", User.class); query.setParameter(1, username); return (User) query.getSingleResult(); } catch (Exception e) { // 못찾으면 예외가 발생 return null; } } }
 
  • data.sql
    • 비밀번호를 암호화하여 저장했기 때문에 DB 내에도 암호화해서 저장해둔 더미가 필요
      • 비밀번호 검증은 로그인 시 자동으로 AuthenticationProvider가 수행
    • ssar는 USER, cos는 USER, ADMIN 권한을 다 가지고 있음
insert into user_tb(username, password, email, roles) values('ssar', '$2a$10$sZvrnV2FZO51MGrG2jTUU.zv3/K/vZFBW5MOYWPTkVeDeoZhH3rai', 'ssar@nate.com', 'USER'); insert into user_tb(username, password, email, roles) values('cos', '$2a$10$sZvrnV2FZO51MGrG2jTUU.zv3/K/vZFBW5MOYWPTkVeDeoZhH3rai', 'ssar@nate.com', 'ADMIN,USER');
 

Results

  • ssar 로그인 시
    • /main 또는 기타 URL로 접근 시도 시, 비 로그인 상태면 로그인 페이지로 자동 이동
notion image
notion image
  • 로그인 성공 시 Console에 로그인한 유저 확인 가능
notion image
  • ssar은 USER 권한만 있으므로 /user는 접속되고 /admin은 권한 없음으로 접속 불가 (403)
notion image
notion image
 
  • cos 로그인 시
    • cos는 USER, ADMIN 권한이 다 존재하므로 둘 다 접속 가능
      • notion image
notion image
notion image
Share article

sxias