Nadpisywanie domyślnej strony logowania Liferaya przy pomocy React Client Extension
Dowiedz się, jak nadpisać domyślną stronę logowania Liferay przy użyciu React Client Extensions. Ten kompleksowy przewodnik przeprowadzi Cię przez proces tworzenia niestandardowej strony logowania.
Wprowadzenie
Mimo że Liferay daje dużą elastyczność w tworzeniu stron czy treści, opcje modyfikacji strony logowania są dość ograniczone. Mimo to wiele firm staje przed koniecznością dostosowania strony logowania do swoich wymagań brandingowych i doświadczenia użytkownika. Niektóre z tych modyfikacji można osiągnąć za pomocą motywów lub globalnego CSS, ale staje się to bardziej skomplikowane, gdy potrzebujesz pełnej kontroli nad stroną logowania.
Ten artykuł poprowadzi Cię przez proces tworzenia niestandardowej strony logowania dla Twojego portalu Liferay przy użyciu React Client Extensions.
Dlaczego ma to znaczenie
W wielu przypadkach nie potrzebujemy niestandardowej strony logowania poza tą już dostarczoną, zwłaszcza że Liferay oferuje kilka użytecznych funkcjonalności out-of-the-box. W większości przypadków powiedziałbym, że wystarczy odpowiednia stylizacja.
Z drugiej strony są specjalne przypadki, w których potrzebujemy czegoś bardziej złożonego. Czasami potrzebujemy niestandardowej logiki do integracji z systemami zewnętrznymi, a czasami po prostu potrzebujemy większej kontroli nad interfejsem użytkownika ze względów brandingowych.
Opcje dostarczenia niestandardowej strony logowania
Liferay nie zapewnia prostego sposobu na wdrożenie własnej niestandardowej strony logowania. Istnieją pewne opcje obejmujące utworzenie zwykłej strony, a następnie modyfikację właściwości portalu (portal properties), aby jej używać, ale z mojego doświadczenia to podejście nie działa tak dobrze, jak powinno.
Drugą opcją byłoby użycie fragmentu OSGi do nadpisania domyślnego JSP logowania. To podejście działa i jest dość proste, ale wtedy mamy do czynienia z JSP-ami, które nie są najnowszą technologią i nie są już preferowanym wyborem.
Moglibyśmy oczywiście również zmodyfikować domyślne logowanie za pomocą niestandardowego JS'a, ale wtedy byłoby najpierw renderowane z regularnym widokiem, a następnie modyfikowane niestandardowym JS-em, co nie jest idealne dla doświadczenia użytkownika.
W projekcie, nad którym obecnie pracuję, chciałem zastosować lepsze podejście: użyć React, bo to obecnie standardowy wybór, nie komplikować tego zbytnio, a także, jeśli to możliwe, sprawić, by działało z Client Extensions. Ten przewodnik pokaże Ci, co udało mi się ustalić.
Architektura niestandardowej strony logowania
Sposób, w jaki osiągniemy integrację React Client Extension ze stroną logowania Liferay, jest dość prosty: wykorzystamy już wspomnianą opcję nadpisania JSP za pomocą fragmentu, ale zamiast pisać niestandardowy kod JSP, usuniemy tylko domyślne pola formularza i wywołamy nasz niestandardowy komponent React.
Nie będziemy zmieniać logiki akcji portletu. W ten sposób możemy ponownie wykorzystać wszystkie istniejące walidacje i środki bezpieczeństwa, skupiając się tylko na dostarczeniu nowego layoutu. Oczywiście zapewnienie niestandardowego hooka logowania również byłoby możliwe, i to coś, co faktycznie robiłem w przeszłości, ale nie jest to naprawdę wymagane w tym przypadku.
Konfiguracja React Client Extension
Przede wszystkim musimy utworzyć React Client Extension. Ten artykuł nie koncentruje się na szczegółach Client Extensions jako takich, zwłaszcza że ten temat jest dość popularny i dobrze udokumentowany.
Jest jednak kilka rzeczy, które musisz wziąć pod uwagę, aby wszystko działało:
- Musisz zachować te same nazwy pól wejściowych, co w domyślnej stronie logowania Liferay: w ten sposób możemy osiągnąć integrację z istniejącym kodem portletu
- Musisz wywołać ten sam URL, co zwykle robi formularz strony logowania Liferay
Jeśli chodzi o pierwszą rzecz: możesz sprawdzić regularny kod HTML logowania i zobaczyć, jak nazywane są pola wejściowe, ale zasadniczo zawsze jest prefiks _com_liferay_login_web_portlet_LoginPortlet_ (aby wybrać właściwy portlet), a następnie nazwa pola wejściowego.
Ważne to:
- login (
_com_liferay_login_web_portlet_LoginPortlet_login) - password (
_com_liferay_login_web_portlet_LoginPortlet_password) - rememberMe (
_com_liferay_login_web_portlet_LoginPortlet_rememberMe) - to nie jest wymagane, ale miło jest mieć taką opcję - formDate (
_com_liferay_login_web_portlet_LoginPortlet_formDate)
A następnie są te, które możemy zignorować:
- redirect (
_com_liferay_login_web_portlet_LoginPortlet_redirect) - saveLastPath (
_com_liferay_login_web_portlet_LoginPortlet_saveLastPath) - doActionAfterLogin (
_com_liferay_login_web_portlet_LoginPortlet_doActionAfterLogin) - checkboxNames (
_com_liferay_login_web_portlet_LoginPortlet_checkboxNames)
Jest również jeden parametr, który musimy wysłać bez prefiksu LoginPortlet: p_auth - to nazwa parametru, który zawiera token związany z uwierzytelnianiem. Można go uzyskać za pomocą window.Liferay.authToken.
Minimalna wersja formularza mogłaby wyglądać tak:
<div className="login-container">
<div className="login-content">
<form method="POST" action={getFormAction()} className="login-form">
<input
type="hidden"
name="_com_liferay_login_web_portlet_LoginPortlet_formDate"
value={Date.now().toString()}
/>
{/* CSRF token*/}
{typeof window !== 'undefined' && window.Liferay?.authToken && (
<input
type="hidden"
name="p_auth"
value={window.Liferay.authToken}
/>
)}
<div className="login-form-group">
<Input
label="User Name / E-Mail"
type="text"
name="_com_liferay_login_web_portlet_LoginPortlet_login"
value={username}
onChange={setUsername}
placeholder=""
/>
<Input
label="Password"
type="password"
name="_com_liferay_login_web_portlet_LoginPortlet_password"
value={password}
onChange={setPassword}
placeholder=""
/>
<div className="login-extra">
<div className="login-checkbox">
<label className="checkbox-label">
<input
type="checkbox"
name="_com_liferay_login_web_portlet_LoginPortlet_rememberMe"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="checkbox-input"
/>
<span className="checkbox-text">Remember me</span>
</label>
</div>
</div>
</div>
</form>
</div>
</div>
To tylko podstawowy przykład - musisz obsłużyć wszystkie stany, stylizację i tak dalej. Niemniej jednak jest to część kodu, którego użyłem w rzeczywistej implementacji.
Jedną z części, która może nie być jasna, jest to, co robi metoda getFormAction użyta w akcji formularza. To właściwie metoda, która dostarcza URL z wymaganymi parametrami i mogłaby wyglądać tak:
const getFormAction = () => {
const currentUrl = window.location.href;
const url = new URL(currentUrl);
// Build the form action URL with required portlet parameters
url.searchParams.set('p_p_id', 'com_liferay_login_web_portlet_LoginPortlet');
url.searchParams.set('p_p_lifecycle', '1');
url.searchParams.set('p_p_state', 'normal');
url.searchParams.set('p_p_mode', 'view');
url.searchParams.set('_com_liferay_login_web_portlet_LoginPortlet_javax.portlet.action', '/login/login');
url.searchParams.set('_com_liferay_login_web_portlet_LoginPortlet_mvcRenderCommandName', '/login/login');
return url.toString();
};
Oczywiście możesz dodać wszelkie dalsze modyfikacje, takie jak nagłówki, linki, logowanie przez inne usługi itp. Na koniec może to wyglądać na przykład tak:

Integracja naszego formularza logowania z Liferay
Gdy mamy już działający komponent React jako Client Extension, musimy zintegrować go z Liferay. To właściwie dość proste: musimy tylko nadpisać domyślny JSP logowania naszym niestandardowym. Aby to zrobić, potrzebujemy niestandardowego fragmentu OSGi, który nadpisze domyślny JSP logowania.
Zaczynamy od utworzenia standardowego modułu OSGi. Magia dzieje się w pliku bnd.bnd:
Bundle-Name: net.casion Login Web Fragment
Bundle-SymbolicName: de.netcasion.login.web.fragment
Bundle-Version: 1.0.0
Fragment-Host: com.liferay.login.web;bundle-version="6.0.65"
Fragment-Host jest tutaj kluczową częścią. W ten sposób mówimy OSGi, że chcemy nadpisać domyślny JSP logowania. Oczywiście musimy wybrać właściwą wersję bundle'a z Liferay - w moim przypadku było to 6.0.65. W Twoim przypadku może to być inna wersja w zależności od wersji Liferay, którą używasz. Można to sprawdzić w gogo shell, uruchamiając:
lb login
Możesz również wybrać więcej niż jedną wersję: bundle-version="[6.0.00,6.0.99]"
Następnie musimy utworzyć prawidłową strukturę katalogów - to jest ważne, ponieważ jeśli nasz plik ma inną nazwę lub znajduje się w złym katalogu, nie zostanie użyty. Od struktury nadrzędnej potrzebujemy następującej struktury:
src/main/resources/META-INF/resources/login.jsp
W login.jsp proponowałbym na początku umieścić oryginalny kod JSP (możesz go znaleźć na GitHubie Liferaya). Następnie możesz usunąć wszystko, czego nie potrzebujesz, jak oryginalne pola formularza. W moim przypadku zachowałem tylko wyświetlanie błędów walidacji.
Ostatnim krokiem jest użycie naszego niestandardowego komponentu. Osobiście zrobiłem to na końcu JSP (po obsłudze błędów).
Dołączenie naszego Client Extension można wykonać po prostu używając tagów <script> ze ścieżką do naszego pliku JS. Jeśli mamy również niestandardowy CSS w naszym Client Extension, możemy go tu również dołączyć.
Przykład:
<script type="module" src="<%= PortalUtil.getStaticResourceURL(request, "/o/innray-login/assets/index.js") %>"></script>
<link rel="stylesheet" href="<%= PortalUtil.getStaticResourceURL(request, "/o/innray-login/assets/index.css") %>"/>
Upewnij się tylko, że używasz prawidłowych ścieżek zgodnych z konfiguracją Twojego Client Extension!
Następnie musimy umieścić niestandardowy tag HTML, który odpowiada konfiguracji Client Extension. Możemy tam również przekazać niestandardowe atrybuty jako data-props. W moim przypadku skończyło się na:
<re-next-login data-company-name="<%= company.getName() %>" data-create-account-url="<%=createAccountURL%>"
data-forgot-password-url="<%=forgotPasswordURL%>"></re-next-login>
Ważne: W moim przypadku musiałem zdefiniować taglib <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> na górze mojego login.jsp, mimo że nie powinno to być wymagane, ponieważ oryginalny init.jsp jest nadal używany.
Podsumowanie
Gdy wdrożymy nasz niestandardowy Client Extension i nadpiszemy domyślny JSP logowania, gotowe. Możemy teraz używać naszej niestandardowej strony logowania ze wszystkimi korzyściami React, zachowując jednocześnie walidacje i bezpieczeństwo Liferay.
Chociaż to hybrydowe podejście łączy dwie technologie, zapewnia najlepszą równowagę między elastycznością a łatwością utrzymania. Ponieważ wstrzykujemy React w login.jsp, powinno być stosunkowo łatwo to utrzymać podczas aktualizacji Liferay. Jednak, jak w przypadku każdego fragmentu OSGi, musisz zwrócić na to szczególną uwagę podczas migracji i upewnić się, że wersja bundle'a Fragment-Host jest odpowiednio zaktualizowana.