Java

어플리케이션 서버를 사용한 인증

Tomcat과 같은 어플리케이션 서버를 사용한 인증 방법을 간단히 정리하면 다음과 같다.

1. 어플리케이션 서버

Tomcat에서는 server.xml 파일에 사용자 지정 Realm 설정을 한다.

예제에서는 member 테이블의 email 컬럼을 사용자명으로, MD5로 암호화된 passwd 컬럼을 암호로 사용하고,

역할은 role 컬럼을 사용하도록 했다.

<Realm className="org.apache.catalina.realm.JDBCRealm"
 driverName="com.mysql.jdbc.Driver"
 connectionURL="jdbc:mysql://localhost:3306/sample?useUnicode=true"
 connectionName="user1" connectionPassword="password"
 userTable="member" userNameCol="email" userCredCol="passwd" digest="MD5"
 userRoleTable="member" roleNameCol="role"
/>

2. 웹 어플리케이션

해당 웹 어플리케이션의 web.xml 파일을 설정한다.
여기에서는 예로 /protected 란 디렉토리의 모든 파일을 요청할 때 인증이 필요하며,
인증된 계정은 Admin이나 SuperAdmin의 Role을 가지고 있어야 한다고 설정했다.

<security-constraint>
 <display-name>보안 설정</display-name>
 <web-resource-collection>
 <web-resource-name>SiteManager</web-resource-name>
  <url-pattern>/protected/*</url-pattern>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
 </web-resource-collection>
 <auth-constraint>
  <description>사이트 관리자</description>
  <role-name>Admin</role-name>
  <role-name>SuperAdmin</role-name>
 </auth-constraint>
</security-constraint>

<login-config>
 <auth-method>FORM</auth-method>
 <realm-name>Site Managers</realm-name>
 <form-login-config>
  <form-login-page>/login.jsp</form-login-page>
  <form-error-page>/login_error.jsp</form-error-page>
 </form-login-config>
</login-config>

<security-role>
 <role-name>Admin</role-name>
</security-role>
<security-role>
 <role-name>SuperAdmin</role-name>
</security-role>

로그인 폼인 login.jsp 파일은 다음과 같이 간단하며 로그인이 성공하면 요청한 페이지로 자동으로 이동된다.

<form method="POST" action="j_security_check">
  <input type="text" name="j_username">
  <input type="password" name="j_password">
  <input type="submit" value="로그인">
</form>

로그인이 되면 다음과 같이 로그인 된 회원 정보를 확인할 수 있다.

String remoteUser = request.getRemoteUser();
java.security.Principal principal = request.getPrincipal();
String username = principal.getName();
boolean isAdmin = request.isUserInRole("Admin");
boolean isSuperAdmin = request.isUserInRole("SuperAdmin");

3. 참고

Tomcat 오류 중 Error reading tld listeners java.lang.NullPointerException

Flex2를 Spring Framework와 테스트하다가 다음과 같은 오류가 Tomcat에서 발생하였다.

정보: Reloading this Context has started
2007. 12. 29 오후 3:08:18 org.apache.catalina.core.StandardContext processTlds
심각: Error reading tld listeners java.lang.NullPointerException
java.lang.NullPointerException
at org.apache.log4j.Category.isEnabledFor(Category.java:757)
at org.apache.commons.logging.impl.Log4JLogger.isTraceEnabled(Log4JLogger.java:327)
at org.apache.catalina.startup.TldConfig.tldScanResourcePaths(TldConfig.java:581)
at org.apache.catalina.startup.TldConfig.execute(TldConfig.java:282)
at org.apache.catalina.core.StandardContext.processTlds(StandardContext.java:4307)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4144)
at org.apache.catalina.core.StandardContext.reload(StandardContext.java:3025)
at org.apache.catalina.loader.WebappLoader.backgroundProcess(WebappLoader.java:432)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1278)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1570)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1579)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1579)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1559)
at java.lang.Thread.run(Thread.java:595)

이 것은 Log4J 1.2.8과 Commons Logging 1.1을 사용할 때 발생하는 문제이다.
Flex2 LCDS에 포함된 Commons Logging의 버전은 1.0.4 였는데 다른 라이브러리를 복사해넣다가 Commons Logging 라이브러리가 덮어쓰기 되어버렸다.
본래대로 1.0.4로 라이브러리를 변경하니 오류가 나타나지 않았다.

Flash Bitmap 이미지를 PNG로 저장하기

Flash에서 Bitmap 이미지 – 사용자와 상호작용을 통해 만들어진 이미지 -를 생성한 후 이것을 JPEG 또는 PNG로 저장하여 보관해야할 경우가 있다.

물론 ActionScript 3.0이 지원되는 Flash CS로 모두 개발하면 쉽게 변환이 가능하지만, 아직까지 ActionScript 2.0을 사용하는 경우가 많다.

여기에서는 Flash 8과 Java 1.4.x 를 사용하여 Flash Bitmap 이미지를 저장하는 방법에 대하여 알아본다.

단계로 보면 다음과 같다.
1. Flash: Flash Bitmap을 Windows BMP로 변환한다.
2. Flash: 변환된 Windows BMP를 서버로 데이터를 전송한다.
3. Java: 수신한 데이터를 BMP파일로 저장한 후 PNG 파일로 변환한다.
4. Java: 변환된 PNG 파일경로를 Flash로 리턴한다.
5. Flash: 서버에서 전달된 PNG 파일 경로를 사용하여 이미지를 출력한다.

Flash ActionScript

Flash Bitmap 이미지를 Windows BMP(확장자 .bmp) 파일로 변환하는 것은 BitmapDataExporter라는 ActionScript 2.0용 클래스를 사용하여 가능하다.
이것은 아래 자료에서 볼 수 있다.

sample.as

import flash.display.BitmapData;
import flash.net.FileReference;
import net.webbymx.files.binary.BitmapDataExporter;

var bmp:BitmapData = null;
var bmpExport:BitmapDataExporter = null;
var bmpData:String = null;
var resultFile:TextField;

function exportBitmap(Void):Void {
  bmpData = new String();
  bmpExport = new BitmapDataExporter(bmp);
  bmpExport.export();
}

bmpExport.onProgress = function(b:String, progress:Number):Void {
  bmpData +=b;
}

bmpExport.onExportDone = function(Void):Void {
  sendDataToServer(bmpData);
}

function sendDataToServer(s:String):Void {
  var rlv:LoadVars = new LoadVars();
  rlv.onLoad = function(success:Boolean): Void {
    if(success) {
       resultFile.text = rlv.fileName;
    } else {
       resultFile.text = "Error while connecting to server...";
    }
  };
  var lv:LoadVars = new LoadVars();
  lv.bitmap = s;
  lv.sendAndLoad("http://localhost/upload.do", rlv, "POST");
}

Java Class

Java 1.4.x에서 Windows BMP파일을 저장하는 방법은 여러가지가 있을 수 있다.
여기에서는 먼저 JAI Tool을 사용하여 BMP를 읽을 수 있는 지 확인한 후, 안된다면 Image4J를 사용하도록 하였다.

JAI Image I/O Tool을 사용하면 성능이 더 좋겠지만 프로젝트를 하다보면 JDK 설치 권한이 없거나, 여러 프로젝트를 운영하는 운용업체에서 곤란하다고 하는 경우가 종종있다.

어플리케이션 배포 당시에는 JAI Image I/O Tool이 설치되지 않았을 경우 Image4J를 사용하여 동작하고 운영중에 JAI Image I/O Tool이 설치되었다면 성능을 위해서라도 JAI를 사용하도록 하기 위함이다.

import java.io.FileOutputStream;
import java.io.File;
import java.io.BufferedInputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import net.sf.image4j.codec.bmp.BMPDecoder;

private boolean savePngFile(String filePath, String bitmap) {
  try {
    File file = new File(filePath);
    FileOutputStream ostream = new FileOutputStream(file);
    byte[] b = hex2byte(bitmap);
    if(b == null) {
      return false;
    }
    ostream.write(b);
    ostream.close();

    BufferedImage bImage = null;
    Iterator iter =
      ImageIO.getImageReadersByMIMEType("image/bmp");
    if(iter.hasNext()) {
      // using JAI
      bImage = ImageIO.read(file);
    } else {
      // using Image4J
      bImage = BMPDecoder.read(file);
    }
    if(bImage == null) {
      return false;
    }
    ImageIO.write(bImage, "png", file);
  } catch (Exception ex) {
    ex.printStackTrace();
    return false;
  }
  return true;
}

private static byte[] hex2byte(String s){
  if(s == null) return null;
  int l = s.length();
  if(l%2 == 1) return null;
  byte[] b = new byte[l/2];
  for(int i = 0 ; i < l/2 ;i++) {
    b[i] = (byte)Integer.parseInt(s.substring(i*2,i*2+2),16);
  }
  return b;
}

자료