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
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?
