Introduction
스프링 시큐리티를 이용하여 로그인을 처리할 때에 AJAX 방식으로 로그인 하는 방법이다.
크게 2가지로 볼 수 있겠다. ㅎㅎ
Using Handler
기본적인 10단계의 필터 체인 중에 UsernamePasswordAuthencationFilter 단계의 "authentication-success-handler-ref"와 "authentication-success-handler-ref" 를 이용하는 방법이다.
<security:http auto-config="true" disable-url-rewriting="true" use-expressions="true"> <security:intercept-url pattern="/login.*" access="permitAll" /> <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" /> <security:form-login login-page="/login.html" username-parameter="id" password-parameter="password" login-processing-url="/login.ajax" default-target-url="/index.html" authentication-success-handler-ref="loginSuccessHandler" authentication-failure-handler-ref="loginFailureHandler" /> <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/" /> </security:http> <!-- 로그인 성공시 핸들러 --> <bean id="loginSuccessHandler" class="ajax.login.security.LoginSuccessHandler" /> <!-- 로그인 실패시 핸들러 --> <bean id="loginFailureHandler" class="ajax.login.security.LoginFailureHandler" />
클래스 구조를 보면 아래와 같다.
아래와 같은 필터 체인의 순서롤 작동하게 된다.
구현 방법은 각각의 핸들러에서 응답을 JSON 1이나 원하는 포멧으로 만들어서 출력하면 된다.
public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { ObjectMapper om = new ObjectMapper(); Map<String, Object> map = new HashMap<String, Object>(); map.put("success", true); map.put("returnUrl", getReturnUrl(request, response)); // {"success" : true, "returnUrl" : "..."} String jsonString = om.writeValueAsString(map); OutputStream out = response.getOutputStream(); out.write(jsonString.getBytes()); } /** * 로그인 하기 전의 요청했던 URL을 알아낸다. * * @param request * @param response * @return */ private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) { RequestCache requestCache = new HttpSessionRequestCache(); SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest == null) { return request.getSession().getServletContext().getContextPath(); } return savedRequest.getRedirectUrl(); } }
public class LoginFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { ObjectMapper om = new ObjectMapper(); Map<String, Object> map = new HashMap<String, Object>(); map.put("success", false); map.put("message", exception.getMessage()); // {"success" : false, "message" : "..."} String jsonString = om.writeValueAsString(map); OutputStream out = response.getOutputStream(); out.write(jsonString.getBytes()); } }
Using @MVC
다른 방법으로 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법이 있다.
개인적으로 이 두번째 방법을 선호한다. 왜냐하면 요청을 "application/json" 으로 유동적으로 한다던가 입출력관련 AOP 등등을 내 맘대로 사용할 수 있다. 그냥 컨트롤러니까~
@Controller public class LoginController { @RequestMapping(value = "/login.html", method = RequestMethod.GET) public void loginPage(ModelAndView mav) { } @Autowired AuthenticationManager authenticationManager; @Autowired SecurityContextRepository repository; @RequestMapping(value = "/login.ajax", method = RequestMethod.POST) @ResponseBody public ModelMap login(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "id") String username, @RequestParam(value = "password") String password) { ModelMap map = new ModelMap(); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( username, password); try { // 로그인 Authentication auth = authenticationManager.authenticate(token); SecurityContextHolder.getContext().setAuthentication(auth); repository.saveContext(SecurityContextHolder.getContext(), request, response); map.put("success", true); map.put("returnUrl", getReturnUrl(request, response)); } catch (BadCredentialsException e) { map.put("success", false); map.put("message", e.getMessage()); } return map; } /** * 로그인 하기 전의 요청했던 URL을 알아낸다. * * @param request * @param response * @return */ private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) { RequestCache requestCache = new HttpSessionRequestCache(); SavedRequest savedRequest = requestCache.getRequest(request, response); if (savedRequest == null) { return request.getSession().getServletContext().getContextPath(); } return savedRequest.getRedirectUrl(); } }
스프링 시큐리티 설정 하는 부분에서 그냥 <security:http> 를 써도 되지만 사용하지 않는 UsernamePasswordAuthenticationFilter 같은 필터 체인을 사용하지 않기 위해 수동으로 구성해 보았다. 꽤 어렵... -_-;; (자세한 내용은 context-security.xml 참조!)
그 밖에도 요청 헤더를 까서 요청이 AJAX 인지 아닌지를 판단해서 다른 처리를 하는 것도 있고, 구현할 수 있는 방법은 무궁무진하다.
- JSON(제이슨, JavaScript Object Notation)은, 인터넷에서 자료를 주고받을 때 그 자료를 표현하는 방법이다. 자료의 종류에 큰 제한은 없으며, 특히 컴퓨터 프로그램의 변수값을 표현하는 데 적합하다. 그 형식은 자바스크립트의 구문 형식을 따르지만, 프로그래밍 언어나 플랫폼에 독립적이므로 C, C++, C#, 자바, 자바스크립트, 펄, 파이썬 등 많은 언어에서 이용할 수 있다. [본문으로]
'Framework > Spring Security' 카테고리의 다른 글
Spring Security Session Destroy (4) | 2013.10.27 |
---|---|
MySql Password Encoder (0) | 2013.09.21 |