Tomcat 7 Jersey Jackson 200 OK but payload gets stripped - java

I made a rest service using Jersey/Jackson and I'm deploying to Tomcat 7 in dev. Locally, using Jetty, when I make the call
https://localhost/usorgws-web/v1/usorg/search/dep?page=2&limit=10
everything works fine and I get a 200 response with the return payload being a list of objects. But when I deploy to our dev environment and make the same call replacing localhost with the server's hostname, I get a 200 Okay, but no payload is returned. I'm sure that it's working because I'm putting print lines in the code and I'm watching the logs. I can see the payload as a populated list propagate from my dao all the way up to my resource. But it looks like as soon as the tomcat container gets a hold of the Response, it strips the payload for some reason. I don't see any errors in the logs
Here is some code:
Resource
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.List;
/**
* Created by barryj on 9/14/16.
*/
#Path("/usorg")
#Component
public class USOrgResource {
private final static Logger logger = Logger.getLogger(USOrgResource.class);
#Autowired
private USOrgDao usOrgDao;
#Autowired
private USOrgService usOrgService;
#Context
private UriInfo uriInfo;
#Value("${pagination.limit.default.min}")
private int minLimit;
#Value("${pagination.limit.default.max}")
private int maxLimit;
#GET
#Path("orgCode/{orgCode}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUSOrgByOrgCode(#PathParam("orgCode") String orgCode) throws
SystemException {
USOrg usOrg = null;
try {
usOrg = usOrgDao.getUsOrgByOrgCode(orgCode);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<USOrg> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrg);
responseWrapper.setDescription("USOrg retrieval by org_code");
responseWrapper.setSuccess(true);
return Response.status(200).entity(responseWrapper).build();
}
#GET
#Path("search/{term}")
#Produces(MediaType.APPLICATION_JSON)
public Response searchUsOrgWithPagination(#PathParam("term") String term, #QueryParam
("page") int page, #QueryParam("limit") int limit) throws SystemException {
logger.info("hit");
checkPageBounds(limit);
USOrgPageResults usOrgPageResults = null;
try {
usOrgPageResults = usOrgService.searchUsOrg(term, page, limit);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<List<USOrg>> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrgPageResults.getUsOrgList());
responseWrapper.setDescription("USOrg search");
responseWrapper.setSuccess(true);
return buildPaginationResponse(200, responseWrapper, usOrgPageResults);
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getUSOrg(#QueryParam("page") int page, #QueryParam("limit") int limit) throws
SystemException {
logger.info("hit");
checkPageBounds(limit);
USOrgPageResults usOrgPageResults = null;
try {
usOrgPageResults = usOrgService.getUsOrg(page, limit);
} catch (DataAccessException e) {
logger.error(e.getMessage(), e);
throw new SystemException();
}
ResponseWrapper<List<USOrg>> responseWrapper = new ResponseWrapper<>();
responseWrapper.setData(usOrgPageResults.getUsOrgList());
responseWrapper.setDescription("USOrg search");
responseWrapper.setSuccess(true);
if (responseWrapper.getData() != null)
logger.info("list size: " + responseWrapper.getData().size());
else
logger.info("list is null");
return buildPaginationResponse(200, responseWrapper, usOrgPageResults);
}
private Response buildPaginationResponse(int status, Object entity, USOrgPageResults
usOrgPageResults) {
final String PAGE = "?page=";
final String LIMIT = "&limit=";
String first = "";
if (usOrgPageResults.getFirst() != null)
first = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getFirst().getPage() +
LIMIT + usOrgPageResults.getFirst().getLimit();
String prev = "";
if (usOrgPageResults.getSelf().getPage() > 0)
prev = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getPrev()
.getPage() + LIMIT + usOrgPageResults.getPrev().getLimit();
String self = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getSelf().getPage() +
LIMIT + usOrgPageResults.getSelf().getLimit();
String next = "";
if (usOrgPageResults.getNext() != null)
next = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getNext().getPage() +
LIMIT + usOrgPageResults.getNext().getLimit();
String last = "";
if (usOrgPageResults.getLast() != null)
last = uriInfo.getAbsolutePath() + PAGE + usOrgPageResults.getLast().getPage() +
LIMIT + usOrgPageResults.getLast().getLimit();
return Response.status(status).entity(entity).header("first", first).header
("prev", prev).header("self", self).header("next", next).header
("last", last).build();
}
private void checkPageBounds(int limit) {
if (limit < minLimit)
throw new NotAllowedException("The limit requested is below the default limit minimum");
if (limit > maxLimit)
throw new NotAllowedException("The limit requested is above the default limit maximum");
}
public void setMinLimit(int minLimit) {
this.minLimit = minLimit;
}
public void setMaxLimit(int maxLimit) {
this.maxLimit = maxLimit;
}
}
Here is the WebApplication class where I register my JacksonJaxbJsonProvider
import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.JacksonFeature;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
public class WebApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(USOrgResource.class);
s.add(JacksonFeature.class);
s.add(SystemExceptionMapper.class);
s.add(NotFoundExceptionMapper.class);
s.add(NotAllowedExceptionMapper.class);
s.add(JacksonJaxbJsonProvider.class);
s.add(CommonResponseHeaderFilter.class);
return s;
}
}

I haven't gotten to the bottom of it yet, but when I comment out the line s.add(CommonResponseHeaderFilter.class) in WebApplication, everything works fine when I deploy to tomcat
public class CommonResponseHeaderFilter implements ContainerResponseFilter {
public CommonResponseHeaderFilter() {
}
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().add("x-content-type-options", "nosniff");
responseContext.getHeaders().add("Cache-Control", "no-cache");
responseContext.getHeaders().add("Keep-Alive", "timeout=5, max=100");
responseContext.getHeaders().add("Connection", "Keep-Alive");
responseContext.getHeaders().add("Transfer-Encoding", "chunked");
}
}

Related

java HttpRequest : java.util.concurrent.TimeoutException after multiple recursive request

I have this code, It uses the java java.net.http.HttpClient :
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class test {
private static String authorizationHeader = "Bearer ";
public static int MAX_RESEND = 5;
public static int TIME_OUT = 150000;
public static String url = "http://127.0.0.1:5000/api/test";
private static final HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofMinutes(3))
.build();
public static HTTPResponse postHttpRequest(HttpClient httpClient, String uri, String body){
HttpResponse.BodyHandler<String> handler = HttpResponse.BodyHandlers.ofString();
HttpRequest request = HttpRequest.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.timeout(Duration.ofMinutes(5))
.uri(URI.create(uri))
.header("Content-Type", "application/json")
.header("Authorization", authorizationHeader)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
CompletableFuture<HttpResponse<String>> response = httpClient.sendAsync(request, handler)
.thenComposeAsync(r -> tryResend(httpClient, request, handler, 1, r, TIME_OUT));
String getResponse = null;
Integer getResponseStatusCode = null;
try {
getResponse = response.thenApply(HttpResponse::body).get(3, TimeUnit.MINUTES);
getResponseStatusCode = response.get().statusCode();
return new HTTPResponse(getResponseStatusCode, getResponse, null) ;
} catch (Exception e) {
return new HTTPResponse(500, e.toString(), e.getMessage());
}
}
public static <T> CompletableFuture<HttpResponse<T>> tryResend(HttpClient client, HttpRequest request, HttpResponse.BodyHandler<T> handler, int count, HttpResponse<T> resp, long timeSleep) {
try {
Thread.sleep(timeSleep);
if (resp.statusCode() == 200 || count >= MAX_RESEND) {
return CompletableFuture.completedFuture(resp);
} else {
return client.sendAsync(request, handler)
.thenComposeAsync(r -> tryResend(client, request, handler, count+1, r, timeSleep));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String body = "{\"app\":\"hi\"}";
HTTPResponse hResponseOrigin = postHttpRequest( httpClient , url , body);
Integer statusCodeOriginResponse = hResponseOrigin.getStatusCode();
String msgOriginResponse = hResponseOrigin.getResponse();
}
}
class HTTPResponse {
private Integer statusCode;
private String response;
private String exception;
public HTTPResponse(Integer statusCode, String response, String exception) {
this.statusCode = statusCode;
this.response = response;
this.exception = exception;
}
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public String getException() {
return exception;
}
public void setException(String exception) {
this.exception = exception;
}
}
The code tries to do n recursive-requests when the first response of the request is not 200.
The issue always appears on the second retry.
I always get this exception:
java.util.concurrent.TimeoutException
example:
postHttpRequest ---------------------------
http://127.0.0.1:5000/api/test
tryResend ------------------------------------------------------
count zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
1
res get tryResend ----------zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxx
{"app":"hi"}
status code: 500
tryResend ------------------------------------------------------
count zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
2
res get tryResend ----------zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzxxxxxxxxxxxxxxx
{"app":"hi"}
msgOriginResponse xxxxxxxxxxxxxxxx----------------------------------------------------------------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
java.util.concurrent.TimeoutException
msgOriginResponse end xxxxxxxxxxxxxxxxx----------------------------------------------------------------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
How can I fix it?? any recomendation ?

Repository Returning Null while attempting Horizontal Scaling of a Service Class

[ISSUE] repo always returns null when I call repo methods, while stepping through, throws null pointer exception. then front end receives
500: Http failure response for http://localhost:4200/api/aiprollout/updatecsv: 500 Internal Server Error
[HAVE TRIED] Adjusting AutoWired and components and service annotations.
[QUESTIONS]
1- Does every repo method need its own service and controller method?
2- Is it okay to create a new service that uses an existing controller?
3- If this new service uses SuperCsv and I create custom CsvCellProcessors, can these cell processors also call the repo? Should these cell processors perform logic? or should it be done else where? What class annotations should these cellProcessors classes have? #Component?
Any advice is greatly appreciated, feel a little lost at this point not even sure what to do.
[CODE]
Controller:
#RestController
#EnableConfigurationProperties({SpoofingConfigurationProperties.class})
#RequestMapping(value = "")
public class AipRolloutController {
private final Logger logger = some logger
private final AipRolloutService AipRolloutService;
private final CsvParserService csvParserService;
#Autowired
public AipRolloutController(AipRolloutService aipRolloutService, CsvParserService csvParserService) {
this.AipRolloutService = aipRolloutService;
this.csvParserService = csvParserService;
}
#PostMapping(value = "/updatecsv", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> processCsv(#RequestParam("csvFile") MultipartFile csvFile) throws IOException {
if (csvFile.isEmpty()) return new ResponseEntity(
responceJson("please select a file!"),
HttpStatus.NO_CONTENT
);
csvParserService.parseCsvFile(csvFile);
return new ResponseEntity(
responceJson("Successfully uploaded - " + csvFile.getOriginalFilename()),
new HttpHeaders(),
HttpStatus.CREATED
);
}
Service:
#Service
public class AipRolloutService {
private static final Logger logger = some logger
#Autowired
private AIPRolloutRepository AIPRolloutRepository;
New Csv parser Service
#Service
public class CsvParserService {
#Autowired private AipRolloutService aipRolloutService;
public CsvParserService(AipRolloutService aipRolloutService) {
this.aipRolloutService = aipRolloutService;
}
public void parseCsvFile(MultipartFile csvFile) throws IOException {
CsvMapReader csvMapReader = new CsvMapReader(new InputStreamReader(csvFile.getInputStream()), CsvPreference.STANDARD_PREFERENCE);
parseCsv(csvMapReader);
csvMapReader.close();
}
private void parseCsv(CsvMapReader csvMapReader) throws IOException {
String[] header = csvMapReader.getHeader(true);
List<String> headers = Arrays.asList(header);
verifySourceColumn(headers);
verifyPovColumn(headers);
final CellProcessor[] processors = getProcessors(headers);
Map<String, Object> csvImportMap = null;
while ((csvImportMap = csvMapReader.read(header, processors)) != null) {
CsvImportDTO csvImportDto = new CsvImportDTO(csvImportMap);
if ( activationTypeP(csvImportDto) ){
int mssValue = Integer.parseInt(csvImportDto.getMssValue());
aipRolloutService.updateAipRollout(csvImportDto.getSource(),
csvImportDto.getPov(),
csvImportDto.getActivationType(),
mssValue);
}
}
}
private CellProcessor[] getProcessors(List<String> headers) {
CellProcessor[] processors = new CellProcessor[headers.size()];
int index = 0;
for (String header : headers) {
if (header.contains(SOURCE_ID)) {
processors[index++] = new CsvSourceIdCellParser();
} else if (header.contains(POV)) {
processors[index++] = new CsvPovCellParser();
} else if (header.contains(ACTIVATION_TYPE)) {
processors[index++] = new CsvActivationTypeCellParser();
} else if (header.contains(ACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(DEACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(MSS_VALUE)) {
processors[index++] = new CsvMssValueCellParser();
} else {
processors[index++] = null; // throw exception? wrong header info instead of allowing null?
}
}
return processors;
}
Custom Cell Processor that calls repo and returns null
public class CsvSourceIdCellParser extends CellProcessorAdaptor {
#Autowired AIPRolloutRepository aipRolloutRepository;
public CsvSourceIdCellParser(){ super(); }
// this constructor allows other processors to be chained
public CsvSourceIdCellParser(CellProcessor next){ super(next); }
#Override
public Object execute(Object value, CsvContext csvContext) {
// throws an Exception if the input is null
validateInputNotNull(value, csvContext);
// get rid of description only need first 3 #'s
value = value.toString().substring(0,3);
// check if WH exists
if( aipRolloutRepository.dcExistsInDatabase(value.toString()) )
return value;
else
throw new RuntimeException("Check Warehouse Value, Value Not Found "
+ "Row number: " + csvContext.getRowNumber()
+ " Column number: " + csvContext.getColumnNumber());
}
}
Repository
#Repository
public class AIPRolloutRepository {
private static final Logger logger = LoggerFactory.getLogger(AIPRolloutRepository.class);
#Autowired
JdbcTemplate jdbcTemplate;
public AIPRolloutRepository() {
}
public boolean dcExistsInDatabase(String dc){
// Query for a count saves time and memory, query for distinct saves time and memory on execution
boolean hasRecord =
jdbcTemplate
.query( "select count (distinct '" + dc +"')" +
"from xxcus.XX_AIP_ROLLOUT" +
"where DC = '" + dc + "';",
new Object[] { dc },
(ResultSet rs) -> {
if (rs.next()) {
return true;
}
return false;
}
);
return hasRecord;
}

User that does not have role outlined in RolesAllowed can still invoke method

I've created a custom IdentityStore, in order to intercept authentication requests and assign roles based on the user.
This is purely POC, not production worthy. And the aim is to not use any Spring methods, only standard Java API.
The problem, is that when I authenticate as user/abc123, calling the method shoppingCartService.getOrderList() does not throw an exception even though user does not have the ADMIN role .. why is that?
Custom IdentityStore:
#ApplicationScoped
public class TestIdentityStore implements IdentityStore {
public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {
if (usernamePasswordCredential.compareTo("user", "abc123")) {
return new CredentialValidationResult("user", new HashSet<>(asList("CUSTOMER")));
} else if (usernamePasswordCredential.compareTo("admin", "abc123")) {
return new CredentialValidationResult("admin", new HashSet<>(asList("ADMIN")));
}
return INVALID_RESULT;
}
}
ApplicationConfig:
#BasicAuthenticationMechanismDefinition(
realmName = "file"
)
#ApplicationScoped
#Named
public class ApplicationConfig {}
Servlet:
#WebServlet("/servlet")
#DeclareRoles({ "CUSTOMER", "ADMIN" })
#ServletSecurity(#HttpConstraint(rolesAllowed = {"CUSTOMER", "ADMIN"}))
public class Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String webName = null;
if (request.getUserPrincipal() != null) {
webName = request.getUserPrincipal().getName();
}
response.getWriter().write("web username: " + webName + "\n");
response.getWriter().write("web user has role \"CUSTOMER\": " + request.isUserInRole("CUSTOMER") + "\n");
response.getWriter().write("web user has role \"ADMIN\": " + request.isUserInRole("ADMIN") + "\n");
ShoppingCartService shoppingCartService = new ShoppingCartServiceImpl();
try {
List<OrderItem> items = shoppingCartService.getOrderList();
response.getWriter().write("shoppingCartService.getOrderList() : " + items + "\n");
} catch (Exception e) {
response.getWriter().write("web user does not have access to shoppingCartService.getOrderList();\n");
}
request.logout();
}
}
Interface:
public interface ShoppingCartService {
#RolesAllowed("ADMIN")
List<OrderItem> getOrderList();
}

Server Authentication Module forwarding in Glassfish

I found this guide for developing your own Server Authentication Module (SAM) for Glassfish: http://docs.oracle.com/cd/E18930_01/html/821-2418/gizel.html
It seems pretty straightforward to verify some credentials (in HTTP Auth headers for instance), but my question is this:
Can I develop my SAM in such a way that I can forward the user to a specific page if he's not logged in?
Here's the example from the guide:
package tip.sam;
import java.io.IOException;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.auth.message.callback.GroupPrincipalCallback;
import javax.security.auth.message.callback.PasswordValidationCallback;
import javax.security.auth.message.module.ServerAuthModule;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.util.Base64;
public class MySam implements ServerAuthModule {
protected static final Class[]
supportedMessageTypes = new Class[]{
HttpServletRequest.class,
HttpServletResponse.class
};
private MessagePolicy requestPolicy;
private MessagePolicy responsePolicy;
private CallbackHandler handler;
private Map options;
private String realmName = null;
private String defaultGroup[] = null;
private static final String REALM_PROPERTY_NAME =
"realm.name";
private static final String GROUP_PROPERTY_NAME =
"group.name";
private static final String BASIC = "Basic";
static final String AUTHORIZATION_HEADER =
"authorization";
static final String AUTHENTICATION_HEADER =
"WWW-Authenticate";
public void initialize(MessagePolicy reqPolicy,
MessagePolicy resPolicy,
CallbackHandler cBH, Map opts)
throws AuthException {
requestPolicy = reqPolicy;
responsePolicy = resPolicy;
handler = cBH;
options = opts;
if (options != null) {
realmName = (String)
options.get(REALM_PROPERTY_NAME);
if (options.containsKey(GROUP_PROPERTY_NAME)) {
defaultGroup = new String[]{(String)
options.get(GROUP_PROPERTY_NAME)};
}
}
}
public Class[] getSupportedMessageTypes() {
return supportedMessageTypes;
}
public AuthStatus validateRequest(
MessageInfo msgInfo, Subject client,
Subject server) throws AuthException {
try {
String username =
processAuthorizationToken(msgInfo, client);
if (username ==
null && requestPolicy.isMandatory()) {
return sendAuthenticateChallenge(msgInfo);
}
setAuthenticationResult(
username, client, msgInfo);
return AuthStatus.SUCCESS;
} catch (Exception e) {
AuthException ae = new AuthException();
ae.initCause(e);
throw ae;
}
}
private String processAuthorizationToken(
MessageInfo msgInfo, Subject s)
throws AuthException {
HttpServletRequest request =
(HttpServletRequest)
msgInfo.getRequestMessage();
String token =
request.getHeader(AUTHORIZATION_HEADER);
if (token != null && token.startsWith(BASIC + " ")) {
token = token.substring(6).trim();
// Decode and parse the authorization token
String decoded =
new String(Base64.decode(token.getBytes()));
int colon = decoded.indexOf(':');
if (colon <= 0 || colon == decoded.length() - 1) {
return (null);
}
String username = decoded.substring(0, colon);
// use the callback to ask the container to
// validate the password
PasswordValidationCallback pVC =
new PasswordValidationCallback(s, username,
decoded.substring(colon + 1).toCharArray());
try {
handler.handle(new Callback[]{pVC});
pVC.clearPassword();
} catch (Exception e) {
AuthException ae = new AuthException();
ae.initCause(e);
throw ae;
}
if (pVC.getResult()) {
return username;
}
}
return null;
}
private AuthStatus sendAuthenticateChallenge(
MessageInfo msgInfo) {
String realm = realmName;
// if the realm property is set use it,
// otherwise use the name of the server
// as the realm name.
if (realm == null) {
HttpServletRequest request =
(HttpServletRequest)
msgInfo.getRequestMessage();
realm = request.getServerName();
}
HttpServletResponse response =
(HttpServletResponse)
msgInfo.getResponseMessage();
String header = BASIC + " realm=\"" + realm + "\"";
response.setHeader(AUTHENTICATION_HEADER, header);
response.setStatus(
HttpServletResponse.SC_UNAUTHORIZED);
return AuthStatus.SEND_CONTINUE;
// MAYBE SOMETHING HERE?
}
public AuthStatus secureResponse(
MessageInfo msgInfo, Subject service)
throws AuthException {
return AuthStatus.SEND_SUCCESS;
}
public void cleanSubject(MessageInfo msgInfo,
Subject subject)
throws AuthException {
if (subject != null) {
subject.getPrincipals().clear();
}
}
private static final String AUTH_TYPE_INFO_KEY =
"javax.servlet.http.authType";
// distinguish the caller principal
// and assign default groups
private void setAuthenticationResult(String name,
Subject s, MessageInfo m)
throws IOException,
UnsupportedCallbackException {
handler.handle(new Callback[]{
new CallerPrincipalCallback(s, name)
});
if (name != null) {
// add the default group if the property is set
if (defaultGroup != null) {
handler.handle(new Callback[]{
new GroupPrincipalCallback(s, defaultGroup)
});
}
m.getMap().put(AUTH_TYPE_INFO_KEY, ""MySAM");
}
}
}
Yes, you can do that in the validateRequest method.
Here is a simple example:
public AuthStatus validateRequest(MessageInfo messageInfo,
Subject clientSubject,
Subject serviceSubject) throws AuthException {
// clientSubject.getPrincipals() returns the principals
// check this set to know if the user is not logged in
// if the user is not logged in do the following
HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
response.sendRedirect("login.html");
}
It might be better to do it inside of a custom LoginModule (if you already know what that is), but I guess this depends on your requirements.
See also:
LoginModule Bridge Profile (JASPIC) in glassfish
Implementing container authentication in Java EE with JASPIC
JAAS for human beings

Get the POST request body from HttpServletRequest

I am trying to get the whole body from the HttpServletRequest object.
The code I am following looks like this:
if ( request.getMethod().equals("POST") )
{
StringBuffer sb = new StringBuffer();
BufferedReader bufferedReader = null;
String content = "";
try {
//InputStream inputStream = request.getInputStream();
//inputStream.available();
//if (inputStream != null) {
bufferedReader = request.getReader() ; //new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead;
while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 ) {
sb.append(charBuffer, 0, bytesRead);
}
//} else {
// sb.append("");
//}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
test = sb.toString();
}
and I am testing the functionality with curl and wget as follows:
curl --header "MD5: abcd" -F "fileupload=#filename.txt http://localhost:8080/abcd.html"
wget --header="MD5: abcd" --post-data='{"imei":"351553012623446","hni":"310150","wdp":false}' http://localhost:8080/abcd.html"
But the while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 ) does not return anything, and so I get nothing appended on StringBuffer.
In Java 8, you can do it in a simpler and clean way :
if ("POST".equalsIgnoreCase(request.getMethod()))
{
test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
Easy way with commons-io.
IOUtils.toString(request.getReader());
https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/IOUtils.html
Be aware, that your code is quite noisy.
I know the thread is old, but a lot of people will read it anyway.
You could do the same thing using the guava library with:
if ("POST".equalsIgnoreCase(request.getMethod())) {
test = CharStreams.toString(request.getReader());
}
If all you want is the POST request body, you could use a method like this:
static String extractPostRequestBody(HttpServletRequest request) throws IOException {
if ("POST".equalsIgnoreCase(request.getMethod())) {
Scanner s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
return "";
}
Credit to: https://stackoverflow.com/a/5445161/1389219
This works for both GET and POST:
#Context
private HttpServletRequest httpRequest;
private void printRequest(HttpServletRequest httpRequest) {
System.out.println(" \n\n Headers");
Enumeration headerNames = httpRequest.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
System.out.println(headerName + " = " + httpRequest.getHeader(headerName));
}
System.out.println("\n\nParameters");
Enumeration params = httpRequest.getParameterNames();
while(params.hasMoreElements()){
String paramName = (String)params.nextElement();
System.out.println(paramName + " = " + httpRequest.getParameter(paramName));
}
System.out.println("\n\n Row data");
System.out.println(extractPostRequestBody(httpRequest));
}
static String extractPostRequestBody(HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
Scanner s = null;
try {
s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
} catch (IOException e) {
e.printStackTrace();
}
return s.hasNext() ? s.next() : "";
}
return "";
}
If the request body is empty, then it simply means that it's already been consumed beforehand. For example, by a request.getParameter(), getParameterValues() or getParameterMap() call. Just remove the lines doing those calls from your code.
This will work for all HTTP method.
public class HttpRequestWrapper extends HttpServletRequestWrapper {
private final String body;
public HttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = IOUtils.toString(request.getReader());
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getBody().getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
#Override
public boolean isFinished() {
return false;
}
#Override
public boolean isReady() {
return false;
}
#Override
public void setReadListener(ReadListener listener) {
}
};
return servletInputStream;
}
public String getBody() {
return this.body;
}
}
Easiest way I could think of:
request.getReader().lines().reduce("",String::concat)
However, this will be one long string which you will have to parse. IF you send a username of tim and a password of 12345. The output of the code above would look like this:
{ "username":"tim", "password": "12345"}
Please be aware
Please be aware that with the reduce() method we are performing a Mutable Reduction which does a great deal of string copying and has a runtime of O(N^2) with N being the number of characters. Please check the Mutable Reduction documentation if you need a more performant result.
I resolved that situation in this way. I created a util method that return a object extracted from request body, using the readValue method of ObjectMapper that is capable of receiving a Reader.
public static <T> T getBody(ResourceRequest request, Class<T> class) {
T objectFromBody = null;
try {
ObjectMapper objectMapper = new ObjectMapper();
HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(request);
objectFromBody = objectMapper.readValue(httpServletRequest.getReader(), class);
} catch (IOException ex) {
log.error("Error message", ex);
}
return objectFromBody;
}
I personnally use this code (on a dev server, not in production). Seems to work. The main difficulty is that once you read the request body, it will be lost and not transferred to the app. So you have to "cache" it first.
/* Export this filter as a jar and place it under directory ".../tomcat/lib" on your Tomcat server/
In the lib directory, also place the dependencies you need
(ex. org.apache.commons.io => commons-io-2.8.0.jar)
Once this is done, in order to activate the filter, on the Tomcat server:
o in .../tomcat/conf/server.xml, add:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" [%{postdata}r] %s %b"/>
=> the server will log the "postdata" attribute we generate in the Java code.
o in .../tomcat/conf/web.xml, add:
<filter>
<filter-name>post-data-dumper-filter</filter-name>
<filter-class>filters.PostDataDumperFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>post-data-dumper-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Once you've done this, restart your tomcat server. You will get extra infos in file "localhost_access_log.<date>.txt"
*/
package filters;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.stream.Collectors;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/* Cache the inputstream in order to read it multiple times.
*/
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/* An input stream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
//---------------------
#Override
public int read() throws IOException {
return input.read();
}
#Override
public boolean isFinished() {
return input.available() == 0;
}
#Override
public boolean isReady() {
return true;
}
//---------------------
#Override
public void setReadListener(ReadListener arg0) {
// TODO Auto-generated method stub
// Ex. : throw new RuntimeException("Not implemented");
}
}
}
public final class PostDataDumperFilter implements Filter {
private FilterConfig filterConfig = null;
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (filterConfig == null)
return;
StringBuffer output = new StringBuffer();
output.append("PostDataDumperFilter-");
/* Wrap the request in order to be able to read its body multiple times */
MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);
// TODO : test the method in order not to log the body when receiving GET/DELETE requests ?
// I finally leave it "as it", since I've seen GET requests containing bodies (hell...).
output.append("Content-type=" + multiReadRequest.getContentType());
output.append(" - HTTP Method=" + multiReadRequest.getMethod());
output.append(" - REQUEST BODY = " + multiReadRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator())));
// Log the request parameters:
Enumeration names = multiReadRequest.getParameterNames();
if (names.hasMoreElements()) {
output.append("- REQUEST PARAMS = ");
}
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
output.append(name + "=");
String values[] = multiReadRequest.getParameterValues(name);
for (int i = 0; i < values.length; i++) {
if (i > 0) output.append("' ");
output.append(values[i]);
}
if (names.hasMoreElements()) output.append("&");
}
multiReadRequest.setAttribute("postdata", output);
chain.doFilter(multiReadRequest, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
}

Categories