Authentication and Authorization

Anyframe Web에서의 인증과 접근 권한 제어에 대해 알아본다.

request를 보낸 클라이언트가 어플리케이션에 등록된 User인지 체크하여 로그인하게 해주는 Authentication과 User 와 어플리케이션 내의 자원 간의 관계를 맺고 관리하는 Authorization 은 어플리케이션 개발 시 항상 고려되는 부분 중의 하나이며 두 부분이 밀접히 관련되어 있다.
인증과 권한 기능을 Application 에 추가하려면 몇 가지 고려해야 할 것이 있다. 먼저 사용자 정보는 어떤 방법(DB, LDAP, FILE, NT … )을 이용해 관리 할 것이며 인증된 사용자 정보를 어떻게 (Session, Cookie… ) 저장할 것인지, 그리고 사용자에게 권한을 부여하는 방법과 접근을 통제할 자원은 어떤 것인지 정의해야 한다. JAAS 기반의 인증 방식을 적용하여 Login Context를 추상화함으로써 Login Module 개발 시 어플리케이션 코드의 수정 없이 타 시스템을 통한 인증을 수행할 수 있도록 처리하는 것이 가장 바람직하며 프로젝트별 인증 처리의 요건(ex.. 외부 인증 solution의 적용)에 맞게 작성해야 한다.
Authentication/Authorization 기능은 모든 어플리케이션에서 그대로 재사용할 수 있는 것은 아니다. 인증과 권한이란 부분은 각 어플리케이션마다 정책이 다를 수 있으므로 커스터마이징이 필요하다. 여기에서는 인증과 권한관리 부분에 관련된 Anyframe Web 기능을 설명하고 프로젝트별로 고유한 Security 서비스를 만들어 인증과 권한을 관리하는 예에 대해 알아 본다.

Authentication

여기서는 일반적으로 많이 쓰이는 인증 방법인 사용자정보를 DB에 저장하고 USER ID 와 PASSWORD로 체크하는 예에 대해 알아본다.
Anyframe Web의 인증
  • Anyframe 서비스(프로젝트별로 고유한 Security 서비스)의 구현(DB기반)
  • 인증된 유저 정보는 javax.security.auth.Subject 객체 형태로 session에 저장

Authentication 적용
  • LogInAction 에 관련 코드를 작성
  • Security Service 호출 - User의 ID, Password 로 유효성 체크
  • javax.security.auth.Subject 객체를 'subject' 라는 key값으로 Session 에 저장
  • Subject 객체 내에 TypedPrincipal 인스턴스 설정
    USER, GROUP, DOMAIN Type 정의. 해당 type 정보를 이용해서 Authentication 수행

Samples

인증과 관련된 비지니스 레이어의 서비스 작성과 LogInAction 처리의 예이다.
Security Service의 작성 예
DB 기반의 authenticate 처리 예는 다음과 같다.
package com.sds.emp.security.services.impl;
...
public class DBAuthenticationService implements AuthenticationService,
		ApplicationContextAware {
...
	public Subject authenticate(Credential credential) throws EmpException {

		..
		String userid = credential.getProperty("userid");
		String password = credential.getProperty("password").trim();

		try {
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sqlQuery);
			..

			pstmt.setString(1, userid);
			pstmt.setString(2, password);
			rsu = pstmt.executeQuery();

			if (rsu.next()) {
				userid = rsu.getString(1);
				String username = rsu.getString(2);
				password = rsu.getString(3);
				String grade = rsu.getString(4);

				Set principals = new HashSet();
				Set credentials = new HashSet();

				principals.add(new TypedPrincipal(username, TypedPrincipal.USER));

				StringTokenizer tokens = new StringTokenizer(grade, ",");
				while (tokens.hasMoreTokens()) {
					principals.add(new TypedPrincipal(tokens.nextToken(),
							TypedPrincipal.GROUP));
				}

				subject = new Subject(false, principals, credentials,
						credentials);
			}
			else {
				throw new EmpException(messageSource, "error.security.login");
			}

		}
		catch (Exception e) {
			if (e instanceof EmpException) throw (EmpException) e;
			else throw new EmpException(messageSource,
					"error.security.check.userid", e);
		}
		finally {
			..
		}
		return subject;
	}
}
Security Service 서비스 설정파일(applicationContext-security.xml)은 다음과 같이 되어 있어야 한다.
Security Service 서비스 설정파일
// 중략
<bean id="securityService"
 class="com.sds.emp.security.services.impl.DBAuthenticationService">
        <property name="dataSource" ref="dataSource"/>
        <property name="sqlQuery" 
	value="SELECT u.USER_ID,u.USER_NAME,u.PASSWORD,u.ENABLED,a.AUTHORITY FROM USERS u, AUTHORITIES a WHERE u.USER_ID=? and u.PASSWORD=? and a.USER_ID = u.USER_ID"/>
</bean>
// 중략

LogInAction 의 작성 예
다음은 LogInAction 작성의 예이다.
import com.sds.emp.security.services.AuthenticationService;
...
public class LogInAction extends DefaultActionSupport {

 public ActionForward process(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {

  try {
    Subject subject = new Subject();
    HttpSession session = request.getSession();

    String userId = (String) PropertyUtils.getSimpleProperty(form, "userId");
    String password = (String) PropertyUtils.getSimpleProperty(form, "password");

    ApplicationContext ctx = getWebApplicationContext();
    AuthenticationService authenticationService 
                               = (AuthenticationService) ctx.getBean("securityService");

    Credential c = new Credential();
    c.setProperty("userid", userId);
    c.setProperty("password", password);

    subject = authenticationService.authenticate(c);session.setAttribute("subject", subject);
    session.setAttribute("userId", userId);

	return mapping.findForward("success");

  } catch (Exception e) {
  ...
 }
}
LogInAction 에서는 각 프로젝트의 기준에 따라 실제 인증을 처리하는 Authentication Service를 호출하여 인증이 성공한 경우 돌려받은 해당 사용자의 인증 정보(Subject 객체)를 session 에 "subject"라는 key 값으로 저장하는 기능을 수행한다.
이 Subject 객체는 authorization 체크에 다시 사용된다.

Authorization

Anyframe Web의 접근권한 제어
  • Action 단위로 Role 정의, 접근 제한 처리 (Role Based Access Control)

Authorization 적용
  • 사용자 식별정보 및 해당 사용자에 부여된 authorities 예

  • 위 사용자 정보의 가정에 의해서 TypedPrincipal.USER 가 tester 이고 TypedPrincipal.GROUP 이 admin 이라는 TypedPrincipal 을 가진 Subject 를 참조하게 됨.
  • struts-config.xml 의 action element에 roles attribute정의
  • Anyframe Web에서 제공하는 DefaultRequestProcessor의 processRoles 에서 session에 있는 Subject 객체의 값과 roles 정보 비교 후 Action 수행 여부 결정


Samples

다음은 접근 권한 제어와 관련된 struts-config.xml의 action element 작성 예이다.
<!--  회원 목록 조회 -->
<action
    input="/home.do"
    name="userForm"
    path="/empListUser"
    type="com.sds.emp.user.web.GetUserListAction"
    scope="request"
    validate="false"
    roles="admin,user">
    <forward name="success" path="/sample/user/listUser.jsp"  />
</action>
위의 예에서 tester는 admin 이란 GROUP 에 속해 있으므로 admin 이란 roles 가 설정되어 있는 Action 들을 수행할 수 있다. roles 가 설정이 되어 있지 않은 Action은 모두 수행한다.

Exception 처리

인증이나 권한을 체크하는 동안 Exception 이 발생했을 때의 처리에 대해 알아본다. 위의 예에서 최초 로그인 시에 userId, password 를 넘겨 비지니스 메소드에서 Authentication 의 처리 시에도 일반적인 Business Exception 과 동일하게 Exception을 throw 하고 있다.
DefaultRequestProcessor에서는 매 request 마다 session에 저장된 Subject 객체를 체크하여 Subject 객체가 null이면 anyframe.web.struts.common.util.AuthenticationException 을 throw 하고 Subject 객체의 정보와 roles 정보를 비교하여 권한이 없으면 anyframe.web.struts.common.util.AuthorizationException 을 throw 하게 된다.
Anyframe Web Framework 에서는 선언적인 방법으로 Exception Handling 을 처리하고 있으므로 struts-config.xml에 Exception 처리에 관련된 설정이 필요하다.

Samples

다음은 Authentication, Authorization Exception 처리와 관련된 struts-config.xml의 global-exceptions element 작성 예이다.
<global-exceptions>
<exception 
    path="/sample/common/error.jsp" 
    key="error.common.msg.authentication" 
    type="anyframe.web.struts.common.util.AuthenticationException"
    handler="com.sds.emp.common.EmpExceptionHandler" />
<exception 
    path="/sample/common/error.jsp" 
    key="error.common.msg.authorization" 
    type="anyframe.web.struts.common.util.AuthorizationException"
    handler="com.sds.emp.common.EmpExceptionHandler" />
...
</global-exceptions>
위의 설정은 AuthenticationException , AuthorizationException 이 발생하면 error.common.msg.authentication 과 error.common.msg.authorization 이란 key값으로 Message Resource에 저장된 내용을 error.jsp 로 forward 한다는 내용이다.
Exception Handling에 대한 자세한 부분은 Web Framework > Extensions > ExceptionHandler 을 참조한다.

Resources