[spring security] 로그인 디버깅하기
기본적인 프로젝트의 구조입니다.
지금 프로젝트는 mybatis와 DAO 부분을 CRUDMapper라는 interface를 통해서 상속받도록하여 간단하게 구성 할 수 있도록 하였습니다.
프로젝트를 보면 DAO 영역이 빠져 있습니다.
프로젝트를 실행하고 상단의 로그인 버튼을 클릭하면 로그인 페이지로 이동합니다.
Spring Security에서 로그인이 어떤 식으로 이루어지는지를 따라가 보도록 하겠습니다.
1. 로그인 페이지에 접속하려면 /signin/main 이라는 url을 get으로 호출하면 로그인 페이지가 보여집니다.
2. 이클립스에서 프로젝트를 선택하고, 상단 메뉴의 Search>File을 선택합니다.
File Search 탭에서 Containing text에 /signin/main 을 입력하고 하단의 Search 버튼을 클릭합니다.
3. 다음과 같은 검색 결과가 나옵니다. 6가지 항목이 검색되었습니다.
검색된 항목에 대해서 설명을 하면
① 이용자가 브라우저에서 주소(url)를 치면 제일 처음 만나는 곳입니다.
@RequestMapping 으로 주소(url)을 정의해줍니다.
url 요청시에 get, post에 대해서 구분하여 접속이 가능하도록 GET, POST로 선언을 합니다.
여기서는 GET 방식의 요청만이 접근 가능하도록 되어 있습니다.
② /signin/main으로 접속되어 data를 가지고 이용자에게 보여질 화면으로 돌아갈 곳이 어딘지를 선언합니다.
여기서는 /signin/main 으로 되어 있습니다.
/project-sample1/src/main/webapp/WEB-INF/views/signin/main.jsp
main.jsp 페이지로 간다는 것인데 servlet-context.xml의 정의에 의해서 /signin/main 이 /signin/main.jsp가 열리게 됩니다.
/project-sample1/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
<property name="contentType" value="text/html; charset=UTF-8" />
</bean>
③ 로그인 실패시에 로그인 페이지로 이동하라고 선언되어 있는 것입니다.
④ Spring Security 설정 파일인 applicationContext-security.xml에서 로그인 페이지에 대해서 선언되어 있는 것입니다.
⑤ Spring Security 권한 선언에 의해서 접속불가인 페이지에 접속 할 때 돌아갈 페이지를 로그인 페이지로 선언한 것입니다.
⑥ 우리가 메인페이지에서 클릭한 로그인 주소(url)입니다.
3. 파어어폭스 브라우저에서 ①FireBug를 이용하여 로그인 아이디, 비밀번호의 소스를 살펴봅니다.
로그인 input box는 name이 ②j_username으로 되어 있고, 비밀번호는 name이 ③j_password로 되어 있습니다.
Spring Security에서는 이 값을 기본으로 로그인 정보를 받기 떄문에 수정하면 안 됩니다.
아이디, 패스워드를 전달할 action은 jquery를 이용하여 선언되어 있습니다.
class가 loginBtn으로 선언된 로그인 button을 클릭하면
main.jsp에서 $(document).ready(function() { ... } 안에 클릭시 이벤트를 선언해 놓았습니다.
$(".loginBtn").on("click", function(){
...
$("#loginInfoFrm").attr("action","<c:url value='/j_spring_security_check'/>");
$("#loginInfoFrm").attr("method", "post");
$("#loginInfoFrm").submit();
...
}
②j_username, ③j_password을 감싸고 있는 <form> 엘리먼트의 id는 loginFrm입니다.
로그인 button은 id가 loginInfoFrm으로 선언되어 있는 <form> 엘리먼트의 action을 값을 '/j_spring_security_check' 로 주고
post로 submit하여 Spring Security에게 로그인을 요청합니다.
4. '/j_spring_security_check' 가 요청되면
/project-sample1/src/main/java/com/jobtoy/service/impl/UserDetailsServiceImpl.java
의 loadUserByUsername(...) 함수가 실행됩니다.
applicationContext-security.xml에서
<beans:bean id="userDetailsService" class="com.jobtoy.service.impl.UserDetailsServiceImpl"></beans:bean>
선언되어 있습니다.
UserDetailsServiceImpl은 org.springframework.security.core.userdetails.UserDetailsService의 loadUserByUsername(...)를 구현하였습니다.
loadUserByUsername(...)는 아이디만 전달 받습니다.
전달받은 아이디로 datatase의 유저정보를 조회하여
org.springframework.security.core.userdetails.UserDetails의 구현체인 UserDetailsImpl에 정의된 이용자 정보 변수에
database에서 조회한 유저정보를 저장해 줍니다.
오류없이 이용자정보가 조회되고 UserDetailsImpl에서 저장되었다면 암호를 비교하는 루틴으로 넘어갑니다.
5. SpringSecurity의 비밀번호 암호화는 applicationContext-security.xml에서 encoder라는 것을 선언해두고
<beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"></beans:bean>
SpringSecurity에서 제공하는 암호화 함수를 이용하여
<authentication-manager erase-credentials="true" id="userAuthnticationManager">
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="encoder" />
</authentication-provider>
</authentication-manager>
비밀번호 암호화를 합니다.
여기서는 흐름을 이해하기 위하여 SpringSecurity에서 제공하는 암호화 함수를 이용하지 않고 그냥 text가 db상에 저장되도록 하였습니다.
그러기 위해서 이용자 암호화 클래스를 만들어서 적용하였습니다.
<!-- <beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"></beans:bean> -->
<beans:bean id="encoder" class="com.jobtoy.service.impl.CustomPasswordEncoder"></beans:bean>
CustomPasswordEncoder를 열어보면
public boolean isPasswordValid(String encPass, String rawPass, Object salt)
public String encode(String rawPass)
두개의 매소드가 제공됩니다.
isPasswordValid은 암호화된 패스워드와 이용자가 직접 입력한 패스워드를 비교해 줍니다.
원래는 복호화가 안 되도록 salt를 사용하여 텍스트를 암호화 하지만 여기서는 텍스트를 base64 암호화된 전문으로 만드는 곳을 주석처리 했습니다.
// 패스워드 암호화 적용하기
//if(encUtil.SWDec(encPass).equals(rawPass)) {
// result = true;
//}
isPasswordValid은 단지
1)로그인시 사용자가 입력한 비밀번호 텍스트(암호화 안 된 텍스트)와
2)Database에서 이용자 정보를 조회해서 가져온 비밀번호(암호화된 텍스트)를
받습니다. Database에 가져온 비밀번호는 회원가입시에 암호화하여 Database에 저장한 것입니다.
로그인시 사용자가 입력한 비밀번호를 다시 암호화해서 나온 값과 Database상에서 가져온 저장된 비밀번호(암호화된 텍스트)가 같은지를 보는 것입니다.
같다면 true를 return하고 다음 루틴으로 넘어갑니다.
encode는 회원가입시에 이용자가 비밀번호를 입력한 값을 암호화하여 Database에 저장 할 때 사용하기 위한 메소드로 보면 됩니다.
6. 정상적으로 암호까지 비교가 끝났다면 applicationContext-security.xml에서 선언된 loginSuccessHandler가 실행됩니다.
beans:bean id="loginSuccessHandler" class="com.jobtoy.service.impl.LoginSuccessHandler"></beans:bean>
loginSuccessHandler에서 적절한 루틴을 통해서 원하는 페이지로 이동합니다.
response.sendRedirect(request.getContextPath() + "/");
현재는 메인페이지(/)로 이동하도록 되어 있습니다.
7. 로그인이 실패한다면 applicationContext-security.xml에서 선언된 LoginFailureHandler가 실행됩니다.
beans:bean id="loginFailureHandler" class="com.jobtoy.service.impl.LoginFailureHandler"></beans:bean>
마찮가지로 적절한 루틴을 통해서 원하는 페이지로 이동합니다.
8. 로그인이 성공한 다음 로그인 정보는 섹션에 저장되어 있습니다. 자바소스상에서 로그인 이용자정보를 가져오려면
SecurityContext context = SecurityContextHolder.getContext();
Object principalObj = context.getAuthentication().getPrincipal();
String userId = ((UserDetailsImpl) principalObj).getUsername();
을 사용하여 로그인 id와 기타정보를 가져 올 수 있습니다.
[spring security] 로그인 디버깅하기
'programming > spring_security' 카테고리의 다른 글
[spring security] Controller에서 JSTL에 Data를 담아 JSP 넘기기 (0) | 2014.08.27 |
---|---|
[spring security] HTML 페이지에서 Controller 주소 요청하고 Data 받기 (0) | 2014.08.27 |
[spring security] MySQL DB 생성하기 (2) | 2014.08.23 |
[spring security] 프로젝트 디버그로 실행하기 (0) | 2014.08.23 |
[spring security] 프로젝트 import하기 (0) | 2014.08.23 |