일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 정보처리기사필기요약
- 프로그래밍
- StringBuilder
- 리눅스
- 입출력
- 메서드
- JavaScript
- github
- SQL
- 백준
- Git
- MySQL
- BufferedReader
- 형변환
- 프로그래머스 SQL
- sql문
- select
- where
- 웹개발
- select문
- html
- 데이터 조회
- order by
- String클래스
- mybatis
- 클래스
- 프로그래머스 sql 고득점 kit
- 알고리즘
- DML
- 자바
- Java
- scanner
- 백엔드
- 예외처리
- 프론트엔드
- 정보처리기사
- 자바스크립트
- 스프링
- 개발자
- Linux
- Today
- Total
ToBe끝판왕
[ 스프링 시큐리티 ] Spring Security 개념 및 설정 본문
스프링 시큐리티
• 스프링 시큐리티( Spring Sercurity ) 란 ?
• 스프링 프레임워크 기반 애플리케이션의 보안을 담당하는 하위 프레임워크
- 복잡한 로직 없이도 어노테이션으로 설정 가능
- 기본적으로 세션 기반 인증 제공
- 기본적으로 필터 기반으로 동작
※ 필터란 ?
- 웹 어플리케이션에 도달하기 전 또는 후에 요청(Request) 과 응답(Response)를 가로채서 특정 작업을 수행 가능토록 한다.
- 요청 흐름 : 클라이언트 → 필터 체인 → 서블릿 → 응답 흐름: 서블릿 → 필터 체인 → 클라이언트
- 인증과 권한부여 처리 가능
- 로깅 및 감사 가능
- 보안 관련 작업 ( 헤더 추가 및 요청 차단 가능 )
• 인증 ( Authentication )
- 사용자의 신원 확인
- 허가된 사용자만 시스템 접근 가능
- 사용자의 이름(userName) 과 비밀번호(password) 기반으로 인증
• 권한
- 인증된 사용자에게 특정 기능 허용
- 역할(Role) 과 권한(Authority) 기반으로 접근 제어
• 스프링 시큐리티 인증처리 과정
- 사용자가 폼에 아이디 , 패스워드 입력
- HttpServletRequest 에 아이디 , 패스워드 정보가 전달
- AuthenticationFilter 가 넘어온 아이디 , 패스워드 정보의 유효성 검사를 실시
- 유효성 검사 이후, 실제 구현체인 UsernamePasswordAuthenticationToken을 만들어 넘겨준다.
- 인증용 객체인 UsernamePasswordAuthenticationToken을 AuthenticationManager 에게 전달
- UsernamePasswordAuthenticationToken 을 AuthenticationProvider 에게 전달
- 사용자 아이디를 UserDetailService 로 보내고, UserDetailService 는 사용자 아이디로 찾은 정보를
UserDetails 객체로 만들어 AuthenticationProvider 에게 전달
- DB에 저장되어 있는 사용자 정보를 가져와 입력정보와 UserDetails 의 정보를 비교해 인증처리 진행
- 인증이 완료되면 SercurityContextHolder 에 Authentication 을 저장
인증이 성공하면 AuthenticationSuccessHandler, 실패하면 AuthenticationFailureHandler 실행
• 스프링 시큐리티 적용
- 먼저 스프링 시큐리티( Spring Sercurity) 를 사용하기 위해서 의존성을 주입해야 한다.
- build.gradle 안 dependencies 블록에 의존성 추가
* build.gradle 에 아래내용 추가
// 스프링 시큐리티 코어 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
• 스프링 시큐리티 설정( Config ) 클래스 파일
- Spring Sercurity 설정 클래스 파일 작성
1) AuthenticationManager 빈 등록
- AuthenticationManager 는 스프링 시큐리티에서 인증을 담당하는 핵심 인터페이스
- 사용자의 인증정보를 검증하고 Authentication 객체 생성
- 스프링 시큐리티 5.7 이상 버전부터는 AuthenticationManager 를 직접 생성하고 빈으로 등록해야 한다.
// AuthenticationManager 빈 등록
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
2) 사용자 정보 조회 UserDetailService 의존성 주입
- loadUserByUsername(String username) 메서드를 구현하여 특정 사용자 이름(username) 에 해당하는 사용자 정보
(UserDetails)를 반환
// 사용자 정보 조회 Service 클래스 주입
private UserDetailService userDetailService;
3) 비밀번호 암호화 PasswordEncoder 의존성 주입
- 스프링 시큐리티에서는 비밀번호를 항상 암호화된 상태로 데이터베이스에 저장하고 비교한다.
- BCryptPasswordEncoder 는 스프링 시큐리티에서 가장 널리 사용되는 암호화 클래스이다.
( BCrypt 알고리즘을 사용하여 비밀번호를 암호화하고 검증 )
// 비밀번호 암호화
private PasswordEncoder passwordEncoder;
- 비밀번호 암호화 설정 클래스 파일
package com.wsd.invest.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfig {
// 비밀번호 암호화
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4) Spring SecurityFilterChain 정의
- SecurityFilterChain 은 HTTP 요청을 처리하는 필터들이 연결된 집합체
- CSRF( Cross Site Request Forgery ) 설정
- 권한에 따른 HTTP URL 설정
// HTTP 요청에 따른 보안정책 정의 (스프링 시큐리티 버전 5.7 이상 버전 방식 )
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// CSRF 비활성화 (필요시 활성화 가능)
.csrf(csrf -> csrf.disable())
// 권한에 따른 HTTP url 설정
.authorizeHttpRequests(auth -> auth
// 메인 / 로그인 / 회원가입 / .jsp 파일 경로 접근 허용
.requestMatchers("/user/main", "/user/login", "/user/join", "/WEB-INF/views/**").permitAll()
// 정적 리소스 허용
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
// forward / include 타입 요청 허용
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.dispatcherTypeMatchers(DispatcherType.INCLUDE).permitAll()
// 나머지 요청 인증 필요
.anyRequest().authenticated()
)
// 로그인 설정
.formLogin(form -> form
// Get 요청 로그인 페이지
.loginPage("/user/login")
// Post 요청 로그인 처리 Url
.loginProcessingUrl("/user/login")
// 로그인 성공 url
.defaultSuccessUrl("/user/main", true)
// 로그인 실패 url
.failureUrl("/user/login?error=true")
.permitAll()
)
// 로그아웃 설정
.logout(logout -> logout
// POST 요청 로그아웃 처리 Url
.logoutUrl("/user/logout")
// 로그아웃 성공 url
.logoutSuccessUrl("/user/login")
// 로그아웃 시 세션쿠키 삭제
.deleteCookies("JSESSIONID")
.permitAll()
);
return http.build();
}
※ View 렌더링 문제
- JSP 를 렌더링 하는데 스프링 시큐리티 권한 및 URL 설정에서 오류가 뜨는 문제 발생
- 설명 및 해결방법 아래 링크 참고
https://baby9235.tistory.com/128
5) 스프링 시큐리티 설정 클래스 파일 최종본
package com.wsd.invest.security;
import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.WebSecurityCustomizer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// 회원 정보 조회 Service 클래스 주입
private UserDetailService userDetailService;
private PasswordEncoder passwordEncoder;
@Autowired
public SecurityConfig(UserDetailService userDetailService, PasswordEncoder passwordEncoder) {
this.userDetailService = userDetailService;
this.passwordEncoder = passwordEncoder;
}
// AuthenticationManager 빈 등록
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
// "//" 허용
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.httpFirewall(defaultHttpFirewall());
}
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}
// HTTP 요청에 따른 보안정책 정의 (스프링 시큐리티 버전 5.7 이상 버전 방식 )
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// CSRF 비활성화 (필요시 활성화 가능)
.csrf(csrf -> csrf.disable())
// 권한에 따른 HTTP url 설정
.authorizeHttpRequests(auth -> auth
// 메인 / 로그인 / 회원가입 / .jsp 파일 경로 접근 허용
.requestMatchers("/user/main", "/user/login", "/user/join", "/WEB-INF/views/**").permitAll()
// 정적 리소스 허용
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
// forward / include 타입 요청 허용
.dispatcherTypeMatchers(DispatcherType.FORWARD).permitAll()
.dispatcherTypeMatchers(DispatcherType.INCLUDE).permitAll()
// 나머지 요청 인증 필요
.anyRequest().authenticated()
)
// 로그인 설정
.formLogin(form -> form
// Get 요청 로그인 페이지
.loginPage("/user/login")
// Post 요청 로그인 처리 Url
.loginProcessingUrl("/user/login")
// 로그인 성공 url
.defaultSuccessUrl("/user/main", true)
// 로그인 실패 url
.failureUrl("/user/login?error=true")
.permitAll()
)
// 로그아웃 설정
.logout(logout -> logout
// POST 요청 로그아웃 처리 Url
.logoutUrl("/user/logout")
// 로그아웃 성공 url
.logoutSuccessUrl("/user/login")
// 로그아웃 시 세션쿠키 삭제
.deleteCookies("JSESSIONID")
.permitAll()
);
return http.build();
}
}
• UserDetailService 클래스 파일
- 데이터베이스 등에서 사용자 정보를 조회하여 UserDetails 객체를 생성
- UserDetailsService 인터페이스를 상속받아 작성
- loadUserByUsername 메서드 사용 : 사용자 인증을 위한 핵심적인 메서드
주어진 사용자 이름(username)을 바탕으로 데이터베이스에서 사용자 정보를 조회하여 UserDetails 객체 반환
public UserDetails loadUserByUsername(String memberId) throws UsernameNotFoundException {
// 사용자 정보 조회 ( 타입 = UserDetails 인터페이스를 구현한 클래스 )
CustomUserDetails user = userDetailMapper.findByUsername(memberId);
// 사용자 정보 조회 X -> 예외
if( user == null ) {
throw new UsernameNotFoundException("User Not Found with memberId: " + memberId);
}
// 사용자의 권한을 GrantedAuthority로 변환
List<SimpleGrantedAuthority> authorities = Collections.singletonList(
new SimpleGrantedAuthority(user.getRole())
);
// User 객체 생성
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
.build();
}
• CustomusterDetails클래스 파일
- UserDetails 객체의 구체적인 형태를 정의한다.
- UserDetails 를 상속받아 작성
- 메서드들을 통해서 사용자의 정보를 제공
- getUsername() : 사용자의 고유ID를 반환 ( 일반적으로 회원ID, 이메일 주소, 사용자의 이름 )
- getPassword() : 사용자의 암호화된 비밀번호 반환
- getAuthorities() : 사용자가 가지고 있는 권한 반환 ( ROLE_USER, ROLE_ADMIN 등 )
- isEnabled() : 사용자 계정이 활성화 되어있는지 반환
package com.wsd.invest.security;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Data
public class CustomUserDetails implements UserDetails {
// DB 에서 PK 값
private String memberId;
@Pattern(regexp = "^[가-힣]{2,}$", message = "회원 이름은 2글자 이상의 한글로 입력하세요.")
@NotBlank(message = "회원 이름은 필수값입니다.")
private String memberNm;
// 비밀번호
@NotBlank(message = "비밀번호는 필수값입니다.")
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-])(?=.*[0-9]).{8,15}$", message = "비밀번호는 8~15자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
private String memberPw;
// 역할기반 권한
private String role;
@Pattern(regexp = "^\\d{2}$", message = "회원 나이는 2글자의 숫자로 입력하세요.")
@NotBlank(message = "회원 나이는 필수값입니다.")
private String memberAge;
@NotBlank(message = "올바른 이메일 형식이 아닙니다.")
private String memberMail;
private Date regDt;
private String regId;
private Date modDt;
private String modId;
public CustomUserDetails() {
this.memberId = memberId;
this.memberNm = memberNm;
this.memberPw = memberPw;
this.role = role;
this.memberAge = memberAge;
this.memberMail = memberMail;
this.regDt = regDt;
this.regId = regId;
this.modDt = modDt;
this.modId = modId;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority(role));
}
@Override
public String getPassword() {
return memberPw;
}
// 스프링 시큐리티에서 username이 memberId를 반환하도록 설정
@Override
public String getUsername() {
return memberId;
}
}
▶ Spring Security Config 클래스 파일, CustomUserDetails 클래스 파일, UserDetailService 클래스
파일을 세팅하고 작성함으로써 기본적인 설정을 마무리 하였다.
'■ 프로그래밍 SKILLS > SPRING FRAMEWORK' 카테고리의 다른 글
[ SPRING ] 어노테이션 @PathVariable 과 @RequestParam (0) | 2024.05.22 |
---|---|
[ SPRING ] 스프링 개념 , 특징 , 환경설정( 이클립스 설치 ) (0) | 2022.06.06 |