본문 바로가기
IT/Web

Spring Boot + Spring Security + MyBatis + Vue.js

by 최고영회 2018. 9. 6.
728x90
반응형
SMALL
Spring Security + Vue.js (login 화면) + Mybatis 로 인증 후 SPA 로 동작하도록 구성 하기 

제목처럼 Spring Boot 로 backend를 구성하고 Spring Security를 이용해서 '로그인/인증' 을 구현하고 Vue.js 로 frontend를 구현해 보자. 

먼저 Spring Security 관련해서 몇가지 설정을 해 본다.
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	AuthProvider authProvider;

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/static/css/**, /static/js/**, *.ico");		
	} 

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.csrf().disable();		// 개발 시 에만
		
		http.authorizeRequests()
			.antMatchers("/user/**").access("ROLE_USER")				// 사용자 페이지
			.antMatchers("/admin/**").access("ROLE_ADMIN")				// 관리자 페이지
			.antMatchers("/login").permitAll()
			.antMatchers("/**").authenticated();
	
		http.formLogin()
			.loginPage("/login")
			.defaultSuccessUrl("/home")
			.usernameParameter("id")
			.passwordParameter("password");
		
		http.logout()
			.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
			.logoutSuccessUrl("/")
			.invalidateHttpSession(true);
		
		http.authenticationProvider(authProvider);
		
	}

위 코드에서 중요한 부분은 authProvider 부분이다. 
직접 작성한 Class 인 AuthProvider 를 통해 인증을 하겠다는 의미이며 AuthProvider.class 에서 Service 를 호출하고 MyBatis와 연동할 예정이다. 

AuthProvider.java 는 아래와 같다.
@Component
@Slf4j
public class AuthProvider implements AuthenticationProvider{
	@Autowired
    MemberService service;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		String userId = authentication.getName();
		String userPw = authentication.getCredentials().toString();
		return authenticate(userId, userPw);
	}
	
	private Authentication authenticate(String id, String pw) throws AuthenticationException {
		Member m = new Member(id, pw);
		m = service.getMemberByUserName(id);
		if ( m == null || !m.getPw().equals(pw)) {
			log.error("{} is not exist or password is not equals", id);
			return null;
		}
		List authList = new ArrayList<>();
        /** 
		 * Role 처리 필요, 일단 임의로 USER Role을 부여한다.  
		**/
		authList.add(new SimpleGrantedAuthority("ROLE_USER"));		
        return new MyAuthentication(id, pw, authList, m);
	}

	@Override
	public boolean supports(Class authentication) {
		return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
	}
}
코드에 대한 설명은 딱히 필요 없을 정도로 간단하다. MyAuthentication.java 는 아래와 같다.
@Setter @Getter
public class MyAuthentication extends UsernamePasswordAuthenticationToken{
	private static final long serialVersionUID = 1L;
	
	Member member;
	
	public MyAuthentication(String id, String pw, List authList, Member member) {
		super(id, pw, authList);
		this.member = member;
	}
}


이제 Spring Security 를 통해 제공되는 login page 에서 ID/PW 를 입력하면 

AuthProvider 를 통해 실제 DB 에 있는 사용자 값(ID, PW)을 확인하여 인증하게 된다. 

물론 이건 테스트 코드이기 때문에 PW 에 대한 암호화는 생략해 둔 상태이다. 


Spring Security 에서 제공하는 login page 는 테스트를 위해 사용하고 실제로는 그래도 조금 예쁜(?) 로그인 페이지가 필요하다. 

SecurityConfig.java 에서 http.formLogin() .loginPage("/login") 로 설정해 두었으니 그에 알맞은 controller 와 login page (.html)을 만들어 보자.

@RequestMapping(value="/login", method=RequestMethod.GET)
public String login(Model model, String error, String logout) {
	if ( error != null ) {
		model.addAttribute("errorMsg", "Your username and password are invalid.");
	}
	if ( logout != null ) {
		model.addAttribute("msg", "You have been logged out successfully");
	}
	return "login.html";
}
error 와 logout 에 대한 메시지를 보여주기 위해 일단 위와 같이 설정해 두었으나 현재 코드에서는 errorMsg 와 msg 는 login page 에 보여주지 않는다. (이건 나중에..) 
일단 위와 같이 작성해 두고 Spring Boot 에서 바라보는 src/main/resources/static/ 에 login.html 을 하나 만들어 놓는다. 
vue.js 의 index.html 을 이용해서 어떻게 해보려고 했는데... Security 의 인증 부분과 Vue의 Router를 어떻게 처리해야 하는지 아직 잘 모르겠어서 login.html 을 아래와 같이 별도로 작성해 두었다.

이제 Project를 run 하고 localhost:8080/ 으로 접속해 보면 아래와 같이 simple 한 login page 가 나타난다.

실제로 로그인을 성공하면 (DB에 있는 값으로 ID/PW 입력) vue 로 만들어둔 home 화면이 나온다.

home 에서 user page 와 admin page 는 vue router 를 이용하여 SPA 로 만든 상태다. 

여기서 문제가 하나 생긴다. 

Spring Security 를 통해 인증을 하고 Role 을 통해 메뉴/페이지별로 권한을 체크하도록 해 두었는데 

SPA 의 경우 Server 로 요청하지 않고 Front End 에서 화면 변화가 일어나기 때문에 

Spring Security에서 설정해 둔 ROLE 은 무의미하게 되는 것이다. 

다시 말하면, 현재 임시로 로그인에 성공할 경우 "ROLE_USER" 권한을 부여해 두었고 Spring Security 에서는 아래 소스코드를 통해

http.authorizeRequests()
			.antMatchers("/user/**").access("ROLE_USER")				// 사용자 페이지
			.antMatchers("/admin/**").access("ROLE_ADMIN")				// 관리자 페이지
			.antMatchers("/login").permitAll()
			.antMatchers("/**").authenticated();

/admin/** 은 ROLE_ADMIN 만 접근 가능하도록 되어 있는데 

vue 의 home 에서 admin page 를 클릭할 경우 Spring Security 의 filter chain 을 타지 않기 때문에 admin page 로 이동이 잘 된다는 것이다... 

이 문제는 다음 포스팅에서 해결해 보도록 하자. (Vue.js 의 Role 기반 Router 에서 처리해야 할듯 한데... )


소스코드는 git 에서 확인 가능 하다. 





728x90
반응형
LIST