웹 프로젝트를 한 차례 진행하며 스프링을 기본적으로만 활용하고 애플리케이션에 대해 생각이 짧았던 것과, 기한 내 설정해보지 못한 다양한 영역 중 하나인 보안 기능을 구현하면서 사용하게 되었다. 처음에는 시큐리티 구현에 실패도 했고 굉장히 오래 걸렸지만, 프로젝트를 거듭하며 계속 연습하면서 보다 빠른 시간 내에 구현할 수 있게 되었다. 구현은 주먹구구적으로 진행했지만, 조금 이해가 된 상태에서 정리해보고자 글을 쓰게 되었다.
1. 스프링 시큐리티란?
Spring Security는 Java EE 기반 엔터프라이즈 소프트웨어 애플리케이션을 위한 보안 서비스이다. 스프링의 핵심 특성 중 하나인 종속성 주입원리를 활용하 서비스이다.
애플리케이션의 보안에서 중요한 두가지는 인증(authentication)과 권한(authorization)이다. 인증은 주체가 누구인가(예 : 사용자, 시스템)를 설정하는 것이고, 권한은 주체가 애플리케이션의 해당 영역에서 활동 가능한지를 결정하는 것이다.
2. 동작방식
사용자로부터 Http 요청을 받는다. 이 요청에서 사용자 이름과 암호를 추출한 후, 인증 개체를 생성한다.
이후 Authentication Provider의 목록으로 인증을 진행하고, 필요한 경우 UserDetails를 활용하여 User 객체를 생성할 수 있다. 인증에 성공하면 인증 개체가 반환되고, 그렇지 않으면 예외가 발생한다.
2. 구현
pom.xml
Spring Security를 사용하기 위해서는 다음과 같은 의존성이 필요하다.
이는 Maven 프로젝트의 pom.xml과 같은 설정 파일에 작성하여 모듈을 받아올 수 있다.
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.7.RELEASE</version>
</dependency>
</dependencies>
Authprovider.java
실제 인증 기능을 한다. 인증 전의 Authentication 객체를 통해 인증된 객체를 반환하는 역할을 한다.
AuthenticationProvider 인터페이스를 구현하여 Custom이 가능하며, 작성 후 AuthenticationManager에 등록하면 된다.
@Component
public class AuthProvider implements AuthenticationProvider {
@Autowired
UserService userService;
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String id = authentication.getName();
String password = authentication.getCredentials().toString
return authenticate(id, password);
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
public Authentication authenticate(String id, String password) throws org.springframework.security.core.AuthenticationException {
List<GrantedAuthority> grantedAuthorityList = new ArrayList<GrantedAuthority>();
LoginDTO principal = (LoginDTO) userService.loadUserByUsername(id);
if(principal == null) {
System.out.println("DTO is null");
throw new UsernameNotFoundException("wrongid");
} else if(principal != null && !principal.getPassword().equals(password)) {
System.out.println("DTO is not null");
throw new BadCredentialsException("wrongpw");
}
grantedAuthorityList.add(new SimpleGrantedAuthority(principal.getUserRole()));
return new MyAuthentication(id, password, grantedAuthorityList, principal);
}
}
Authentication.java
인증된 객체를 생성자를 사용하여 반환하는 역할을 한다.
public class MyAuthentication extends UsernamePasswordAuthenticationToken {
LoginDTO principal;
public MyAuthentication(String id, String password, List<GrantedAuthority> grantedAuthorityList, LoginDTO principal) {
super(id, password, grantedAuthorityList);
this.principal = principal;
}
public LoginDTO getUser() {return this.principal;}
}
UserConfig.java
설정과 관련된 파일이다. 인증된 객체가 페이지에 접근하는 경로를 설정할 수 있고, 로그인, 로그아웃 등의 URI 와 성공시 redirect되는 url 등을 설정할 수 있다.
@Configuration
@EnableWebSecurity
public class UserConfig extends WebSecurityConfigurerAdapter {
@Autowired
AuthProvider authProvider;
//DB를 사용한 사용자 인증 처리 설정
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService(userService()).passwordEncoder(passwordEncoder());
auth.authenticationProvider(authProvider);
}
@Override
public void configure(WebSecurity web) throws Exception {
// static 디렉터리의 하위 파일 목록은 인증 무시 ( = 항상통과 )
web.ignoring().antMatchers("/css/**",
"/js/**",
"/img/**",
"/lib/**",
"/templates/fragments/**",
"templates/layouts/**",
"templates/user/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
//권한 필요한 URI : antMatchers("URI").authenticated()
//권한 불필요한 URI : antMatchers("URI").permitAll()
http.authorizeRequests()
// .antMatchers("/**").permitAll()
//security_login
.and()
.formLogin()
//
.loginPage("/user/login")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/table/word") //main으로 수정
.successHandler(new LoginSuccessHandler())
.failureHandler(new LoginFailHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/templates/user/logout")
.logoutSuccessUrl("/")//main으로 수정
.invalidateHttpSession(true)
.permitAll();
}
}
LoginSuccessHandler.java
로그인 성공 시 redirect하기 전 메시지 및 response 객체를 작성할 수 있는 파일이다.
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("login Success in");
System.out.println("success : " + authentication.getName());
//return에 맞게 수정 필요
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
String data = " { \"response\" : {"+
" \"error\" : false , "+
" \"message\" : \"로그인하였습니다.\"} } ";
PrintWriter out = response.getWriter();
out.print(data);
out.flush();
out.close();
}
'기록 > Web' 카테고리의 다른 글
[Spring Security] @AuthenticationPrincipal과 ArgumentResolver (0) | 2021.11.09 |
---|---|
[React] Can't resolve 'redux-pender' in route (0) | 2021.10.27 |
[Spring][React] CORS Access-Control-Allow-Origin (0) | 2021.09.27 |
[React] 프로젝트 실행 (0) | 2021.09.27 |
[Mybatis] WARNING: An illegal reflective access operation has occurred (0) | 2021.08.20 |