728x90
Spring Boot에 Security 적용하기
1. pom.xml 추가
<!--Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
2. SecurityConfig.java 설정
- 시큐리티가 /login(POST) 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
- 로그인 진행이 완료되면 시큐리티 session을 만들어 준다(Security ContextHolder)
- 오브젝트 타입 -> Authentication 타입 객체
- Authentication 안에 User 정보가 있어야 함
- Security Session(Authentication(UserDetails = PrincipalDetails))
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import common.wpin.EncryptType;
import security.CustomAuthenticationFailureHandler;
import security.CustomAuthenticationSuccessHandler;
@Configuration
@EnableWebSecurity //스프링 시큐리티 필터가 스프링 필터체인에 등록된다.
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //Controller단에서도 권한설정을 할 수 있게 만듦
public class SecurityConfig extends WebSecurityConfigurerAdapter{
CustomAuthenticationFailureHandler customAuthenticationFailureHandler = new CustomAuthenticationFailureHandler();
CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler = new CustomAuthenticationSuccessHandler();
//스프링 시큐리티의 필터 연결을 설정하기 위한 오버라이딩
//예외가 웹접근 URL을 설정한다
//ACL(Access Control List - 접근 제어 목록)의 예외 URL을 설정
//정적 자원에 대해서는 Security 설정을 적용하지 않음
@Override
public void configure(WebSecurity web)throws Exception{
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**");
}
//인터셉터로 요청을 안전하게 보호하는 방법을 설정하기 위한 오버라이딩
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/contract").hasAnyAuthority("ADMIN", "USER")
.antMatchers("/userauth").hasAnyAuthority("ADMIN")
.antMatchers("/auth").hasAuthority("ADMIN")
.antMatchers("/user").hasAuthority("ADMIN")
.antMatchers("/myhome").authenticated();
http.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(customAuthenticationSuccessHandler)
.failureHandler(customAuthenticationFailureHandler)
.usernameParameter("empNo")
.passwordParameter("password")
.permitAll();
http.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return EncryptType.PASSWORD.encrypt(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encode(rawPassword.toString()).equals(encodedPassword);
}
};
}
//사용자 세부 서비스를 설정하기 위한 오버라이딩
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
super.configure(auth);
}
}
3. SecurityUser.java(UserDetails 설정)
package security;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class SecurityUser implements UserDetails{
private Collection<? extends GrantedAuthority> authorities;
private String username; //id
private String password; //password
private String name; //name
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
4. UserDetailsServiceImpl.java(UserDetailsService 설정)
package security;
import java.util.ArrayList;
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.Component;
import login.repository.LoginRepository;
import login.vo.LoginVO;
@Component
public class UserDetailsServiceImpl implements UserDetailsService{
@Autowired
private LoginRepository loginRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LoginVO loginVO = loginRepository.findById(username);
SecurityUser securityUser = new SecurityUser();
if(loginVO != null) {
securityUser.setName(loginVO.getName());
securityUser.setUsername(loginVO.getEmpNo());
securityUser.setPassword(loginVO.getPassword());
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(loginVO.getRole()));
securityUser.setAuthorities(authorities);
}
return securityUser; //여기서 return된 UserDetails는 SecurityContext의 Authentication에 등록되어 인증 정보를 갖고 있는다.
}
}
5. CustomAuthenticationFailureHandler.java(로그인 실패시)
package security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException{
String errormsg = null;
if(exception instanceof UsernameNotFoundException) {
errormsg = "1";//계정이 존재하지 않습니다.
}else if(exception instanceof BadCredentialsException) {
errormsg = "2"; //아이디 혹은 비밀번호를 잘못 입력했습니다.
}else if(exception instanceof DisabledException) {
errormsg = "3";//인증되지 않은 계정입니다.(계정 비활성화)
}else if(exception instanceof SessionAuthenticationException) {
errormsg = "4"; //중복로그인
}
setDefaultFailureUrl("/login?error="+errormsg);
log.info("errormsg : {}, {}", errormsg, exception);
super.onAuthenticationFailure(request, response, exception);
}
}
6. CustomAuthenticationSuccessHandler.java(로그인 성공시)
package security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import login.repository.LoginRepository;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private LoginRepository loginRepository;
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authdata)
throws IOException, ServletException {
HttpSession session = null;
session = req.getSession(true);
session.setAttribute("loginUser", authdata.getPrincipal());
res.sendRedirect("/contract");
//에러 세션 지우기
clearAuthenticationAttributes(req);
log.info("session 정보 : {}", session);
log.info("로그인 유저 정보입니다. {}", session.getAttribute("loginUser"));
}
protected void clearAuthenticationAttributes(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if(session == null)
return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}
7. SecurityUtils.java(로그인한 유저의 정보를 쉽게 가져오기 위한 설정파일)
package security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityUtils {
public static SecurityUser getLoginUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
SecurityUser customUser = (SecurityUser) authentication.getPrincipal();
return customUser;
}
}
728x90
'FULLSTACK > SPRING' 카테고리의 다른 글
Spring Boot - JPA, Thymeleaf, tiles 설정 (0) | 2021.06.20 |
---|---|
Spring Boot - AJAX 통신 (0) | 2021.06.20 |
12/7~8 Spring Boot, JPA (0) | 2020.12.08 |
STS기반 SPRING - 구멍가게코딩단 책과 함께합니다 (0) | 2020.11.13 |
SPRING 4차시 - AOP, TRANSACTION, FILE UP/DOWN (0) | 2020.11.13 |