Ciąg dalszy pierwszej części artykułu o JAAS. Zastanówmy się, czy w JAAS można używać samych adnotacji i jakie problemy napotkamy. W tej części dowiemy się, które narzędzia Servlet API pozwalają nam uczynić kod bardziej uniwersalnym. I podsumujmy to, co przeczytaliśmy.
Kontynuacja
W pierwszej części przeglądu technologii JAAS (patrz „ JAAS – Wprowadzenie do technologii (część 1) ”) przyjrzeliśmy się głównemu przypadkowi użycia JAAS i Servlet API. Widzieliśmy, że kontener serwletów Tomcat zarządzał bezpieczeństwem naszej aplikacji internetowej przy użyciu architektury JAAS. Mając wiedzę na temat „metody autoryzacji” i „Security Realm”, kontener Tomcat sam zapewnił nam niezbędną implementację mechanizmu uwierzytelniania i sam zapewnił nam CallbackHandler, wykorzystaliśmy to wszystko w naszym module logowania. Należy jedynie pamiętać, że przeglądarka zapisuje dane dotyczące loginu i hasła przesyłane w ramach uwierzytelniania BASIC. Dlatego przy każdym nowym skanowaniu za pomocą przeglądarki Chrome możesz nacisnąć Ctrl+Shift+N, aby otworzyć nowe okno do pracy w trybie incognito.Adnotacje
Konfiguracja za pomocą XML już dawno wyszła z mody. Dlatego ważne jest, aby powiedzieć, że począwszy od wersji Servlet API 3.0, mamy możliwość ustawiania ustawień serwletu za pomocą adnotacji, bez korzystania z pliku deskryptora wdrażania web.xml. Zobaczmy jak zmieni się zarządzanie bezpieczeństwem, jeśli zastosujemy adnotacje. I czy możliwe jest wdrożenie opisanych powyżej podejść za pomocą adnotacji? Specyfikacja Servlet API i jej sekcja „ Adnotacje i możliwość podłączania ” ponownie pomogą nam zrozumieć adnotacje . Ta sekcja mówi, że deklarację serwletuweb.xml
można zastąpić adnotacją @WebServlet
. W związku z tym nasz serwlet będzie wyglądał następująco:
@WebServlet(name="app", urlPatterns = "/secret")
public class App extends HttpServlet {
Następnie spójrzmy na rozdział „ 13.3 Bezpieczeństwo programowe ”. Mówi, że możemy również zadeklarować ograniczenie bezpieczeństwa poprzez adnotacje:
@WebServlet(name="app", urlPatterns = "/secret")
@ServletSecurity(httpMethodConstraints = {
@HttpMethodConstraint(value = "GET", rolesAllowed = "admin")
})
public class App extends HttpServlet {
Teraz web.xml
w naszym pozostał już tylko jeden blok - login-config
. Problem w tym, że tak się składa, że łatwo i prosto go nie da się wymienić. Ze względu na ścisły związek pomiędzy ustawieniami zabezpieczeń aplikacji internetowych i ustawieniami zabezpieczeń serwera WWW, nie ma prostego i uniwersalnego sposobu, aby to zrobić, nawet programowo. Jest to jeden z problemów związanych z uwierzytelnianiem za pomocą JAAS i Servlet API. Mówiąc o bloku login-config
warto zrozumieć, że jest to deklaratywny opis Mechanizmów Uwierzytelniających, tj. mechanizmy uwierzytelniania. Nadal nie ma prostego, uniwersalnego sposobu na jego zastąpienie, bo... przetwarzanie web.xml
odbywa się głęboko w kontenerach serwletów. Na przykład w Tomcat możesz sprawdzić plik źródłowy ContextConfig.java . Dlatego nawet w przypadku kontenera serwletów Tomcat istnieje kilka opcji i wszystkie są różne. Przykładowo, jeśli korzystamy z kontenera serwletów Embedded Tomcat (czyli podnosimy serwer WWW z kodu), to o takich opcjach można przeczytać tutaj: „ Embedded Tomcat z podstawowym uwierzytelnianiem poprzez kod ”. Ponadto ogólny przykład uruchomienia Embedde Tomcat można zobaczyć w przewodniku po platformie Heroku PaaS: „ Tworzenie aplikacji internetowej Java przy użyciu Embedded Tomcat ”. Jeśli Tomcat nie jest używany w trybie osadzonym, w przypadku Tomcat możesz zastosować powszechnie stosowane podejście - detektory zdarzeń. W Tomcat jest to „ Komponent odbiornika cyklu życia ”. Jednocześnie ważne jest, aby zrozumieć, że kontenery serwletów (w naszym przypadku Tomcat) mogą mieć własne moduły ładujące klasy i nie będzie możliwe po prostu pobranie i użycie twoich klas. W przypadku Tomcata musisz zrozumieć „Instrukcję obsługi modułu ładującego klasy ”. W innym kontenerze serwletów o nazwie Undertow można to osiągnąć za pomocą „ Rozszerzeń serwletów ”. Jak widać, niektóre zapewniły bardziej elastyczne mechanizmy, inne nie. Jak widać, nie ma jednej opcji. Wszystkie są bardzo różne. Czy można w jakiś sposób zrobić coś uniwersalnego, korzystając wyłącznie z Servlet API i JAAS? W Internecie można znaleźć propozycję wykorzystania Servlet Filter do przeprowadzenia uwierzytelnienia bez blokady login-config
. Rozważmy w końcu tę opcję. To pozwoli nam powtórzyć działanie JAAS.
Filtr autoryzacji
Naszym celem jest więc całkowite pozbycie sięweb.xml
pliku. Jeśli się go pozbędziemy, to niestety nie będziemy już mogli korzystać z ograniczenia bezpieczeństwa, ponieważ ich przetwarzanie może nastąpić znacznie wcześniej niż zastosowane zostaną filtry serwletów. To opłata, jaką trzeba zapłacić za „uniwersalność” stosowania filtrów. Te. będziemy musieli usunąć adnotację @ServletSecurity
, a wszystkie kontrole, które poprzednio opisaliśmy w ograniczeniu bezpieczeństwa, będą musiały zostać przeprowadzone programowo. Jak widać, ta opcja również narzuca nam wiele przykrych ograniczeń. Przede wszystkim usuńmy adnotację @ServletSecurity
z zasobu i blok login-config
z pliku web.xml
. Teraz sami będziemy musieli wdrożyć uwierzytelnianie podstawowe. Teraz dodajmy nasz filtr:
@WebFilter("/*")
public class JaasFilter implements javax.servlet.Filter {
Póki co wydaje się to proste. Napiszmy metodę inicjalizacji:
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String jaas_conf = filterConfig.getServletContext().getRealPath("/WEB-INF/jaas.config");
System.getProperties().setProperty("java.security.auth.login.config",jaas_conf);
}
Jak widać, jesteśmy teraz zmuszeni użyć podstawowych narzędzi JAAS do wyszukiwania pliku konfiguracyjnego jaas. Ma to dużą wadę - jeśli jest kilka aplikacji, to jedna aplikacja może złamać uwierzytelnienie drugiej. Sposób ogólnego ustawienia pliku konfiguracyjnego Jaas opisano szczegółowo w dokumentacji JAAS: „ Dodatek A: Ustawienia JAAS w pliku właściwości zabezpieczeń java.security ”. Następnie opiszemy samą metodę filtrowania. Zacznijmy od otrzymania żądania HTTP:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Если в реквесте уже есть Principal - ничего не делаем
if (req.getUserPrincipal() != null ) {
chain.doFilter(request, response);
}
Tutaj wszystko jest proste. Jeśli podmiot zabezpieczeń jest dostępny, uwierzytelnianie zakończyło się pomyślnie. Następnie należy opisać działanie filtra, gdy użytkownik nie przeszedł uwierzytelnienia, czyli tzw. nie został jeszcze rozpoznany. Wcześniej opisaliśmy podstawową metodę uwierzytelniania BASIC. Ponieważ Ponieważ teraz sami piszemy filtr, będziemy musieli dowiedzieć się, jak faktycznie działa uwierzytelnianie w języku BASIC. Możesz użyć „ Dokumenty internetowe MDN: autoryzacja HTTP ”. A także „ Jak działa uwierzytelnianie HTTP? ” Z opisu działania uwierzytelniania podstawowego jasno wynika, że jeśli serwer chce przeprowadzić uwierzytelnienie BASIC, a użytkownik nie podał danych, to serwer wysyła specjalny nagłówek „WWW-Authenticate” i kod błędu 401. Utwórzmy wewnętrzna metoda tego:
private void requestNewAuthInResponse(ServletResponse response) throws IOException {
HttpServletResponse resp = (HttpServletResponse) response;
String value = "Basic realm=\"JaasLogin\"";
resp.setHeader("WWW-Authenticate", value);
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
Teraz skorzystajmy z tej metody i dodajmy doFilter
do niej następujący blok kodu:
// Получаем security Header. А если его нет - запрашиваем
String secHeader = req.getHeader("authorization");
if (secHeader == null) {
requestNewAuthInResponse(response);
}
Dodajmy teraz gałąź dla if
, która zostanie wykonana, gdy nagłówek zostanie już przesłany:
// Проверяем аутентификацию
else {
String authorization = secHeader.replace("Basic ", "");
Base64.Decoder decoder = java.util.Base64.getDecoder();
authorization = new String(decoder.decode(authorization));
String[] loginData = authorization.split(":");
try {
if (loginData.length == 2) {
req.login(loginData[0], loginData[1]);
chain.doFilter(request, response);
} else {
requestNewAuthInResponse(response);
}
} catch (ServletException e) {
requestNewAuthInResponse(response);
}
}
Do tego kodu możesz dodać autoryzację, np.: req.isUserInRole("admin")
Zatem dokonaliśmy u Ciebie uwierzytelnienia za pomocą filtra. Z jednej strony jest dużo kodu i ręcznego przetwarzania. Z drugiej strony istnieje wszechstronność i niezależność serwera.
GO TO FULL VERSION