1. 환경 설정
기본 의존 관계 설정 - pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
기본 환경 설정 - application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/springboot
spring.datasource.username=postgre
spring.datasource.password=1234
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.thymeleaf.cache=false
spring.devtools.livereload.enabled=true
spring.devtools.restart.enabled=true
SecurityConfig 설정
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}
2. 메뉴 권한 및 WebIgnore 설정
WebIgnore 설정 : js / css / image 파일 등 보안 필터를 적용할 필요가 없는 리소스 설정
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations());
}
3. Form 인증 - User 등록 / PasswordEncoder
- Spring Security 5.0 이전에는 기본 PasswordEncoder가 평문을 지원하는 NoOpPasswordEncoder
- 암호화 포맷 : {id}encodedPassword
: bcrypt, noop, pbkdf2, scrypt, sha256 ( 기본 포맷은 Bcrypt )
- 생성 : PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
- 인터페이스 :
encode(password) 패스워드 암호화
matches(rawPassword, encodedPassword) 패스워드 비교
4. Form 인증 - CustomUserDetailsService
public UserDetails loadByUsername(String username) throws UsernameNotFoundException {
Account account = userRepository.findByUsername(username);
if (account == null){
throw new UsernameNotFoundException("No user found with username: "+ username);
}
// 권한
ArrayList<Granted Authority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority(account.getRole()));
return new AccountContext(account, roles);
}
5. Form 인증 - CustomAuthenticationProvider
public Authentication authenticate(Authentication auth) throws AuthenticationCredentialsNotFoundException {
String loginId = auth.getName();
String passwd = (String)auth.getCredentials();
UserDetails userDetails = userDetailsService.loadUserByUSername(loginId);
if(userDetails == null || !passwordEncoder.matches(passwd, userDetails.getPassword())){
throw new BadCredentialsException("Invalid password");
}
return new UsernamePasswordAuthenticationToken(userDetails.getUser(), null, userDetails.getAuthorities());
}
6. Form 인증 - Custom Login Form Page
Spring Security 가 제공하는 기본 /login 페이지가 아닌 다른 로그인 페이지를 사용하기 위한 설정
@Override
public void configure(HttpSecurity http) throws Exception{
http.formLogin().loginPage("/customLogin");
}
7. Form 인증 - 로그아웃 및 화면 보안 처리
- 로그아웃 방법
<form> 태그를 사용해서 POST로 요청
<a> 태그를 사용해서 GET으로 요청 - SecurityContextLogoutHandler 활용
- 인증 여부에 따라 로그인 / 로그아웃 표현
<li sec:authorize="isAnonymous()"> <a th:href="@{/login}"> 로그인 </a> </li>
<li sec:authorize="isAuthenticated()"> <a th:href="@{/logout}"> 로그아웃 </a> </li>
@GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null){
new SecurityContextLogoutHandler().logout(request,response,auth);
}
return "redirect:/login";
}
8. Form 인증 - WebAuthenticationDetails, AuthenticationDetailsSource
- WebAuthenticationDetails : 인증 과정 중 전달된 데이터를 저장, Authentication 의 details 속성에 저장
- AuthenticationDetailsSource : WebAuthenticationDetails 객체를 생성
9. Form 인증 - CustomAuthenticationSuccessHandler, CustomAuthenticationFailureHandler
< SecurityConfig >
@Override
public void configure(HttpSecurity http) throws Exception{
http
.formLogin()
.successHandler(CustomAuthenticationSuccessHandler())
.failureHandler(CustomAuthenticationFailureHandler())
}
< CustomAuthenticationSuccessHandler >
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
RequestCache requestCache = new HttpSessionRequestCache(); // 요청 캐시와 관련된 작업
final HttpSession session = request.getSession(false); // 세션 관련 작업
Object principal = authentication.getPrincipal(); // 인증된 사용자 관련 작업
redirectStrategy.sendRedirect(request, response, targetUrl); // 인증 성공 후 이동
}
< CustomAuthenticationFailureHandler >
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
if(exception instanceOf UsernameNotFoundException){
errorMessage = messages.getMessage("사용자가 존재하지 않습니다.", null, locale);
}
else if (exception instanceof BadCredentialsException) {
errorMessage = messages.getMessage("아이디 혹은 비밀번호가 일치하지 않습니다.", null, locale);
}
else{
errorMessage = "인증에 실패하였습니다. 웹 마스터에게 문의하십시오!";
}
}
10. Form 인증 - Access Denied
< SecurityConfig >
@Override
public void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedPage("/accessDenied")
.accessDeniedHandler(accessDeniedHander)
}
}
< AccessDeniedHandler >
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
String deniedUrl = errorPage + "?exception=" + accessDeniedException.getMessage();
response.sendRedirect(request, response, deniedUrl);
}
public void setErrorPage(String errorPage) {
this.errorPage = errorPage;
}
11. Form 인증 - 인증 사용자 정보 구하기
1. 어플리케이션 전역
Account account = (Account) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = account.getUsername();
2. Spring MVC
- Authentication, Principal
public String getUsername(Authentication authentication, Principal principal) {
Account account = (Account) authentication.getPrincipal();
Account account = (Account) ((UsernamePasswordAuthenticationToken) principal).getPrincipal();
String username = account.getUsername();
}
- @AuthenticationPrincipal
: 인증 성공 이후 생성된 Authentication 객체의 principal 속성에 저장되어 있는 객체
public ModelAndView getUsername(@AuthenticationPrincipal Account account) {
String username = account.getUsername();
}
'SpringSecurity' 카테고리의 다른 글
스프링 시큐리티 복습 5 - 인가 프로세스 DB 연동 웹 계층 구현 (0) | 2023.08.23 |
---|---|
스프링 시큐리티 복습 4 - 인증 프로세스 Ajax 인증 구현 (0) | 2023.08.23 |
스프링 시큐리티 복습 2 - 주요 아키텍처 이해 (0) | 2023.08.18 |
스프링 시큐리티 복습 1 - 시큐리티 기본 API 및 Filter 이해 (0) | 2023.08.18 |
[5강] 시큐리티 권한처리 (0) | 2023.07.27 |
댓글