Version 1.0.1
저작권 © 2007-2011 삼성SDS
어프리케이션에서 사용자가 로그인 했을 때, 사용자 정보를 세션에 저장하고 사용자 정보에 따른 로케일 설정, ThreadLocal을 이용해 서비스에서 사용자 정보를 사용하는 예제를 포함하고 있다.
Installation
Command 창에서 다음과 같이 명령어를 입력하여 login plugin을 설치한다.
mvn anyframe:install -Dname=login
installed(mvn anyframe:installed) 혹은 jetty:run(mvn clean jetty:run) command를 이용하여 설치 결과를 확인해볼 수 있다.
Dependent Plugins
| Plugin Name | Version Range |
|---|---|
| core | 2.0.0 > * |
일반적인 Web Application 에서는 사용자 인증을 위해서는 Id, Password방식을 많이 사용한다.
Login Plugin은 아래와 같은 단계로 진행된다.
1. 사용자 인증
2. 사용자 정보 조회
3. 사용자 정보 세션 저장 및 로케일 설정
4. Interceptor에서 ThreadLocal에 세션정보 저장
5. Biz. Service에서 ThreadLocal활용
6. User Locale 변경
보통은 Web Application의 사용자 인증은 Id, Password 방식을 많이 사용한다. 아래는 Login Plugin의 로그인화면이다. 사용자 Locale에 따라 두 명의 사용자가 등록되어 있다.

로그인 화면에서 아이디, 패스워드를 입력한 후 Login 버튼을 클릭하면 AuthenticationController에서 사용자 입력값(id, password)을 이용해 사용자인증 절차를 거친다. Login Plugin에서는 AuthenticationService에서 사용자 인증을 한다. 만약 인증에 실패 했을 경우에는 아래와 같이 화면에 인증실패 메세지를 보여준다.

인증에 성공하게 되면 Application에서 필요한 사용자 정보를 조회한다. 사용자 이름, 사용자 로케일 정보를 조회한다. Login Plugin 에서는 UserService를 이용해 사용자 정보를 조회한다. 아래는 인증에 성공한 후 UserService의 API를 호출해 사용자 정보를 조회하는 샘플소스이다.
boolean certification = authenticationService.authenticate(loginInfo);
if(certification){
UserInfo userInfo = userService.getUserInfo(id);
..중략
}else{
..중략
}Application에서 사용자 정보는 자주 사용되므로 Session에 저장해 필요할 때 Session에서 꺼내 쓰도록 한다. 그러기 위해서는 Session에 앞서 조회한 사용자 정보를 저장하는 로직이 필요하다.
아래는 사용자 인증, 정보조회, 세션 저장하는 AuthenticationController의 일부이다.
@RequestMapping("/login.do")
public String login(@RequestParam("id") String id,
@RequestParam("password") String password, Model model,
HttpSession session, HttpServletRequest request, HttpServletResponse response) throws Exception {
//사용자 인증
LoginInfo loginInfo = new LoginInfo();
loginInfo.setId(id);
loginInfo.setPassword(password);
loginInfo.setIpAddress(request.getRemoteAddr());
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
loginInfo.setTime(formatter.format(new Date()));
boolean certification = authenticationService.authenticate(loginInfo);
if(certification){
UserInfo userInfo = userService.getUserInfo(id);
localeResolver.setLocale(request, response, userInfo.getLanguage());
session.setAttribute("userInfo", userInfo);
return "forward:/loginMovieFinder.do?method=list";
}else{
model.addAttribute("loginError", "login.error");
return "/login/login";
}
}사용자 정보를 userInfo란 이름으로 Session에 저장한다.
AuthenticationInterceptor는 사용자가 서버에 Request를 보낼 경우 Session에 저장된 사용자 정보를 꺼내 ThreadLocal에 저장하는 역할을 한다. ThreadLocal에 저장된 값들은 Request가 끝나는 시점까지 유지되기 때문에 Biz. Service Layer에서 언제든지 필요한 정보를 꺼낼 수 있다.
AuthenticationInterceptor 설정은 아래와 같다.
<mvc:interceptors> <bean class="org.anyframe.plugin.login.interceptor.AuthenticationInterceptor" /> </mvc:interceptors>
AuthenticationInterceptor의 preHandler메소드는 아래와 같다.
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
if ( session != null ){
UserInfo userInfo = (UserInfo) session.getAttribute("userInfo");
if ( userInfo != null ){
ThreadLocalUtil.add("userInfo", (UserInfo) session.getAttribute("userInfo"));
}
}
return super.preHandle(request, response, handler);
}Session에 저장된 사용자 정보를 userInfo란 이름으로 ThreadLocal에 저장하고 있다. AuthenticationInterceptor의 afterCompletion메소드에서는 반드시 ThreadLocal에 저장된 값을 clearSharedInfo을 호출해 삭제하도록 한다.
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
ThreadLocalUtil.clearSharedInfo();
super.afterCompletion(request, response, handler, ex);
}ThreadLocalUtil은 ThreadLocal을 보다 쉽게 사용할 수 있도록 제공되는 클래스이다. Interceptor에서 Session에 저장된 사용자 정보를 ThreadLocalUtil의 add메소드를 이용해 ThreadLocal에 저장했다면 Biz. Service에서 언제든지 필요한 정보를 얻을 수 있다.
예를들어 각 Service Method에 AOP를 이용해 해당 메소드를 어떤 사용자가 호출했는지 로깅을 남길 수도 있다. 아래는 LoggingUserInfoAspect의 소스코드이다.
@Aspect
@Service
public class LoggingUserInfoAspect {
@Pointcut("execution(* org.anyframe.plugin.login..*Impl.*(..))")
public void loggingMethod() {
}
@Before("loggingMethod()")
public void beforeLogging(JoinPoint thisJoinPoint) {
Class<? extends Object> clazz = thisJoinPoint.getTarget().getClass();
StringBuilder messageBuf = new StringBuilder();
if( ThreadLocalUtil.isExist("userInfo")){
UserInfo userInfo = (UserInfo)ThreadLocalUtil.get("userInfo");
messageBuf
.append("\n-------------------------------------------------------------------------------\n");
messageBuf.append(" This method called by " + userInfo.getUserName() +"\n");
messageBuf
.append("-------------------------------------------------------------------------------");
}
Log logger = LogFactory.getLog(clazz);
if (logger.isInfoEnabled()) {
logger.info(messageBuf.toString());
}
}
}Biz. Service의 메소드가 호출되면 어떤 User가 메소드를 실행 했는지 로그를 남긴다.
Spring Message 테그는 code(message key)값을 이용해 MessageSource에 있는 해당 문자를 사용자에게 보여준다. 이 때 LocaleResolver를 이용해 다국어 처리도 가능하다. ChangeLocaleController는 사용자가 다른 언어로 보고 싶을 때 SessionLocaleResolver의 언어를 바꿈으로써 다국어 처리를 지원한다.

위 화면에서 보고 싶은 언어를 클릭하면 ChangeLocaleController의 changeLoacle메소드가 호출되어 LocaleResolver의 로케일을 변경한다.
아래는 ChangeLocaleController의 소스코드이다.
@Controller("loginChangeLocaleController")
public class ChangeLocaleController {
@Inject
private LocaleResolver localeResolver;
@RequestMapping("/loginChangeLocale.do")
public String changeLoacle(@RequestParam(value = "locale", defaultValue = "en") String newLocale,
Model model,
HttpSession session, HttpServletRequest request, HttpServletResponse response) throws Exception {
LocaleEditor localeEditor = new LocaleEditor();
localeEditor.setAsText(newLocale);
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
return "forward:/loginMovieFinder.do?method=list";
}
}LocaleResolver의 값이 변경되면 화면에서 다음과 같이 다른 언어로 변경된 것을 확인 할 수 있다.
