스프링 부트를 공부하면서 스프링에서 제공하는 spring security를 통해 로그인 기능을 편리하게 구현할 수 있다는 것을 알았다.
기존에 회사에서는 interceptor 기능을 통해서 로그인 기능을 만들어서 사용했다. 둘다 좋은 방식이지만 spring security를 사용하면 csrf 공격도 막을 수 있어서 한번 사용해봐도 좋을 것 같다.
1. 라이브러리 pom.xml
1 2 3 4 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> | cs |
2. SpringSecurityAdaptor
-> 스프링 시큐리티에 필요한 내용을 정의하는 configuration을 생성해야한다.
-> WebSecurityConfigurerAdapter 클래스를 상속받아서 configure 메서드를 재정의 해야한다.
-> @EnableWebSecurity, @EnableGlobalAuthentication와 같은 애노테이션을 사용하여 스프링 시큐리티 사용을 정의 해야 한다.
-> public void configure(WebSecurity web) throws Exception 메서드를 재정의하여 로그인 상관 없이 허용을 해줘야할 리소스 위치를 정의한다.
-> protected void configure(HttpSecurity http) throws Exception 메소드를 재정의하여 로그인 URL, 권한분리, logout URL 등등을 설정할 수 있다. (자세한 설명은 메서드에 주석으로 확인)
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | package com.wedul.common.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication; 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.web.util.matcher.AntPathRequestMatcher; import com.wedul.wedulpos.user.serviceImpl.AuthProvider; /** * Security Configuration * * @author wedul * */ @Configuration @EnableWebSecurity @EnableGlobalAuthentication @ComponentScan(basePackages = {"com.wedul.*"}) public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired AuthProvider authProvider; @Autowired AuthFailureHandler authFailureHandler; @Autowired AuthSuccessHandler authSuccessHandler; @Override public void configure(WebSecurity web) throws Exception { // 허용되어야 할 경로들 web.ignoring().antMatchers("/resources/**", "/dist/**", "/weather", "/user/password/find", "/user/join", "/user/email", "/user/send/temppw", "/findpw", "/user/findpw", "/user/cert/check", "/join", "/getLanguage/**", "/getMessage"); // #3 } @Override protected void configure(HttpSecurity http) throws Exception { // 로그인 설정 http.authorizeRequests() // ROLE_USER, ROLE_ADMIN으로 권한 분리 유알엘 정의 .antMatchers("/", "/user/login", "/error**").permitAll() .antMatchers("/**").access("ROLE_USER") .antMatchers("/**").access("ROLE_ADMIN") .antMatchers("/admin/**").access("ROLE_ADMIN") .antMatchers("/**").authenticated() .and() // 로그인 페이지 및 성공 url, handler 그리고 로그인 시 사용되는 id, password 파라미터 정의 .formLogin() .loginPage("/user/login") .defaultSuccessUrl("/") .failureHandler(authFailureHandler) .successHandler(authSuccessHandler) .usernameParameter("id") .passwordParameter("password") .and() // 로그아웃 관련 설정 .logout().logoutRequestMatcher(new AntPathRequestMatcher("/user/logout")) .logoutSuccessUrl("/") .invalidateHttpSession(true) .and() // csrf 사용유무 설정 // csrf 설정을 사용하면 모든 request에 csrf 값을 함께 전달해야한다. .csrf() .and() // 로그인 프로세스가 진행될 provider .authenticationProvider(authProvider); } } | cs |
3. 로그인 검증을 진행하는 AuthProvider
-> AuthenticationProvider 인터페이스를 구현
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package com.wedul.wedulpos.user.serviceImpl; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Component; import com.wedul.common.util.Constant; import com.wedul.common.util.HashUtil; import com.wedul.wedulpos.user.dto.MyAuthenticaion; import com.wedul.wedulpos.user.dto.UserDto; import com.wedul.wedulpos.user.service.UserService; /** * 인증 프로바이더 * 로그인시 사용자가 입력한 아이디와 비밀번호를 확인하고 해당 권한을 주는 클래스 * * @author wedul * */ @Component("authProvider") public class AuthProvider implements AuthenticationProvider { @Autowired UserService userService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String id = authentication.getName(); String password = HashUtil.sha256(authentication.getCredentials().toString()); UserDto user = userService.selectUser(new UserDto(id)); // email에 맞는 user가 없거나 비밀번호가 맞지 않는 경우. if (null == user || !user.getPassword().equals(password)) { return null; } List<GrantedAuthority> grantedAuthorityList = new ArrayList<>(); // 로그인한 계정에게 권한 부여 if (user.isIsadmin()) { grantedAuthorityList.add(new SimpleGrantedAuthority(Constant.ROLE_TYPE.ROLE_ADMIN.toString())); } else { grantedAuthorityList.add(new SimpleGrantedAuthority(Constant.ROLE_TYPE.ROLE_USER.toString())); } // 로그인 성공시 로그인 사용자 정보 반환 return new MyAuthenticaion(id, password, grantedAuthorityList, user); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } } | cs |
4. 로그인 성공, 실패 후 진행되는 handler 클래스
-> 로그인 성공 그리고 실패 후 진행될 클래스 정의
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package com.wedul.common.config; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; /** * 로그인 실패 핸들러 * * @author wedul * */ @Component public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 실패 시 response를 json 형태로 결과값 전달 response.getWriter().print("{\"success\": false}"); response.getWriter().flush(); } } package com.wedul.common.config; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; /** * 로그인 성공 핸들러 * * @author wedul * */ @Component public class AuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { response.setStatus(HttpServletResponse.SC_OK); // 성공 시 response를 json형태로 반환 response.getWriter().print("{\"success\": true}"); response.getWriter().flush(); } } | cs |
스프링 시큐리티를 처음 적용해 볼때는 어려움도 많았지만 적용하니 엄청 간단하게 사용할 수 있어서서 좋았다. 적용된 소스코드에 대한 정확한 정보는 https://github.com/weduls/wedulpos_boot 여기서 확인 할 수 있다.
'web > Spring' 카테고리의 다른 글
Spring, spring-boot의 mvc 다양한 설정 설명 (0) | 2018.06.03 |
---|---|
Spring framework에서 html을 pdf만들어 다운로드 하기 (29) | 2018.05.27 |
spring boot에 https 접속 적용하기 (0) | 2018.05.27 |
Spring에서 url 요청하는 RestTemplate 설명 (0) | 2018.05.27 |
spring boot에서 jsp사용하기 (0) | 2018.05.27 |