Spring Acegi Security (bonus - lokalizace chybových hlášení)

V poslední době jsem řešil problematiku zabezpečení webových projektů spolu se správou uživatelů a oprávnění. Původně se zdálo, že neexistuje open-source projekt, který by pokrýval naše potřeby. Po čase jsem však narazil na Acegi Security projekt, který je určený přímo pro Spring. Ještě před třemi dny jsem měl úplně jiné představy o tom, co Acegi řeší - myslel jsem, že to je jen sada providerů pro napojení na externí zdroje dat o uživatelích (LDAP, Active Directory a pod.). To jsem se ovšem hluboce mýlil.

Acegi pokrývá v podstatě veškeré aspekty, které je nutné řešit v souvislosti se zabezpečením webových aplikací a jde ještě mnohem dál (není fixně vázaný jen na web oriented aplikace a neřeší pouze přihlašování, ale umí i vyhodnocovat oprávnění). Nenarazil jsem na mnoho míst, které by bylo potřeba nějak rozšiřovat - v podstatě většina toho, co člověk potřebuje tam už je k dispozici. Jedinou hlavní částí, kterou je potřeba si obvykle implementovat je DAO nad databází uživatelů a oprávnění (typicky potřebujete CRUD operace nad databází uživatelů, které Acegi neimplementuje - logicky toto jde mimo její zájmy a také typicky potřebujete k uživatelům rozdílné údaje a někdy i vlastní workflow).

Navíc mě opravdu překvapila "návrhová čistota" kódu, ze kterého se Acegi skládá. Brouzdáním po třídách a porozumění jejich kódu jsem se opravdu mnoho naučil. Doporučuji všem, kdo řeší podobné problémy jako já, kouknout se právě na tuto knihovnu.

Slíbený bonus článku:

Lokalizace chybových hlášení Acegi je ke stažení zde.

Do aplikace se jednoduše lokalizace zavede vložením následující deklarace do applicationContext.xml (nebo do jiného Spring konfiguráku).


   <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:cesta/na/classpath/k/souboru"/>
   </bean>

Ještě je dobré dát si pozor, aby bylo v LocaleContextHolder, nastaveno správné locale. Tato třída ukládá informace o aktuálním locale v ThreadLocal proměnné a je tedy platná pouze pro aktuální vlákno (a také je vhodné ji před odevzdáním vlákna do poolu čistit). Pokud nepoužíváte zároveň Spring MVC, musíte si toto implementovat sami.

Já jsem vyřešil nastavování locale následujícím filtrem (který jsem přidal do řetězce filtrů v FilterChainProxy:


/**
 * Sets locale according to locale supplied in request.
 */
public class RequestLocaleFilter extends OncePerRequestFilter implements Filter {
	/**
	 * Same contract as for doFilter, but guaranteed to be just invoked once per
	 * request. Provides HttpServletRequest and HttpServletResponse arguments
	 * instead of the default ServletRequest and ServletResponse ones.
	 */
	protected void doFilterInternal(
                HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
		try {
			LocaleContextHolder.setLocale(request.getLocale());
			doFilter(request, response, filterChain);
		} finally {
			LocaleContextHolder.resetLocaleContext();
		}
	}
}