'Interceptor'에 해당되는 글 1건
- 2015.12.30 :: Spring Security를 이용한 login(2)
이제 코드를 살펴보자.
먼저 jpa로 person이라는 객체를 만들어 보자.
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 | package com.gno.sample.dto; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity(name="person") public class Person { private int idx; private String id; private String email; private String password; private String auth; public Person() { // TODO Auto-generated constructor stub } public Person(String id, String email, String password) { // TODO Auto-generated constructor stub this.id = id; this.email = email; this.password = password; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public int getIdx() { return idx; } public void setIdx(int idx) { this.idx = idx; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAuth() { return auth; } public void setAuth(String auth) { this.auth = auth; } @Override public String toString() { // TODO Auto-generated method stub return "name=" + id + ", password=" + password + ", email=" + email; } } | cs |
idx는 auto generator 로 auth는 권한을 저장하는 컬럼 나머진 이름 그대로 .... repository를 생략한다.
먼저 provider를 만들자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package com.gno.sample.security; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; public class CustomAuthenticationProvider extends DaoAuthenticationProvider { private final Log logger = LogFactory.getLog(this.getClass()); @Override public Authentication authenticate(Authentication arg0) throws AuthenticationException { // TODO Auto-generated method stub return super.authenticate(arg0); } } | cs |
위와 같이 선언하면 내부적으로 알아서 form에서 넘어온 데이터(Authentication은 form에 넘어온 정보를 저장하고 있다.)와 db에서 가져온 정보를 비교해준다. 상위 클래스 소스를 보면 내부적으로 무언가 비교를 해주고 있다. 그래서 override만 해주면 된다.
다음은 service 코드를 보자.
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 | package com.gno.sample.security; import java.util.ArrayList; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import com.gno.sample.dto.Person; import com.gno.sample.repository.PersonRepository; public class CustomUserDetailService implements UserDetailsService{ @Autowired private PersonRepository repo; @Override public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException { // TODO Auto-generated method stub ArrayList<Person> person = (ArrayList<Person>) repo.findById(id); if(person.size() == 0){ throw new UsernameNotFoundException(id); } //userDetail을 default로 사용할때 // return new User(person.get(0).getId(), person.get(0).getPassword(), "ROLE_USER"); return new CustomUserDetail(person.get(0)); } } | cs |
service는 단순히 form에서 넘어온 id를 repository에서 가져와 userDetail에 넘겨준다.
마지막으로 UserDetail을 보자.
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 | package com.gno.sample.security; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.gno.sample.dto.Person; public class CustomUserDetail implements UserDetails{ private Person person; public CustomUserDetail(Person person) { // TODO Auto-generated constructor stub this.person = person; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { // TODO Auto-generated method stub List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); authorities.add(new SimpleGrantedAuthority(person.getAuth())); return authorities; } @Override public String getPassword() { // TODO Auto-generated method stub return this.person.getPassword(); } @Override public String getUsername() { // TODO Auto-generated method stub return this.person.getId(); } @Override public boolean isAccountNonExpired() { // TODO Auto-generated method stub return true; } @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return true; } @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return true; } @Override public boolean isEnabled() { // TODO Auto-generated method stub return true; } } | cs |
UserDetail은 override 함수에 jpa 설정된 객체로 선언해주면 되고 가장 중요한 함수는 GrantedAuthority 함수이다. 이 함수에서 각 페이지에 관한 권한을 부여하게 된다.
결론적으로 service는 단순히 detail 객체를 콜하며, detail은 Authority(권한)을 부여만 해주는 역할만 한다. 이런것들의 전체를 비교해주는 것은 Provider에서 이루어진다.
이제 login 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 | package com.gno.sample.security; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException { // TODO Auto-generated method stub System.out.println(auth.getAuthorities().getClass().getName()); List<GrantedAuthority> authorities = (List<GrantedAuthority>) auth.getAuthorities(); String strAuth = authorities.get(0).getAuthority(); Cookie cookie = new Cookie("auth", strAuth); cookie.setPath("/"); // ���߿� ������Ƽ�� ���� // cookie.setDomain("test.com"); response.addCookie(cookie); if(strAuth.equals("ROLE_ADMIN")){ response.sendRedirect(request.getContextPath() + "/admin.do"); }else{ response.sendRedirect(request.getContextPath() + "/user.do"); } } } | cs |
권한에 따라 어느 페이지로 갈지 redirect 해주는거 말고 없다. failure역시 마찬가지다.
이제 interceptor를 살펴보자.
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 | package com.gno.sample.security; import java.util.Collection; import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /* * 여기서는 세션만 체크하지 authority 가지고는 아무짓도 안 합니다 */ public class CustomInterceptor extends HandlerInterceptorAdapter { // HandlerInterceptorAdapter 를 상속 받아야 intercepter 등록이 가능함 private static final Logger logger = Logger .getLogger(CustomInterceptor.class); // public String getSessionID() { // SecurityContext ctx = SecurityContextHolder.getContext(); // // if (SecurityContextHolder.getContext().getAuthentication() != null) { // WebAuthenticationDetails detail = (WebAuthenticationDetails) ctx // .getAuthentication().getDetails(); // // return detail.getSessionId(); // // } // return null; // } @SuppressWarnings("unchecked") @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // boolean result = false; String rootPath = request.getContextPath(); if(request.getServletPath().equals("/login.do")){ return true; } try { logger.debug("enter intercepter"); HttpSession session = request.getSession(false); if (session == null) { // session non exist response.sendRedirect("login.do"); // response.sendRedirect(rootPath+"/login"); // index.jsp 로 이동, web.xml 에 설정 되어 있음 (<welcome-file-list> 태그) return false; } else { SecurityContext ctx = (SecurityContext) session .getAttribute("SPRING_SECURITY_CONTEXT"); if(ctx == null){ response.sendRedirect(rootPath+"/login.do"); return false; }else{ return true; } } } catch (Exception e) { e.printStackTrace(); logger.debug(e.getMessage()); return false; } } } | cs |
interceptor는 preHandle과 postHandle이 있는데 Contoller의 함수가 호출되기전 과 호출된후의 일들을 검사(?) 할수 있다. login의 경우 페이지간 이동 포함해서 session 유지를 위한 것들을 검증하기 때문에 preHandle에 선언을 하며 SecurityContext의 session만 검사를 해주는게 끝이다. true일경우는 현재 가지고 있는 url로 이동하며 false일 경우는 redirect 곳으로 이동할때 return 값을 false로 선언한다.
여기까지 spring security의 설명은 끝이며 모든 소스는 (https://github.com/gnogun/SpringSecuritySample.git)에 있다.