redirect to an action from a struts filter

Viewed 40

I have a problem, I have a project with struts2 and I want to make a filter to make a login from azure, however, I want to redirect to an action if everything went correctly, but it gives me the following error

enter image description here

I am not very good with this technology, I am learning and I have relied on some tutorials, however, I do not make it into the action.

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">
   
    <listener>
        <listener-class>com.seguridata.rne.portal.servlet.http.SessionListener</listener-class>
    </listener>    
    
    <!-- filter -->
    <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 -->

    <!-- filter-mapping -->
    <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>
    <!-- .\filter-mapping -->
    
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    
    <welcome-file-list>
        <welcome-file>/view/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>tenant</param-name>
    <param-value>xxxxxxgmail.onmicrosoft.com</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>com.seguridata.rne.portal.adaj4jAzure.BasicFilter</filter-class>
    <init-param>
     <param-name>client_id</param-name>
     <param-value>xxxxxx-xxxxxx-xxxx-a1ce-xxxxxx</param-value>
    </init-param>
    <init-param>
     <param-name>secret_key</param-name>
     <param-value>xxxxx~xxxxxxx</param-value>
    </init-param>
   </filter>
    
</web-app>

My basicFilter

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 {

    if (request instanceof HttpServletRequest) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
    String path = httpRequest.getContextPath();
    String basePath = httpRequest.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path;
        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);
            }
            httpResponse.sendRedirect("login/loginAction_validateUser.action");
        } 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 {
    HashMap<String, String> params = new HashMap<>();
    
    httpRequest.getParameterMap().keySet().forEach((Object key) -> {
        String[]  tempParam =(String[]) httpRequest.getParameterMap().get(key);
        params.put((String)key, tempParam[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;
    }
}

Is what I am trying to do possible?

0 Answers
Related