W drugiej części zajmiemy się obsługą uprawnień użyszkodnika.

Jak sprawdzać uprawnienia?

Odpowiedzi na to pytanie jest prawdopodobnie tyle ilu jest programistów +1. Generalnie większość będzie jednak zgodna, że jedną z najwygodniejszych dróg jest użycie programowania aspektowego(najlepiej z dynamicznym weaveringiem) oraz nieinwazyjnych technik takich jak XML. Jako, że konfiguracja w XML działa na mnie jak czerwona płachta na byka zastosuję zatem adnotacje. Pozwoli nam to też na znaczne uproszczenie przykładów.

Adnotowanie zabezpieczanych metod

By zaadnotować metodę, którą chcemy zabezpieczyć przydała by się adnotacja:

Listing 1. Adnotacja SecureAction

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SecureAction {

	String principal() default "" ;
	
	String role() default "" ;
}

W tej wersji adnotacja pozwala na mapowanie pomiędzy akcjami, a uprawnieniami jak wiele do jednego. Można oczywiście rozszerzyć ten problem o mapowanie wielu uprawnień lub ról do jednej akcji wymaga to jednak odpowiedniego przygotowania na poziomie architektury m.n. określenia czy wymagane jest jedno z wymienionych uprawnień czy wymagane są wszystkie. Użycie adnotacji jest oczywiste.

Sprawdzanie uprawnień

Do sprawdzenia czy dany użytkownik posiada dane uprawnienia posłuży nam interceptor.

Listing 1. Adnotacja SecurityInterceptor

@SessionScoped
public class SecurityInterceptor implements MethodInterceptor {

	@Inject
	private Provider<subject> subjectProvider;

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		SecureAction annotation = invocation.getMethod().getAnnotation(
				SecureAction.class);
		
		Subject subject = subjectProvider.get();
		String principal = annotation.principal();
		if (!"".equals(principal)) {
			boolean hasPrincipal = subject.isPermitted(principal);
			if(!hasPrincipal)
				throw new AuthorizationException();
		}

		String role = annotation.role();
		if (!"".equals(role)) {
			boolean hasRole = subject.hasRole(role);
			if (!hasRole)
				throw new AuthorizationException();
		}
		return invocation.proceed();
	}

}</subject>

I znowuż przedstawiam tu uproszczoną implementację. Można ją twórczo rozwinąć. Ważnym elementem jest jednak wstrzyknięcie providera, a nie bezpośrednio obiektu klasy Subject. Provider będzie dostarczał obiektu związanego z aktualnym kontekstem co w przypadku aplikacji web ma duże znaczenie 😉
Należy pamiętać, że Guice implementuje AOP w wersji AOP Alliance dzięki czemu nasz kod jest w miarę przenośny.

Podsumowanie

Integracja Vaadin z Shiro za pomocą Guice jest stosunkowo prosta. Należy jednak podjąć pewne decyzje już we wczesnej fazie projektu ponieważ późniejsza zmiana koncepcji zabezpieczenia aplikacji może spowodować dużo problemów.