piątek, 26 listopada 2010

News dla programistów Oracle ADF

Stało się. Oracle oficjalnie uruchomiło ścieżkę certyfikacji dla programistów ADF. Wiadomość opublikował wczoraj na swoim blogu Shay Shmeltzer. Egzamin oficjalnie nosi nazwę Oracle Application Development Framework Essentials (1Z1-554) i jak na razie jest w wersji beta – czyli ma służyć głównie wysądowaniu zainteresowania ścieżką certyfikacji z zakresu technologii Oracle Fusion oraz ustaleniu ostatecznego zakresu tematycznego. Dla bywalców forum dyskusyjnego OTN nie jest to zaskoczenie. Temat certyfikatów z ADF jest cyklicznie poruszany od co najmniej trzech lat. Na temat problematyki egzaminu można poczytać tutaj. Moje osobiste wrażenia są takie – sporo tego – nawet dla doświadczonego developera. Wrzucono do jednego worka w zasadzie całość technologii, lekko nawet poza nią wykraczając (ADF + SOA, WebCenter). Zresztą sam Shay napisał:
„...this is not a trivial exam - you should only apply if you actually have practical experience developing with ADF”.
Nie wiem też, jaka będzie forma tego egzaminu. Część pytań jest czysto teoretyczna, niektóre zagadnienia natomiast w zasadzie nie do opisania bez użycia JDeveloper'a. Cóż, wysłałem maila do autora postu i być może wkrótce będę wiedział coś więcej. Postaram się też skonfrontować zakres tematyczny egzaminu z tym, co napisane jest w dostępnej literaturze przedmiotu.

czwartek, 25 listopada 2010

Kolejny OTN Developer Day za mną

To był bez wątpienia jeden z bardziej produktywnie spędzonych ostatnio dni:). Naprawdę było warto. Tym razem cykliczna impreza organizowana przez Oracle poświęcona była m.in. cechom serwera GlassFish i planom jego rozwoju w przyszłości. Większa część zajęć, skupiona była jednak na nowościach w Javie EE 6, czyli EJB 3.1, JAX-RS, JSF 2.0, CDI, Servlet 3.0 i Java Persistence API 2.0. Wszystko oczywiście w wykorzystaniem powyższego serwera i NetBeans'a. Jedyny minus to ograniczony czas na zajęcia praktyczne, co zaskutkowało tym, że chyba nikt nie przerobił wszystkich ćwiczeń. Na szczęście każdy uczestnik dostał komplet zadań na płytce. Obiecałem sobie przerobić je wszystkie i oczywiście w miarę możliwości podzielić się tutaj wrażeniami.

P.S. Dzieląc się miedzy sobą opiniami na temat szóstki zgodnie stwierdziliśmy, że nie da się pisać zapytań przy pomocy Criteria API :).

sobota, 23 października 2010

Java Authentication and Authorization Service (JAAS) od podstaw, czyli jak stworzyć własny moduł logowania

Dzisiejszy wpis poświęcony będzie podstawom implementacji szkieletu JAAS odpowiedzialnego w Javie za autentykację do programu i autoryzację dostępu. Ideą JAAS jest swobodne łączenie modułów odpowiedzialnych za bezpieczeństwo - PAM (Pluggable Authentication Modules). Poniższy przykład zaczniemy od utworzenia klasy CustomPrincipal przechowującej nazwę naszego podmiotu (Subject) zgłaszającego prośbę o uwierzytelnienie - dla uproszczenia nazywajmy go dalej użytkownikiem. Musi ona implementować interfejsy: java.security.Principal i java.io.Serializable oraz zawierać metody toString() i equals(). Subject może przechowywać wiele obiektów Principal takich jak np: grupy, jednak na potrzeby niniejszego przykładu wykorzystamy tylko jeden.

package kuba.demo.jaas.module;

import java.io.Serializable;
import java.security.Principal;
import java.util.logging.Logger;

public class CustomPrincipal implements Principal, Serializable {
 
 private static Logger log =Logger.getLogger(CustomPrincipal.class.getName());
 private static final long serialVersionUID = 1L;
 
 private String name;

 
    public CustomPrincipal(String name) {
     
     if (name == null){
      log.severe("Brak principal name");
         throw new NullPointerException("Principal name is null");
     }
     this.name = name;
   }


    @Override
    public String getName() {
 return name;
     }

 
    public String toString() {
     return("CustomPrincipal-" + name);
    }


    public boolean equals(Object o) {
     if (o == null){
         return false;
     }
        if (this == o){
               return true;
        }
        
        if (!(o instanceof CustomPrincipal)){
                return false;
        }
        
        CustomPrincipal that = (CustomPrincipal)o;

     if (this.getName().equals(that.getName())){
         return true;
     }
    
     return false;
    }
     
    
    public int hashCode() {
     return name.hashCode();
    }
}


Kolejną wymaganą klasą jest CustomCallbackHandler - implementuje interfejs javax.security.auth.callback.CallbackHandler. Można powiedzieć, że jest ona pośrednikiem pomiędzy naszą aplikacją, a usługą logowania, przekazując jej nasze dane uwierzytelniające (credentials).

package kuba.demo.jaas.module;

import java.io.IOException;
import java.util.logging.Logger;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

public class CustomCallbackHandler implements CallbackHandler {
 
 private static Logger log =Logger.getLogger(CustomCallbackHandler.class.getName());
 
 private String user;
 private String password;

 public CustomCallbackHandler(String user, String password) {
  this.user = user;
  this.password = password;
 }

 @Override
 public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {
  
  NameCallback nameCallback  = (NameCallback)callbacks[0];
  PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
  nameCallback.setName(user);
  passwordCallback.setPassword(password.toCharArray());
  log.info("Inicjalizacja callback handler'a dla uzytkownika: "+user);
 }
}

Pozostała jeszcze klasa odpowiadająca za uwierzytelnienie użytkownika czyli właściwy moduł logowania. Moduł taki - CutomLoginModule.java - musi implementować interfejs javax.security.auth.spi.LoginModule. Cała logika odpowiedzialna za uwierzytelnienie odbywa się w metodzie login(). Możemy w niej sięgnąć do bazy danych lub serwera usług katalogowych. Jeśli autentykacja nie powiedzie się, zwrócony zostanie wyjątek javax.security.auth.login.LoginException. Jeśli logowanie będzie poprawne wywoływana jest metoda commit(). Przesłaniając ją możemy przypisujemy naszemu uwierzytelnionemu obiektowi (Subject) odpowiednie właściwości. W przypadku gdy któraś z powyższych metod zwróci błąd, wywoływana jest metoda abort().

package kuba.demo.jaas.module;

import java.security.Principal;
import java.util.Map;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class CustomLoginModule implements LoginModule{
 
 private static Logger log =Logger.getLogger(CustomLoginModule.class.getName());
 
 private Subject subject;
 private CallbackHandler callbackHandler;
 private Map sharedState;
 private Map options;
 private String userName;
 private String userPassword;
 
 
 @Override
 public void initialize(Subject subject, CallbackHandler callbackHandler,
         Map sharedState, Map options) {
  
  this.subject = subject;
  this.callbackHandler = callbackHandler;
  this.sharedState = sharedState;
  this.options = options;
 }

 
 @Override
 public boolean login() throws LoginException {
  
  NameCallback nameCallback = new NameCallback("Username");
  PasswordCallback passwordCallback = new PasswordCallback("Password",false);
  
  Callback[] callbacks = new Callback[]{ nameCallback, passwordCallback };
  
  try {
    callbackHandler.handle(callbacks);
    
  } catch (Exception e) { 
   e.printStackTrace();
  }
  
  userName = nameCallback.getName();
  userPassword = String.valueOf(passwordCallback.getPassword());
  
  if("jakub.pawlowski".equals(userName) && "my_password".equals(userPassword)){
   
   log.info("Poprawnie zalogowano uzytkownika [ "+userName+" ]");
   return true;
   
  }else{
   
   log.info("Logowanie nie powiodlo sie");
   return false;
  }
 }

 
 @Override
 public boolean commit() throws LoginException {
  
  Principal principal = new CustomPrincipal(userName);
  subject.getPrincipals().add(principal);
  userPassword = null;
  log.info("Dodano obiekt CustomPrincipal");
  
  return true;
 }

 
 @Override
 public boolean abort() throws LoginException {
  
  log.info("Wywolanie abort()");
  userName = null;
  userPassword = null;
  return true;
 }

 
 @Override
 public boolean logout() throws LoginException {
  
  log.info("Uzytkownik [ "+userName+" ] zostal wylogowany");
  userName = null;
  userPassword = null;
  return true;
 }

}

Ostatnim krokiem jest utworzenie pliku konfiguracyjnego wskazującego na mój moduł logowania. Ja nazwałem go custom_jaas.config

MyLoginModule
{
 kuba.demo.jaas.module.CustomLoginModule REQUIRED;
};

Czas na sprawdzenie jak to wszystko działa. Poniżej prosty klient, należy go uruchomić z opcją wskazującą na plik konfiguracyjny: -Djava.security.auth.login.config=="e:/custom_jaas.config"

package kuba.demo.jaas.client;

import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import kuba.demo.jaas.module.CustomCallbackHandler;
import kuba.demo.jaas.module.CustomPrincipal;

public class CustomLoginModuleClient {

 private static Logger log =Logger.getLogger(CustomLoginModuleClient.class.getName());
 
 public static void main(String[] args) {
  
  CustomCallbackHandler callbackHandler = new CustomCallbackHandler("jakub.pawlowski", "my_password");
  
  try {
   LoginContext loginContext = new LoginContext("MyLoginModule",callbackHandler);
   loginContext.login();
   
   Subject s = loginContext.getSubject();
   Set ps = s.getPrincipals();
   Iterator iter = ps.iterator();
    
    while(iter.hasNext()){
     
     Principal principal = (Principal)iter.next();
     
     if(principal instanceof CustomPrincipal){
      
      log.info("Principal [ "+principal.getName()+" ]");
     }
    }
    
    loginContext.logout();
   
  } catch (LoginException e) {
   e.printStackTrace();
  }

 }
}


Jak widać poniżej autoryzacja przebiegła poprawnie

INFO: Poprawnie zalogowano uzytkownika [ jakub.pawlowski ]
2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule commit
INFO: Dodano obiekt CustomPrincipal
2010-10-23 19:28:09 kuba.demo.jaas.client.CustomLoginModuleClient main
INFO: Principal [ jakub.pawlowski ]
2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule logout
INFO: Uzytkownik [ jakub.pawlowski ] zostal wylogowany