[2023.1.20.금] spring security
spring-web 프로젝트
* spring framework 로 생성한 프로젝트
* spring bean configuration 파일 정의
* web.xml에 spring 관련 설정 추가
* spring-context, spring-webmvc, spring-jdbc, mybatis, mybatis-spring, logging, fileupload, jackson-databinder 등의 라이브러리 의존성 추가
springboot-rest 프로젝트
* spring boot 로 생성한 프로젝트
* application yml 설정파일 사용
* spring-boot-starter-web, mybatis-spring-boot-starter, spring-boot-devtools, ojdbc8, lombok, spring-boot-starter-test 라이브러리 의존성 추가
* REST API를 구현할 웹 애플리케이션 프로젝트
- 사용자정보에 대한 추가, 조회, 변경, 삭제 처리를 제공하는 REST API 기반 웹 애플리케이션 프로젝트
- 클라이언트와 서버가 json 형식의 데이터를 주고받는다.
- 별도의 Front-end 애플리케이션 프로젝트를 생성해서 rest api 애플리케이션과 상호작용함
springboot-app 프로젝트
* spring boot 로 생성한 프로젝트
* application yml 설정파일 사용
* spring-boot-starter-web, mybatis-spring-boot-starter, spring-boot-devtools, ojdbc8, lombok, spring-boot-starter-test 라이브러리 의존성 추가
* spring-boot-start-security, spring-security-taglib, spring-security-test 라이브러리 의존성 추가
* tomcat-embed-jasper, jstl 라이브러리 의존성 추가
* 특징
- 회원가입 시 사용자 비밀번호가 암호화되어 저장된다.
- 로그인 페이지만 제공하고, 로그인 처리는 spring-security가 수행한다.
- 인증되지 않은 사용자는 홈, 회원가입, 로그인만 접근이 허용된다.
- 인증된 사용자는 게시판에 접근할 수 있다.
- ROLE_GUEST 권한을 가진 사용자는 게시판 목록, 상세정보, 댓글 작성, 댓글 삭제 가능
- ROLE_USER 권한을 가진 사용자는 게시판 목록, 상세정보, 게시글 등록, 게시글 삭제, 댓글 작성, 댓글 삭제 가능
- ROLE_USER 권한을 가진 사용자는 사용자 정보를 이용할 수 있다.
- 사용자 상세정보, 비밀번호 변경, 탈퇴가 가능하다.
- ROLE_ADMIN 권한을 가진 사용자는 관리자 기능을 이용할 수 있다.
스프링 시큐리티 (Spring Security)
- 스프링 시큐리티는 스프링 기반의 애플리케이션의 보안(인증과 권한관리, 인가 등)을 담당하는 프레임워크다.
- 인증(Authentication)과 인가(Authorization)를 담당하는 프레임워크다.
- 스프링 시큐리티는 서블릿 필터(Filter)와 이들로 구성된 필터체인으로 보안과 관련된 처리를 수행한다.
스프링 시큐리티의 주요 용어
- 접근 주체(Principal) : 보호된 리소스에 접근하는 대상(사용자)
- 인증(Authentication) : 보호된 리소스에 접근하는 대상이 누구인지, 애플리케이션의 작업을 수행해도 되는 주체인지 확인하는 과정 ( 우리 사용자인지 )
- 예시) Form 기반 로그인
- 인가(Authorization) : 해당 리소스에 대한 접근 가능한 권한을 가지고 있는지 확인하는 과정(인증과정 이후에 수행한다.)
- 권한 : 어떠한 리소스에 대한 접근 제한, 모든 리소스는 접근 제어 권한이 걸려있으며, 인가과정에서 해당 리소스에 대해 제한된 최소한의 권한을 가졌는지 확인한다.
스프링 시큐리티 인증 절차
스프링에서 제공하는 DelegatingFilterProxy가 중간에서 클라이언트의 요청을 가로챔
-> FilterChainProxy로 요청처리 위임
-> 필터를 순서에 맞게 실행하면서 인증, 인가작업 처리 (보안처리)
-> 보안처리가 모두 끝나면 요청을 spring MVC로 전달
로그인 -> username(아이디, 사원번호, 이메일 등등 다 다름), password 전달
-> Authentication 객체의 Principal에 username, Credentails에 password 저장
-> Authorities (접근권한), Authenticated (인증여부) 는 아직 안들어있는 상태
-> AuthenticationManager가 실질적인 인증 절차 처리
-> 인증이 완료되면 새로운 Authentication 객체 생성
-> 사용자정보, 권한, 인증여부도 들어있음
-> 그게 세션에 저장
- 사용자가 로그인 정보를 서버로 보내고 인증을 요청한다.
- AuthenticationFilter가 요청를 가로채고, UsernamePasswordAuthenticationTocken 구현 객체를 생성.
( Authentication 객체의 구현객체 (아이디, 비밀번호 폼 로그인 요청이면 이 구현객체 생성) . 사용자가 입력한 로그인 정보를 기반으로 생성되며, 현상태는 Authentication 객체에 아이디, 비밀번호 정보만 들어있는 상태 ) - UsernamePasswordAuthenticationToken객체를 AuthenticationManager -> AuthenticationProvider에 전달.
- 데이터베이스로부터 사용자 인증 정보를 가져오는 UserDetailsService에 사용자 정보를 전달한다.
( Provider로부터 전달받은 아이디로 사용자 정보를 조회 - loadUserByUsername(String username) 메소드 ) - 전달받은 정보를 통해 데이터베이스에서 조회한 사용자로 UserDetails객체를 생성한다.
-> UserDetailsService를 구현해서 CustromUserDetailsService 구현객체 생성 -> 얘가 사용자 정보 조회
-> 재정의한 loadUserByUsername(String username) 메소드의 반환값으로 비밀번호, 권한정보 등이 담겨있는 UserDetails 객체 생성 - AuthenticationProvider는 전달받은 UserDetails객체와 원래 있던 Authentication 객체의 사용자 정보를 비교한다.
- 두 비밀번호가 일치하면, 아이디만 들어있던 Authentication의 principal 부분에 권한정보가 들어있는 userDetails를 저장 -> 이 새로운 정보들을 가지고 인증된 사용자 정보를 담은 새로운 Authentication객체를 반환한다.
- Authentication객체를 AuthenticationManager로 반환 -> 최초의 AuthenticationFilter로 반환 -> SecurityContext에 저장
- 세션에 담긴 사용자 정보를 언제든지 꺼내쓸 수 있음
- 실질적인 인증과정은 사용자가 입력한 데이터(아이디/비밀번호)와 UserDetailsService의 UserDetails loadUserByUsername(String username) 메소드가 반환하는 UserDetails객체를 비교함으로 동작한다.
- UserDetailsService와 UserDetails 구현을 어떻게 하느냐에 따라서 인증의 세부과정이 달라진다.
* pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>springboot-app</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!--
spring-security 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--
jsp용 spring-security-tag 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<!--
spring-web 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--
mybatis 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<!--
spring-boot devtools 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--
oralce jdbc 드라이버 라이브러리 의존성 추가
-->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
<!--
lombok 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--
jsp를 지원하는 내장형 톰캣 라이브러리 의존성 추가
-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--
jstl 태그 라이브러리 의존성 추가
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
* UserDetails 인터페이스
// 권한정보 반환
Collection<? extends GrantedAuthority> getAuthorities();
// 비밀번호 반환
String getPassword();
// 사용자 이름 (사용자 아이디, 이메일, 사원번호, 학생번호 등 애플리케이션에 따라서 다름)
String getUsername();
// 계정 만료여부 반환
boolean isAccountNonExpired();
// 계정 잠금여부 반환
boolean isAccountNonLocked();
// 비밀번호 만료여부 반환
boolean isCredentialsNonExpired();
// 계정 사용가능여부 반환
boolean isEnabled();
* LoginUser 클래스
어노테이션을 붙이면 로그인된 사용자 정보가 이 객체에 저장, 그 정보를 컨트롤러에서 가져오기 위해 생성
* LoginUser 클래스를 상속받고, UserDetails 인터페이스를 구현한, CustomUserDetails 클래스
package com.example.security;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* UserDetails 인터페이스를 구현한 클래스다. <br />
* 사용자정보 (아이디, 비밀번호, 권한정보)를 제공하는 클래스다. <br />
* 조회된 사용자 정보, 권한 정보를 저장. 인증이 완료되면 Authentication 객체에 저장. <br />
* AuthenticationProvider가 사용자 인증을 할 때 이 객체의 정보를 이용한다. <br />
* @Authenticated LoginUser loginUser를 하면 결국 이 객체에 담긴 사용자 정보를 불러오는 것 (부모클래스로 담는것)
* AuthenticationProvider는 사용자 인증이 완료되면 Authentication 객체를 새로 생성해서 사용자정보, 권한정보, 인증완료여부를 저장한다.
* @author wnnns
*
*/
public class CustomUserDetails extends LoginUser implements UserDetails {
private static final long serialVersionUID = -1022370295119655350L;
private Collection<? extends GrantedAuthority> authorities;
// CustormDetealsService에서 조회된 사용자 정보, 권한 정보를 저장
public CustomUserDetails(String userId,
String password,
String userName,
Collection<? extends GrantedAuthority> authorities) {
// 부모 생성자(LoginUser객체)를 호출해서 LoginUser의 id, encryptPassword, name에 사용자아이디, 비밀번호, 이름이 저장되게 한다.)
super(userId, password, userName);
// 사용자의 보유 권한을 authorities에 저장한다.
this.authorities = authorities;
}
// 권한 정보 반환
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
// 비밀번호 반환
@Override
public String getPassword() {
return getEncryptPassword();
}
// 사용자 이름 (사용자 아이디, 이메일, 사원번호, 학생번호 등 애플리케이션에 따라서 다름)
@Override
public String getUsername() {
return getId();
}
// 계정 만료여부 반환
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정 잠금여부 반환
@Override
public boolean isAccountNonLocked() {
return true;
}
// 비밀번호 만료여부 반환
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정 사용가능여부 반환
@Override
public boolean isEnabled() {
return true;
}
}
* UserDetailsService 인터페이스를 구현한 CustomUserDetailsService 클래스
package com.example.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.mapper.UserMapper;
import com.example.mapper.UserRoleMapper;
import com.example.vo.User;
import com.example.vo.UserRole;
/**
* UserDetailsService 인터페이스를 구현한 클래스다. <br />
* UserDetailsService 인터페이스의 UserDetails loadUserByUsername(String userId) 메소드를 구현한 클래스다. <br />
* loadUserByUsername(String username) 메소드는 사용자명(아이디, 이메일, 사원번호, 학생번호 등)으로 데이터베이스에서
* 사용자 정보와 권한정보를 조회해서 UserDetails 객체에 담아서 반환하는 메소드다. <br />
* UserDetailsService 인터페이스의 구현클래스에 재정의 loadUserByUsername(String username) 메소드가 반환하는 UserDetails 구현객체가
* 실제 인증과정에서 사용된다. ( UserDeatils 구현객체가 db에서 불러온 사용자 정보를 담고있기 때문)
*
* CustomUserDetailsService 클래스에서는 사용자정보와 권한정보를 조회해서 CustomUserDetails 객체를 생성하고 담아서 반환했다. <br />
*
* @author wnnns
*
*/
@Service
public class CustomUserDetailsService implements UserDetailsService {
// 사용자정보와 사용자 권한 정보를 조회하기 위해서 UserMapper와 UserRoleMapper를 주입받는다.
@Autowired
private UserMapper userMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Override
public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
// 사용자아이디는 인증작업을 수행하는 AuthenticationProvider가 사용자정보 찾으라고 전달해준 것이다.
// 사용자아이디로 사용자 정보를 조회한다.
User user = userMapper.getUserById(userId);
// 사용자정보가 존재하지 않으면 예외를 던진다.
if (user == null) {
throw new UsernameNotFoundException("사용자 정보가 존재하지 않습니다.");
}
if ("Y".equals(user.getDeleted())) {
throw new UsernameNotFoundException("탈퇴한 사용자입니다.");
}
// 사용자의 권한정보를 조회한다.
List<UserRole> userRoles = userRoleMapper.getUserRolesByUserId(userId);
// 조회된 권한정보로 GrantedAutority 객체를 생성한다.
Collection<? extends GrantedAuthority> authorities = this.getAuthorities(userRoles);
return new CustomUserDetails( // 생성자
user.getId(), // 사용자 아이디
user.getEncryptPassword(), // 암호화된 사용자 비밀번호
user.getName(), // 사용자 이름
authorities); // 사용자가 보유한 권한정보
}
// 사용자 권한정보 목록을 전달받아서 GrantedAuthority 객체의 집합으로 반환한다.
private Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoles) {
// 권한정보를 보관하는 객체 GrantedAuthority 생성 (인터페이스)
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
// GrantedAuthority의 구현객체 SimpleGrantedAuthority
for (UserRole userRole : userRoles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(userRole.getRoleName());
authorities.add(authority);
}
return authorities;
}
}
UserDetailsService 인터페이스의 loadUserByUsername 메소드를 재정의
사용자 인증을 하려면 사용자 정보를 알아야 함 -> User 객체 획득 -> 아이디, 이름, 비밀번호가 들어있음
사용자 인가를 하려면 권한정보를 조회해야 함 -> 사용자 권한이 담겨 있는 User의 권한도 획득
이 정보들을 아무 객체에 담는게 아니고, CustomUserDetails customUserDetails = new CustomUserDetails 객체에 담음
스프링에서 제공하는 GrantedAuthority 인터페이스 생성 -> 이 객체의 구현체인 SimpleGrantedAuthority 획득
권한정보(ROLE_ADMIN, ROLE_GUEST, ...) 를 담은 authority 객체를 만들어서 인터페이스에 등록
loadUserByUserName 메소드를 실행하면 -> 아이디, 비밀번호, 이름, 권한정보가 담겨있는 customUserDetails 객체를 생성해서 반환
* 인증이 완료되면 Authentication 객체를 새로 생성하고, 사용자정보와 권한정보를 저장
: 원래 Authentication 의 principal에는 username인 "hong"이 담겨있음
-> 새로운 Authentication의 principal에는 인증, 인가 완료된 사용자정보인 CustomUserDetails 객체가 담겨있음
+ authorities 에는 사용자의 접근권한,
+ authenticated 에는 true가 담겨있음
-> 이 Authentication 객체를 AuthenticationManager 객체로 반환
-> 이 Authentication 객체를 SecurityContext 안의 AuthenticationContext 안에 저장
-> 여기 담긴 것을 언제든지 꺼내쓸 수 있음
=> 로그인창에서 아이디, 비밀번호 입력
-> Authentication 객체에 아이디, 비밀번호만 저장
-> UserDetailsService의 메소드를 통해 이 객체의 아이디로 db에서 객체 획득
-> CustomUserDetails 객체 반환
-> 이 사용자 정보의 아이디, 비밀번호, 이름을 알 수 있음
-> CustomUserDetails 객체에 담겨있는 사용자 정보의 비밀번호와 로그인할 때 입력한 Authentication 객체에 담겨있는 비밀번호가 서로 같은지 비교
-> 비밀번호가 같으면 인증이 완료된 것
-> 새로운 Authentication 객체 생성
-> 새로운 Authentication 객체에 사용자정보 객체, 접근권한, 인증여부까지 모두 담아서 반환
-> AuthenticationManager를 거쳐서 SecurityContext에 저장 -> 세션에 저장된 것
* 우리가 필요한 것은 Authentication의 principal에 담긴 사용자 정보
=> 그래서 AuthenticatedUser 객체 (어노테이션 인터페이스) 생성
@AuthenticationPrincipal : Authentication의 principal에 들어있는 것을 가져와라
@AuthenticatedUser LoginUser loginUser -> 로그인 인증된 유저
( * CustomUserDetails 객체는 LoginUser 타입이기도 하고(상속), UserDetails 타입이기도 함(구현) )
회원가입
* HomeController
* CustomSecurityConfig
홈, 회원가입, 로그인은 전부다 접근 가능
* SpringbootAppApplication ( 비밀번호 암호화 )
@Autowired
private PasswordEncoder passwordEncoder 로 주입받아서 사용
* UserService
DB 에 암호화된 비밀번호
로그인
* HomeController
-> 로그인 과정을 요청핸들러 메소드로 만들 필요 X -> 아래 과정이 바로 로그인
스프링 시큐리티가 기본적으로 로그인폼, 로그인 인증까지 지원함
=> 사용자의 권한에 따라 접근할 수 있는 페이지 제한
* CustomSecurityConfig
package com.example.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* spring security 설정정보를 제공하는 클래스다 <br />
* @author wnnns
*/
@EnableWebSecurity
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
// 인증에 필요한 사용자정보와 권한정보를 포함하는 UserDetails 객체를 반환하는 CustomUserDetailsService 객체를 의존성 주입받는다.
@Autowired
private CustomUserDetailsService customUserDetailsService;
// 회원가입시 비밀번호 암호화에 사용했던 비밀번호 인코드 객체를 의존성 주입받는다.
@Autowired
private PasswordEncoder passwordEncoder;
/*
* HTTP 요청에 대한 인증/인가 관련 설정정보를 정의한다. <br />
* - csrf 정책, 인가정책(권한체크 정책), 인증정책(로그인 정책), 로그아웃 정책, 예외처리 정책을 설정한다.
*
* HttpSecurity
* - 각종 보안관련 설정을 담당하는 객체
* - HttpSecurity는 각종 xxxConfigurer객체를 반환하는 메소드를 제공한다.
* csrf() 메소드는 csft 토큰 사용여부를 설정하는 CsrfConfigurer를 반환한다.
* authorizeHttpRequests() 메소드는 인가정책을 설정하는 AuthorizationManagerRequestMatcherRegistry 반환
* formLogin() 메소드는 폼로그인 정책을 설정하는 FormLoginConfigurer 반환
* logout() 메소드는 로그아웃 정책을 설정하는 LogoutConfigurer 반환
* exceptionHandling() 메소드는 예외처리 정책을 설정하는 ExceptionHandlingConfigurer 반환
* xxxConfigurer 객체의 and() 메소드는 HttpSecurity 객체를 반환
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// CSRF 토큰 사용여부 설정
//.csrf() // CSRF(사이트 간 요청위조) 방지를 위한 csrf 토큰 사용여부 설정, CsrfConfigurer 객체를 반환한다.
//.disable() // csrf 토큰 사용을 비활성화한다. (기본값은 csrf 토큰을 사용한다.)
// 1. 인가정책 설정 시작
.authorizeHttpRequests() // 모든 요청에 대해서 아래에 설정된 인가정책(권한체크)을 적용하도록 한다. AuthorizationManagerRequestMatcherRegistry 객체 반환
.antMatchers("/", "/register", "/registered", "/login").permitAll() // 모두 접근을 허용한다.
.antMatchers("/post/**").hasAnyRole("GUEST", "USER") // hasAnyRole은 ROLE_GUEST 혹은 ROLE_USER 권한 필요
.antMatchers("/user/**").hasRole("USER") // hasRole은 ROLE_USER 권한 필요
.antMatchers("/admin/**").hasRole("ADMIN") // hasRole은 ROLE_ADMIN 권한 필요
.anyRequest().authenticated() // 위에서 제시된 요청 외의 모든 요청도 반드시 인증 필요
// 1. 인가정책 설정 종료
.and() // HttpSecurity 객체 반환. 새 보안설정에 필요한 객체(xxxConfigurer)를 반환하는 메소드를 호츨해서 보안설정을 추가한다.
// 2. 인증정책 설정 시작
.formLogin() // 인증방식은 폼인증 방식을 사용하도록 지정. FormLoginConfigurer 객체 반환
.loginPage("/login") // 로그인 폼을 요청하는 URI 지정
.loginProcessingUrl("/login") // 로그인 처리를 요청하는 URI 지정
.usernameParameter("id") // 로그인 폼의 사용자이름 입력필드 이름 지정 (아이디, 이메일, 사원번호 등)
.passwordParameter("password") // 로그인 폼의 비밀번호 입력필드 이름 지정
.defaultSuccessUrl("/") // 로그인 성공 시 재요청할 URI 지정
.failureUrl("/login?error=fail") // 로그인 실패 시 재요청할 URI 지정
// 2. 인증정책 설정 종료
.and() // HttpSecurity 객체 반환
// 3. 로그아웃 정책 설정 시작
.logout() // 로그아웃. LogoutConfigurer 객체 반환
.logoutUrl("/logout") // 로그아웃 처리를 요청하는 URI 지정
.logoutSuccessUrl("/") // 로그아웃 성공 시 재요청할 URI 지정
// 3. 로그아웃 정책 설정 종료
.and()
// 4. 예외처리 정책 설정 시작
.exceptionHandling() // 예외처리. ExceptionHandlingConfigurer 객체 반환
.accessDeniedPage("/access-denied");// 접근이 거부됐을 때 요청할 URI 지정
// 4. 예외처리 정책 설정 종료
}
// 보안정책을 적용하지 않을 URI 설정 (이미지, 스타일시트, 자바스크립트 소스와 같은 정적 콘텐츠는 인증/인가 작업을 수행하지 않도록 설정)
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/favicon.ico");
}
// 사용자정의 UserDetailsService 객체(customUserDetailsService)와 비밀번호 암호화객체를 AuthenticationManagerBuilder에 등록
// -> 기본제공하는거 말고 얘네를 써라
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder);
}
}