> Hello World !!!

     

@syaku

스프링 시큐리티 로그인 Handler Ajax #3 : Spring Framework Security Login Ajax 스프링프레임워크 #3

written by Seok Kyun. Choi. 최석균

스프링 시큐리티 로그인 Handler Ajax #3

스프링 시큐리티 연재 포스팅
2014/08/29 - [개발노트/Spring] - 스프링 시큐리티 로그인 #1 : Spring Framework Security Login 스프링프레임워크 #1
2014/09/04 - [개발노트/Spring] - 스프링 시큐리티 로그인 커스텀 #2 : Spring Framework Security Login Custom 스프링프레임워크 #2
2014/09/04 - [개발노트/Spring] - 스프링 시큐리티 로그인 Handler Ajax #3 : Spring Framework Security Login Ajax 스프링프레임워크 #3
2014/10/25 - [개발노트/Spring] - 스프링 시큐리티 커스텀 로그인 : Spring Security Custom Login UserDetailsService AuthenticationProvider #4 스프링프레임워크/Spring Framework

개발환경

Mac OS X 10.9.4
JAVA 1.6
Apache Tomcat 7.x
Spring 3.1.1
Spring Tool Suite 3.5.1
Maven 2.5.1
jQuery 1.11.1

이번에는 로그인을 ajax 방식으로 요청하고 핸들러를 이용하여 결과를 전달하는 방법을 알아보자.
다양한 예제를 위핸 ajax 방식와 submit 방식을 모두 구현하였고, 응답방식은 xml,json,html 3가지를 지원한다.

@소스 pom.xml

<!-- Spring Security -->
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-web</artifactId>
     <version>${org.springframework-version}</version>
</dependency>
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-config</artifactId>
     <version>${org.springframework-version}</version>
</dependency>

<!— CGLib —>
<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.1</version>
     <type>jar</type>
     <scope>compile</scope>
</dependency>

@소스 security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
     xmlns="http://www.springframework.org/schema/security"
     xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/security
     http://www.springframework.org/schema/security/spring-security.xsd
     ">

     <http auto-config="true" use-expressions="true" access-denied-page="/denied">

     <form-login
     login-page="/signin"
     username-parameter="user_id"
     password-parameter="password"
     authentication-success-handler-ref="signinSuccessHandler"
     authentication-failure-handler-ref="signinFailureHandler"
     default-target-url="/mypage"
     always-use-default-target="false"
     />

     <logout
     invalidate-session="true"
     logout-success-url="/signin"
     logout-url="/signout" />

     </http>

     <beans:bean id="signinSuccessHandler" class="com.syaku.security.SigninSuccessHandler" />
     <beans:bean id="signinFailureHandler" class="com.syaku.security.SigninFailureHandler">
          <beans:property name="defaultFailureUrl" value="/signin?error=true" />
     </beans:bean>

     <authentication-manager>
          <authentication-provider>
               <password-encoder ref="passwordEncoder"/>
               <user-service>
                    <user name="guest" password="35675e68f4b5af7b995d9205ad0fc43842f16450" authorities="ROLE_USER"/>
                    <user name="admin" password="d033e22ae348aeb5660fc2140aec35850c4da997" authorities="ROLE_ADMIN"/>
               </user-service>
          </authentication-provider>
     </authentication-manager>

     <beans:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder" />

</beans:beans>

스프링 시큐리티 설정에서 authentication-success-handler-ref, authentication-failure-handler-ref 추가하고 아래 클래스 빈을 적용하였다.
로그인 실패될 경우 호출되는 authentication-failure-url 속성은 삭제되었다. 그래서 defaultFailureUrl 프로퍼티를 이용하여 url를 리다이렉트되게 구현하였다.

로그인을 성공하면 authentication-success-handler-ref 호출되고, 실패하면 authentication-failure-handler-ref 호출된다.

@소스 SigninSuccessHandler.java

package com.syaku.security;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

public class SigninSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
     private static final Logger logger = LoggerFactory.getLogger(SigninSuccessHandler.class);

     @Override
     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException {

          String accept = request.getHeader("accept");

          if( StringUtils.indexOf(accept, "html") > -1 ) {

               super.onAuthenticationSuccess(request, response, auth);

          } else if( StringUtils.indexOf(accept, "xml") > -1 ) {
               response.setContentType("application/xml");
               response.setCharacterEncoding("utf-8");

               String data = StringUtils.join(new String[] {
                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
                    "<response>",
                    "<error>false</error>",
                    "<message>로그인하였습니다.</message>",
                    "</response>"
               });

               PrintWriter out = response.getWriter();
               out.print(data);
               out.flush();
               out.close();

          } else if( StringUtils.indexOf(accept, "json") > -1 ) {
               response.setContentType("application/json");
               response.setCharacterEncoding("utf-8");

               String data = StringUtils.join(new String[] {
                    " { \"response\" : {",
                    " \"error\" : false , ",
                    " \"message\" : \"로그인하였습니다.\" ",
                    "} } "
               });

               PrintWriter out = response.getWriter();
               out.print(data);
               out.flush();
               out.close();

          }
     }
}

응답 방식을 accept 에 담아 형식을 확인한다. response.setCharacterEncoding("utf-8"); 언어셋을 먼저 지정하고 PrintWriter out = response.getWriter(); 선언해야 한다.

@소스 SigninFailureHandler.java

package com.syaku.security;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

public class SigninFailureHandler extends SimpleUrlAuthenticationFailureHandler {

     public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect-login-failure";
     private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
     public String getTargetUrlParameter() {
          return targetUrlParameter;
     }

     public void setTargetUrlParameter(String targetUrlParameter) {
          this.targetUrlParameter = targetUrlParameter;
     }

     @Override
     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

          String accept = request.getHeader("accept");

          String error = "true";
          String message = "로그인실패하였습니다.";

          if( StringUtils.indexOf(accept, "html") > -1 ) {

               String redirectUrl = request.getParameter(this.targetUrlParameter);
               if (redirectUrl != null) {
                    super.logger.debug("Found redirect URL: " + redirectUrl);
                    getRedirectStrategy().sendRedirect(request, response, redirectUrl);
               } else {
                    super.onAuthenticationFailure(request, response, exception);
               }

          } else if( StringUtils.indexOf(accept, "xml") > -1 ) {
               response.setContentType("application/xml");
               response.setCharacterEncoding("utf-8");

               String data = StringUtils.join(new String[] {
                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
                    "<response>",
                    "<error>" , error , "</error>",
                    "<message>" , message , "</message>",
                    "</response>"
               });

               PrintWriter out = response.getWriter();
               out.print(data);
               out.flush();
               out.close();

          } else if( StringUtils.indexOf(accept, "json") > -1 ) {
               response.setContentType("application/json");
               response.setCharacterEncoding("utf-8");

               String data = StringUtils.join(new String[] {
                    " { \"response\" : {",
                    " \"error\" : " , error , ", ",
                    " \"message\" : \"", message , "\" ",
                    "} } "
               });

               PrintWriter out = response.getWriter();
               out.print(data);
               out.flush();
               out.close();

          }
     }
}

마지막으로 로그인 뷰페이지를 수정한다.

@소스 signin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html lang="ko">
     <head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>Spring security</title>

     <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
     <script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
     </head>

     <body>

     <script>

     function get_msg(message) {
          var move = '70px';
          jQuery('#message').text(message);
          jQuery('#message').animate({
               top : '+=' + move
          }, 'slow', function() {
               jQuery('#message').delay(1000).animate({ top : '-=' + move }, 'slow');
          });
     }

     <c:if test="${error == 'true'}">
     jQuery(function() {
          get_msg("로그인 실패하였습니다.");
     });

     </c:if>

     function signin() {
          $.ajax({
               url : './j_spring_security_check',
               data: $('form input').serialize(),
               type: 'POST',
               dataType : 'json',
               beforeSend: function(xhr) {
                    xhr.setRequestHeader("Accept", "application/json");
               }
          }).done(function(body) {

               var message = body.response.message;
               var error = body.response.error;
               if (error) get_msg(message);

               if (error == false) {
                    var url = '${referer}';
                    if (url == '') url = '<c:url value="/mypage" />';
                    location.href = url;
               }

          });

     }
     </script>

     <div>
     <div id="message" style="width:300px;position:absolute; top:-60px;border: 1px;border-color: #000;"></div>
     </div>

     <div style="margin-top:100px;">
     <form id="form" action="./j_spring_security_check" method="post">
     아이디 : <input type="text" id="user_id" name="user_id">
     비밀번호 : <input type="password" id="password" name="password">
     <button type="button" onclick="signin();">Ajax Sign in</button>
     <button type="submit">Submit Sign in</button>
     </form>

     </div>

     </body>

</html>

버튼을 2개를 만들었다. Ajax 용 버튼과 Submit 용 버튼이며, Submit 은 리다이렉트되는 방식으로 구현하였다.

posted syaku blog

Syaku Blog by Seok Kyun. Choi. 최석균.

http://syaku.tistory.com