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" />
클래스 구조를 보면 아래와 같다.
아래와 같은 필터 체인의 순서롤 작동하게 된다.
구현 방법은 각각의 핸들러에서 응답을 JSON1이나 원하는 포멧으로 만들어서 출력하면 된다.
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 |
ajax.login.1.war
ajax.login.1.zip