How to redirect app in Struts 2 with Azure SSO

Viewed 259

I am wanting to implement a SSO with Azure to an application that is built with Struts 2, I have already implemented the filter to make me the login from the microsoft page.

enter image description here

however the redirection issue when everything is correct is not quite clear to me as I am not very good with Struts as when you originally log into the app it redirects to a URL like this localhost:8080/Portal/login/loginAction_validateUser.action.

this is the original login class of the application

public class ActionLogin extends ActionSupport implements ServletRequestAware {

    private static Logger logger = LogManager.getLogger(ActionLogin.class);

    private HttpServletRequest request;

    private UtilHttp uhttp = new UtilHttp();

    private UtilFiles ufiles = new UtilFiles();

    private User user;

    private Person person;

    private int invalidateSession;

   
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }

  
    public String validateUser() {
        try {

            HttpSession session = request.getSession();
            uhttp.cleanSession(request);
            LoginBusiness business = new LoginBusiness();
            String page = "menu";
            boolean loadMenu = true;

            if (invalidateSession == 1) {
               
                new LoginBusiness().createDifferentSession(session, user.getLogin());
                invalidateSession = 0;
            } 

            user = business.validateUser(user.getLogin(), user.getPassword(), user.getIdProfile());
            if (business.isUserLocked(user.getIdUser())) {
                 
                    addActionError(PortalAdministracionErrorCode.E0400.getMessage());
                return "blockedAccount";
                }

            if (user.isValidAuth()) {
                logger.info("user.isValidAuth()");

                if (user.isChangePassword()) {
                 
                    loadMenu = false;
                    page = "change";
                }
                
                if (business.isPasswordExpired(user.getIdUser())) {
                   
                    addActionError(getText(PortalAdministracionErrorCode.E0401.getMessage()));
                    loadMenu = false;
                    return "passwordexpired";
                }

     
                session = request.getSession(true);
                session.setAttribute(Constants.SESSION_USER, user);

            } else {
               
                addActionError(getText("label.error.login.password"));
                user.setAuthType("1");
                page = "login";
            }

            return page;

        } catch (Exception e) {
            addActionError(e.getMessage());
            return "login";
        }
    }

 

    public String logOut() {
        HttpSession session = request.getSession(false);
        if (session != null) {

            try {
                logger.info("Realiza un logOut el usuario ");
                uhttp.cleanSession(request);
                return "logout";
                //session.invalidate();
            } catch (Exception ex) {
                logger.error(ex);
                return "logout";
            }
        }
        return "logout";
    }

    public String invalidateSession() {
        invalidateSession = 0;
        return "login";
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public int getInvalidateSession() {
        return invalidateSession;
    }

    public void setInvalidateSession(int invalidateSession) {
        this.invalidateSession = invalidateSession;
    }
}

this is the XML that is configured with struts, when the login is ok it redirects to the JSP called entrada.jsp but in the url it does not change when it does this if it does not put the action of the login that was made, any idea how to adapt it?

my login.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
    
<struts>
    <package name="login" namespace="/login" extends="struts-default,json-default">

        <interceptors>
            <interceptor-stack name="defaultSecurityStack">
                <interceptor-ref name="tokenSession">
                    <param name="excludeMethods">*</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack" />
            </interceptor-stack>
        </interceptors>
        
        <default-interceptor-ref name="defaultSecurityStack" />
        
        <global-results>
            <result name="error">/jsp/common/errorDetail.jsp</result>
            <result name="invalid.token">/jsp/Login.jsp</result>
        </global-results>  
        
        <action name="loginAction_*" method="{1}" class="action.ActionLogin">           
            <result name="login">/jsp/Login.jsp</result>
            <result name="menu">/jsp/menu/Entrada.jsp</result>
             
            <result name="platformAdmon">/jsp/profile/AddPlatformAdmon.jsp</result>
            <result name="change">/jsp/menu/ChangePassword.jsp</result>
            <result name="passwordexpired">/jsp/login/account/password-expired.jsp</result>
            <result name="success">/jsp/menu/ResultOperation.jsp</result>
           
            <result name="blockedAccount">/jsp/login/account/blocked.jsp</result>
            <result name="logout">/jsp/logout.jsp</result>
            <interceptor-ref name="defaultSecurityStack">
                <param name="tokenSession.excludeMethods">validatePlatformAdmin,logOut,editPassword</param>
            </interceptor-ref>
            <allowed-methods>toPageLogin, addPlatformAdmon, authType, changePassword, editPassword, logOut, showMenu, validatePlatformAdmin, validateUser</allowed-methods>
        </action>
        
        <action name="account_*" method="{1}" class="com.seguridata.rne.administracion.portal.action.LoginAccountAction">
            <result name="login">/jsp/Login.jsp</result>
            <result name="blocked">/jsp/login/account/blocked.jsp</result>
            <result name="resultblocked">/jsp/login/account/blocked-response.jsp</result>
            <result name="unlock">/jsp/login/account/unlock.jsp</result>
            <result name="resultunlock">/jsp/login/account/unlock-response.jsp</result>
            <result name="passwordexpired">/jsp/login/account/password-expired.jsp</result>
            <result name="passwordexpired_success">/jsp/login/account/password-expired-success.jsp</result>
            <interceptor-ref name="defaultSecurityStack">
                <param name="tokenSession.excludeMethods">*</param>
            </interceptor-ref>
            <allowed-methods>unlock, unlockRequest, updatePasswordExpired, validateUnlockRequest</allowed-methods>
        </action>
    </package>    
</struts>

this is my filter code for the SSO redirection I don't really see the issue here but in the configuration in Azure

public class BasicFilter implements Filter {

    public static final String STATES = "states";
    public static final String STATE = "state";
    public static final Integer STATE_TTL = 3600;
    public static final String FAILED_TO_VALIDATE_MESSAGE = "Failed to validate data received from Authorization service - ";
    private String clientId = "";
    private String clientSecret = "";
    private String tenant = "";
    private String authority;

    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
       
         HttpServletResponse httpResponse = (HttpServletResponse) response;
         if (request instanceof HttpServletRequest) {
           
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            
            try {
                String currentUri = httpRequest.getRequestURL().toString();
                String queryStr = httpRequest.getQueryString();
                String fullUrl = currentUri + (queryStr != null ? "?" + queryStr : "");

                // check if user has a AuthData in the session
                if (!AuthHelper.isAuthenticated(httpRequest)) {
                    if (AuthHelper.containsAuthenticationData(httpRequest)) {
                        processAuthenticationData(httpRequest, currentUri, fullUrl);
                    } else {
                        // not authenticated
                        sendAuthRedirect(httpRequest, httpResponse);
                        return;
                    }
                }
                if (isAuthDataExpired(httpRequest)) {
                    updateAuthDataUsingRefreshToken(httpRequest);
                }
            } catch (AuthenticationException authException) {
                // something went wrong (like expiration or revocation of token)
                // we should invalidate AuthData stored in session and redirect to Authorization server
                removePrincipalFromSession(httpRequest);
                sendAuthRedirect(httpRequest, httpResponse);
                return;
            } catch (Throwable exc) {
                httpResponse.setStatus(500);
                request.setAttribute("error", exc.getMessage());
                request.getRequestDispatcher("/error.jsp").forward(request, response);
            }
        }
        
     
        chain.doFilter(request, response);
                  
    }

    private boolean isAuthDataExpired(HttpServletRequest httpRequest) {
        AuthenticationResult authData = AuthHelper.getAuthSessionObject(httpRequest);
        return authData.getExpiresOnDate().before(new Date()) ? true : false;
    }

    private void updateAuthDataUsingRefreshToken(HttpServletRequest httpRequest) throws Throwable {
        AuthenticationResult authData =
                getAccessTokenFromRefreshToken(AuthHelper.getAuthSessionObject(httpRequest).getRefreshToken());
        setSessionPrincipal(httpRequest, authData);
    }

    private void processAuthenticationData(HttpServletRequest httpRequest, String currentUri, String fullUrl)
            throws Throwable {
            HttpSession session = httpRequest.getSession(false);
        HashMap<String, String> params = new HashMap<>();
        for (String key : httpRequest.getParameterMap().keySet()) {
            params.put(key, httpRequest.getParameterMap().get(key)[0]);
        }
        // validate that state in response equals to state in request
        StateData stateData = validateState(httpRequest.getSession(), params.get(STATE));

        AuthenticationResponse authResponse = AuthenticationResponseParser.parse(new URI(fullUrl), params);
        if (AuthHelper.isAuthenticationSuccessful(authResponse)) {
            AuthenticationSuccessResponse oidcResponse = (AuthenticationSuccessResponse) authResponse;
            // validate that OIDC Auth Response matches Code Flow (contains only requested artifacts)
            validateAuthRespMatchesCodeFlow(oidcResponse);

            AuthenticationResult authData =
                    getAccessToken(oidcResponse.getAuthorizationCode(), currentUri);
            // validate nonce to prevent reply attacks (code maybe substituted to one with broader access)
            validateNonce(stateData, getClaimValueFromIdToken(authData.getIdToken(), "nonce"));

            setSessionPrincipal(httpRequest, authData);
        } else {
            AuthenticationErrorResponse oidcResponse = (AuthenticationErrorResponse) authResponse;
            throw new Exception(String.format("Request for auth code failed: %s - %s",
                    oidcResponse.getErrorObject().getCode(),
                    oidcResponse.getErrorObject().getDescription()));
        }
    }

    private void validateNonce(StateData stateData, String nonce) throws Exception {
        if (StringUtils.isEmpty(nonce) || !nonce.equals(stateData.getNonce())) {
            throw new Exception(FAILED_TO_VALIDATE_MESSAGE + "could not validate nonce");
        }
    }

    private String getClaimValueFromIdToken(String idToken, String claimKey) throws ParseException {
        return (String) JWTParser.parse(idToken).getJWTClaimsSet().getClaim(claimKey);
    }

    private void sendAuthRedirect(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException {
        httpResponse.setStatus(302);

        // use state parameter to validate response from Authorization server
        String state = UUID.randomUUID().toString();

        // use nonce parameter to validate idToken
        String nonce = UUID.randomUUID().toString();

        storeStateInSession(httpRequest.getSession(), state, nonce);

        String currentUri = httpRequest.getRequestURL().toString();
        httpResponse.sendRedirect(getRedirectUrl(currentUri, state, nonce));
    }

    /**
     * make sure that state is stored in the session,
     * delete it from session - should be used only once
     *
     * @param session
     * @param state
     * @throws Exception
     */
    private StateData validateState(HttpSession session, String state) throws Exception {
          
        if (StringUtils.isNotEmpty(state)) {
            StateData stateDataInSession = removeStateFromSession(session, state);
            if (stateDataInSession != null) {
                return stateDataInSession;
            }
        }
        throw new Exception(FAILED_TO_VALIDATE_MESSAGE + "could not validate state");
    }

    private void validateAuthRespMatchesCodeFlow(AuthenticationSuccessResponse oidcResponse) throws Exception {
        if (oidcResponse.getIDToken() != null || oidcResponse.getAccessToken() != null ||
                oidcResponse.getAuthorizationCode() == null) {
            throw new Exception(FAILED_TO_VALIDATE_MESSAGE + "unexpected set of artifacts received");
        }
    }

    @SuppressWarnings("unchecked")
    private StateData removeStateFromSession(HttpSession session, String state) {
           
        Map<String, StateData> states = (Map<String, StateData>) session.getAttribute(STATES);
        if (states != null) {
            eliminateExpiredStates(states);
            StateData stateData = states.get(state);
            if (stateData != null) {
                states.remove(state);
                return stateData;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private void storeStateInSession(HttpSession session, String state, String nonce) {
        
        if (session.getAttribute(STATES) == null) {
            session.setAttribute(STATES, new HashMap<String, StateData>());
        }
        ((Map<String, StateData>) session.getAttribute(STATES)).put(state, new StateData(nonce, new Date()));
    }

    private void eliminateExpiredStates(Map<String, StateData> map) {
        Iterator<Map.Entry<String, StateData>> it = map.entrySet().iterator();

        Date currTime = new Date();
        while (it.hasNext()) {
            Map.Entry<String, StateData> entry = it.next();
            long diffInSeconds = TimeUnit.MILLISECONDS.
                    toSeconds(currTime.getTime() - entry.getValue().getExpirationDate().getTime());

            if (diffInSeconds > STATE_TTL) {
                it.remove();
            }
        }
    }

    private AuthenticationResult getAccessTokenFromRefreshToken(
            String refreshToken) throws Throwable {
        AuthenticationContext context;
        AuthenticationResult result = null;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(authority + tenant + "/", true,
                    service);
            Future<AuthenticationResult> future = context
                    .acquireTokenByRefreshToken(refreshToken, new ClientCredential(clientId, clientSecret), null, null);
            result = future.get();
        } catch (ExecutionException e) {
            throw e.getCause();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new ServiceUnavailableException("authentication result was null");
        }
        return result;
    }

    private AuthenticationResult getAccessToken(
            AuthorizationCode authorizationCode, String currentUri)
            throws Throwable {
        String authCode = authorizationCode.getValue();
        ClientCredential credential = new ClientCredential(clientId,
                clientSecret);
        AuthenticationContext context;
        AuthenticationResult result = null;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(authority + tenant + "/", true,
                    service);
            Future<AuthenticationResult> future = context
                    .acquireTokenByAuthorizationCode(authCode, new URI(
                            currentUri), credential, null);
            result = future.get();
        } catch (ExecutionException e) {
            throw e.getCause();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new ServiceUnavailableException("authentication result was null");
        }
        return result;
    }

    private void setSessionPrincipal(HttpServletRequest httpRequest,
                                     AuthenticationResult result) {
        httpRequest.getSession().setAttribute(AuthHelper.PRINCIPAL_SESSION_NAME, result);
    }

    private void removePrincipalFromSession(HttpServletRequest httpRequest) {
        httpRequest.getSession().removeAttribute(AuthHelper.PRINCIPAL_SESSION_NAME);
    }

    private String getRedirectUrl(String currentUri, String state, String nonce)
            throws UnsupportedEncodingException {
        String redirectUrl = authority
                + this.tenant
                + "/oauth2/authorize?response_type=code&scope=directory.read.all&response_mode=form_post&redirect_uri="
                + URLEncoder.encode(currentUri, "UTF-8") + "&client_id="
                + clientId + "&resource=https%3a%2f%2fgraph.microsoft.com"
                + "&state=" + state
                + "&nonce=" + nonce;

        return redirectUrl;
    }

    public void init(FilterConfig config) throws ServletException {
        clientId = config.getInitParameter("client_id");
        authority = config.getServletContext().getInitParameter("authority");
        tenant = config.getServletContext().getInitParameter("tenant");
        clientSecret = config.getInitParameter("secret_key");
    }

    private class StateData {
        private String nonce;
        private Date expirationDate;

        public StateData(String nonce, Date expirationDate) {
            this.nonce = nonce;
            this.expirationDate = expirationDate;
        }

        public String getNonce() {
            return nonce;
        }

        public Date getExpirationDate() {
            return expirationDate;
        }
    }
}

my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   
    <filter>
        <filter-name>struts-prepare</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareFilter</filter-class>
    </filter>
    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>
    <filter>
        <filter-name>struts-execute</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsExecuteFilter</filter-class>
    </filter>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts-prepare</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-execute</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
        
    <jsp-config>
        <taglib>
            <taglib-uri>/struts-tags</taglib-uri>
            <taglib-location>/WEB-INF/struts-tags.tld</taglib-location>
        </taglib>

        <taglib>
            <taglib-uri>sitemesh-page</taglib-uri>
            <taglib-location>/WEB-INF/sitemesh-page.tld</taglib-location>
        </taglib>

        <taglib>
            <taglib-uri>sitemesh-decorator</taglib-uri>
            <taglib-location>/WEB-INF/sitemesh-decorator.tld</taglib-location>
        </taglib>
    </jsp-config>
    
  
    <welcome-file-list>
        <welcome-file>/jsp/Login.jsp</welcome-file>
    </welcome-file-list>

     <context-param>
  <param-name>authority</param-name>
  <param-value>https://login.windows.net/</param-value>
 </context-param>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
 </context-param>

 <context-param>
  <param-name>tenant</param-name>
  <param-value>mytenat</param-value>
 </context-param>
 
 <filter-mapping>
  <filter-name>BasicFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>
  <filter>
  <filter-name>BasicFilter</filter-name>
  <filter-class>mx.prototype.adaj4jAzure.BasicFilter</filter-class>
  <init-param>
   <param-name>client_id</param-name>
   <param-value>myclientid</param-value>
  </init-param>
  <init-param>
   <param-name>secret_key</param-name>
   <param-value>secretkeyy</param-value>
  </init-param>
 </filter>
  <servlet>
  <servlet-name>mvc-dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
    
</web-app>

my struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
    
<struts>

<!--    
    <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
    <constant name="struts.devMode" value="false" />
    <constant name="struts.configuration.xml.reload" value="false" />
    <constant name="struts.i18n.reload" value="false" />
    <constant name="struts.custom.i18n.resources" value="ApplicationResources" />
    <constant name="struts.multipart.maxSize" value="5242880"/>
   -->
   
    <constant name="struts.enable.DynamicMethodInvocation" value="true"/>
    <constant name="struts.custom.i18n.resources" value="ApplicationResources" />
    <constant name="struts.multipart.maxSize" value="524288000" />
    <constant name="struts.devMode" value="false" />    
    <constant name="struts.i18n.reload" value="false"/>
    <constant name="struts.configuration.xml.reload" value="false"/>
    <constant name="struts.freemarker.templatesCache" value="true"/>
    <constant name="struts.freemarker.templatesCache.updateDelay" value="120"/>
    <constant name="struts.freemarker.mru.max.strong.size" value="120"/>
    <constant name="struts.action.excludePattern" value="/struts/webconsole.html" />
    <constant name="struts.action.excludePattern" value="/struts/webconsole.css"/>
    <constant name="struts.action.excludePattern" value="/struts/webconsole.js"/>

    <include file="strutsDomain.xml"/>
    <include file="strutsLogin.xml"/>
    <include file="strutsPasswordParameters.xml"/>
    <include file="strutsPersonalize.xml"/>
    <include file="strutsProfile.xml"/>
    <include file="strutsTemplate.xml"/>
    <include file="strutsRoute.xml"/>
    <include file="strutsPlantillas.xml"/>
   
    <package name="serveAll" namespace="" extends="struts-default">
        <action name="*">
            <result>/jsp/common/error.jsp</result>
        </action>
    </package>
</struts>
1 Answers

I think your filter looks fine indeed.

Probably the error is in your LoginAction, you need to adapt it to the new auth flow and remove older stuff. Please, consider the following simplification:

public class ActionLogin extends ActionSupport implements ServletRequestAware {

    private static Logger logger = LogManager.getLogger(ActionLogin.class);

    private HttpServletRequest request;

    private UtilHttp uhttp = new UtilHttp();

    private UtilFiles ufiles = new UtilFiles();

    private User user;

    private Person person;

    private int invalidateSession;

   
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }

  
    public String validateUser() {
        try {

            HttpSession session = request.getSession();
            // First, you need to obtain a reference to the AuthenticationResult
            // your filter defined the session with the setSessionPrincipal method
            AuthenticationResult authenticationResult) = 
                (AuthenticationResult)session.getAttribute(AuthHelper.PRINCIPAL_SESSION_NAME);
            // Handle authenticationResult == null case, probably redirecting
            // to an error or 403 page
            if (authenticationResult == null) {
              return "error";
            }

            // Obtain user information from the AuthenticationResult
            // Debug the object and see what information is provided
            // in the associated UserInfo object, or use the idToken,
            // something that allows you to uniquely identify your user
            String userId = "...";

            // Now, obtain user information from the database
            // probably you will need to define a new method in LoginBusiness
            LoginBusiness business = new LoginBusiness();
            user = business.getUser(userId);
            // Perhaps this condition is right, I don't know
            if (business.isUserLocked(user.getIdUser())) {
             addActionError(PortalAdministracionErrorCode.E0400.getMessage());
                return "blockedAccount";
            }

            // set the user attribute in order to allow the rest of your code
            // to work properly 
            session.setAttribute(Constants.SESSION_USER, user);

            return "menu";

        } catch (Exception e) {
            addActionError(e.getMessage());
            return "error";
        }
    }

 
    // Probaly logout, etcetera should be changed as well
    // I don't know whether the rest of the code should or not be 
    // maintained: it will greatly depend on the rest of your application
    public String logOut() {
        HttpSession session = request.getSession(false);
        if (session != null) {

            try {
                logger.info("Realiza un logOut el usuario ");
                uhttp.cleanSession(request);
                return "logout";
                //session.invalidate();
            } catch (Exception ex) {
                logger.error(ex);
                return "logout";
            }
        }
        return "logout";
    }

    public String invalidateSession() {
        invalidateSession = 0;
        return "login";
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public int getInvalidateSession() {
        return invalidateSession;
    }

    public void setInvalidateSession(int invalidateSession) {
        this.invalidateSession = invalidateSession;
    }
}

The login.xml file can be simplified also:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
    
<struts>
    <package name="login" namespace="/login" extends="struts-default,json-default">

        <interceptors>
            <interceptor-stack name="defaultSecurityStack">
                <interceptor-ref name="tokenSession">
                    <param name="excludeMethods">*</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack" />
            </interceptor-stack>
        </interceptors>
        
        <default-interceptor-ref name="defaultSecurityStack" />
        
        <global-results>
            <result name="error">/jsp/common/errorDetail.jsp</result>
            <result name="invalid.token">/jsp/Login.jsp</result>
        </global-results>  
        
        <action name="loginAction_*" method="{1}" class="action.ActionLogin">           
            <result name="menu">/jsp/menu/Entrada.jsp</result>
            <result name="blockedAccount">/jsp/login/account/blocked.jsp</result>
            <result name="logout">/jsp/logout.jsp</result>
            <interceptor-ref name="defaultSecurityStack">
                <param name="tokenSession.excludeMethods">validatePlatformAdmin,logOut,editPassword</param>
            </interceptor-ref>
            <allowed-methods>toPageLogin, addPlatformAdmon, authType, changePassword, editPassword, logOut, showMenu, validatePlatformAdmin, validateUser</allowed-methods>
        </action>
        
        <action name="account_*" method="{1}" class="com.seguridata.rne.administracion.portal.action.LoginAccountAction">
            <result name="login">/jsp/Login.jsp</result>
            <result name="blocked">/jsp/login/account/blocked.jsp</result>
            <result name="resultblocked">/jsp/login/account/blocked-response.jsp</result>
            <result name="unlock">/jsp/login/account/unlock.jsp</result>
            <result name="resultunlock">/jsp/login/account/unlock-response.jsp</result>
            <result name="passwordexpired">/jsp/login/account/password-expired.jsp</result>
            <result name="passwordexpired_success">/jsp/login/account/password-expired-success.jsp</result>
            <interceptor-ref name="defaultSecurityStack">
                <param name="tokenSession.excludeMethods">*</param>
            </interceptor-ref>
            <allowed-methods>unlock, unlockRequest, updatePasswordExpired, validateUnlockRequest</allowed-methods>
        </action>
    </package>    
</struts>

I only edited the paths that look obvious to me, probably this configuration could be greatly simplified, especially the one related to defaultSecurityStack.

Finally, consider to change the order of your filters in the web.xml file, for the same path they are invoked in the order in which they are defined, be sure that BasicFilter is the first one:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

     <context-param>
       <param-name>authority</param-name>
       <param-value>https://login.windows.net/</param-value>
      </context-param>
      <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
      </context-param>

      <context-param>
       <param-name>tenant</param-name>
       <param-value>mytenat</param-value>
      </context-param>
 
      <filter>
       <filter-name>BasicFilter</filter-name>
       <filter-class>mx.prototype.adaj4jAzure.BasicFilter</filter-class>
       <init-param>
        <param-name>client_id</param-name>
        <param-value>myclientid</param-value>
       </init-param>
       <init-param>
        <param-name>secret_key</param-name>
        <param-value>secretkeyy</param-value>
       </init-param>
      </filter>
      <filter-mapping>
       <filter-name>BasicFilter</filter-name>
       <url-pattern>/*</url-pattern>
      </filter-mapping>
   
    <filter>
        <filter-name>struts-prepare</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareFilter</filter-class>
    </filter>
    <filter>
        <filter-name>sitemesh</filter-name>
        <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>
    <filter>
        <filter-name>struts-execute</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsExecuteFilter</filter-class>
    </filter>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts-prepare</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>sitemesh</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>struts-execute</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
        
    <jsp-config>
        <taglib>
            <taglib-uri>/struts-tags</taglib-uri>
            <taglib-location>/WEB-INF/struts-tags.tld</taglib-location>
        </taglib>

        <taglib>
            <taglib-uri>sitemesh-page</taglib-uri>
            <taglib-location>/WEB-INF/sitemesh-page.tld</taglib-location>
        </taglib>

        <taglib>
            <taglib-uri>sitemesh-decorator</taglib-uri>
            <taglib-location>/WEB-INF/sitemesh-decorator.tld</taglib-location>
        </taglib>
    </jsp-config>
    
  
    <welcome-file-list>
        <welcome-file>/jsp/Login.jsp</welcome-file>
    </welcome-file-list>
 
  <servlet>
  <servlet-name>mvc-dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
 </servlet-mapping>
    
</web-app>
Related