Unable to inject a session bean into a servlet on my bluemix app - websphere

I created a simple Java EE app in bluemix and deployed it. Then I created session bean and a servlet. I am unable to access the session bean method from Servlet.
Anybody has done this on bluemix?
here is my servlet code...
package nags.test.web;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import nags.test.ejb.NagseSessionBean;
/**
* Servlet implementation class TestServlet
*/
#WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#EJB
NagseSessionBean nagsBean;
/**
* #see HttpServlet#HttpServlet()
*/
public TestServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doService(request, response);
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doService(request, response);
}
public void doService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("TestServlet doService Begin");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML>");
out.println("<html>");
out.println("<head>");
out.println("<meta charset=\"UTF-8\">");
out.println("<title>Nags HTML from Servlet</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h1> This is Nags HTML5 PAge </h1>");
if (nagsBean == null){
System.out.println("TestServlet doService Unable to inject EJB");
}else{
System.out.println("TestServlet doService Able to inject EJB");
String helloString = nagsBean.sayHello("Nagarjun");
if (helloString == null){
out.println("<h1> No Response from Session Bean </h1>");
}else {
out.println("<h1> helloString </h1>");
}
}
out.println("</body>");
out.println("</html>");
System.out.println("TestServlet doService End");
//out.flush();
//out.close();
}
}

Your code has no errors, and trying on Bluemix works fine. I tried it using both a Stateless and a Stateful enterprise bean.
So probably there is an error with your Bean code.
To investigate the possible problem, retrieve the Bluemix logs using the "cf logs --recent" command and analyse the exception thrown.

Related

How to enable Async support for Sling Servlet - Felix

I have this code
#Component(service = Servlet.class, scope = ServiceScope.PROTOTYPE, property={
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN+"=/content/*",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED+"=true",
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED+"=true"}
)
#SlingServletResourceTypes(
resourceTypes="cq/Page",
methods=HttpConstants.METHOD_GET,
selectors = "asynctest",
extensions="json")
public class ReactiveServlet extends HttpServlet {
#Override
protected void doGet(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
AsyncContext async = request.startAsync();
// Some logic
}
}
When calling this servlet /content/mypage.asynctest.json then getting this error
java.lang.IllegalStateException: null
at org.apache.felix.http.base.internal.dispatch.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:338) [org.apache.felix.http.jetty:4.1.10]
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:369) [org.apache.felix.http.servlet-api:1.1.2]
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:369) [org.apache.felix.http.servlet-api:1.1.2]
The short answer is that async is NOT supported by Sling-Servlets. Your exception is thrown in this class:
https://github.com/apache/felix-dev/blob/master/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletRequestWrapper.java
#Override
public AsyncContext startAsync() throws IllegalStateException
{
if ( !this.asyncSupported )
{
throw new IllegalStateException();
}
return super.startAsync();
}
But you are mixing OSGi Http-Whiteboard Pattern with Sling-Servlets. I'm not sure, what you wanted to do.
Sling/AEM is a technology-stack, where layer is built on layer. Unfortunately multiple of these layers allow registering a servlet.
Sling Servlets = Apache Sling (recommended, default)
OSGi HTTP Whiteboard Pattern = Apache Felix (only for special cases)
JEE Servlet = Jetty Servlet Container (NOT recommended)
Sling-Servlet
The Sling-Servlet you registered with #SlingServletResourceTypes doesn't support async. The output of the following servlet is: Async is not supported by an Sling-Servlet! (http://localhost:4502/content/we-retail.asynctest.json)
import static org.apache.sling.api.servlets.ServletResolverConstants.*;
import javax.servlet.AsyncContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.service.component.annotations.Component;
#Component(service = Servlet.class, property = {
SLING_SERVLET_RESOURCE_TYPES + "=cq:Page",
SLING_SERVLET_SELECTORS + "=asynctest",
SLING_SERVLET_EXTENSIONS + "=json",
SLING_SERVLET_METHODS + "=GET"
})
public class AsyncSlingServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(
SlingHttpServletRequest request,
SlingHttpServletResponse response
) throws ServletException, IOException {
if (request.isAsyncSupported() /* false */) {
final AsyncContext async = request.startAsync();
async.start(() -> {
async.getResponse().setContentType("text/plain");
async.getResponse().setCharacterEncoding("utf-8");
try {
async.getResponse().getWriter().println(
"Hello from the Sling-Servlet!");
} catch (IOException e) {
// ignore
}
async.complete();
});
} else {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().println(
"Async is not supported by an Sling-Servlet!");
}
}
}
OSGi HTTP Whiteboard Pattern
The almost same servlet registered via the OSGi HTTP Whiteboard pattern supports async. It returns Hello from the OSGi Http-Whiteboard Servlet! (http://localhost:4502/my-project/hello). But such servlets are aside to Sling, so they are "rivals" or "competitors". You have to be careful, not to negatively impact Sling. So the /content path should be avoided.
import javax.servlet.AsyncContext;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
#Component(
service = Servlet.class,
scope = ServiceScope.PROTOTYPE,
property = {
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN
+ "=/my-project/*",
HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT
+ "=("
+ HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME
+ "=org.apache.sling)",
HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED
+ "=true",
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED
+ "=true" }
)
public class AsyncOSGiServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.isAsyncSupported() /* true */) {
final AsyncContext async = request.startAsync();
async.start(() -> {
async.getResponse().setContentType("text/plain");
async.getResponse().setCharacterEncoding("utf-8");
try {
async.getResponse().getWriter().println(
"Hello from the OSGi Http-Whiteboard Servlet!");
} catch (IOException e) {
// ignore
}
async.complete();
});
} else {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().println(
"Async is not supported by an OSGi Http-Whiteboard Servlet!");
}
}
}

spring boot RestController get HttpServletResponse content

I use spring boot build project, RestController return string data.
I want get response content in Filter.
But cant get, please help me.
controller:
#RestController
#RequestMapping(value = "/service/example")
public class ExampleController {
#RequestMapping(value = "/get/test", method = RequestMethod.POST)
public String message(#RequestBody String data) {
return "test";
}
#RequestMapping(value = "/get/test1", method = RequestMethod.POST)
public void message(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
writer.write("dfsfd");
writer.flush();
}
}
filter:
#WebFilter(filterName="myFilter",urlPatterns="/service/*")
public class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
MyHttpServletResponseWrapper responseWrapper = new MyHttpServletResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
String responseContent = responseWrapper.getContent();
System.out.println("response="+responseContent);
}
}
MyHttpServletResponseWrapper :
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {
private PrintWriter cachedWriter;
private CharArrayWriter bufferedWriter;
/**
* Constructs a response adaptor wrapping the given response.
*
* #param response The response to be wrapped
* #throws IllegalArgumentException if the response is null
*/
public MyHttpServletResponseWrapper(HttpServletResponse response) {
super(response);
bufferedWriter = new CharArrayWriter();
cachedWriter = new PrintWriter(bufferedWriter);
}
#Override
public PrintWriter getWriter() throws IOException {
return cachedWriter;
}
/**
* 获取原始HTML
*
* #return
*/
public String getContent() {
byte[] bytes = bufferedWriter.toString().getBytes();
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
}
post to /service/example/get/test cant get content.
but post to /service/example/get/test1 can get content.
why?
My project has many rest like /service/example/get/test, I dont want to change each one.
how to get response content in filter, please help, Thanks!!!
I created one simple spring boot project, in this project you can control which url you want to filter:
Rest service class (3 services, we will filter 2 only)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.filter.GenericFilterBean;
#SpringBootApplication
#RestController
#RequestMapping(value = "/service/example")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping(value = "/get/test", method = RequestMethod.POST)
public String message(#RequestBody String data) {
return "test";
}
#RequestMapping(value = "/get/test1", method = RequestMethod.POST)
public void message(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
writer.write("dfsfd");
writer.flush();
}
#RequestMapping(value = "/api", method = RequestMethod.POST)
public void messages(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
writer.write("dfsfd");
writer.flush();
}
#Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(myFilter());
registration.addUrlPatterns("/service/example/get/*");
registration.setOrder(1);
return registration;
}
#Bean(name = "someFilter")
public GenericFilterBean myFilter() {
return new MyFilter();
}
}
MyFilter class:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.filter.GenericFilterBean;
public class MyFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
System.out.println("Filter called");
filterChain.doFilter(servletRequest, servletResponse);
}
}
try to call 3 services:
http://localhost:8080/service/example/get/test
http://localhost:8080/service/example/get/test1
http://localhost:8080/service/example/api
and check the printed log.
you can control the url patter using this line
registration.addUrlPatterns("/service/example/get/*");
I hope this sample help you, thanks

Spring Security Oauth - Custom format for OAuth2Exceptions

The error format of spring security oauth conforms with the OAuth spec and looks like this.
{
"error":"insufficient_scope",
"error_description":"Insufficient scope for this resource",
"scope":"do.something"
}
Especially on a resource server I find it a bit strange to get a different error format for authentication issues. So I would like to change the way this exception is rendered.
The documentation says
Error handling in an Authorization Server uses standard Spring MVC
features, namely #ExceptionHandler methods
So I tried something like this to customize the format of the error:
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyErrorHandler {
#ExceptionHandler(value = {InsufficientScopeException.class})
ResponseEntity<MyErrorRepresentation> handle(RuntimeException ex, HttpServletRequest request) {
return errorResponse(HttpStatus.FORBIDDEN,
MyErrorRepresentation.builder()
.errorId("insufficient.scope")
.build(),
request);
}
}
But this does not work.
Looking at the code, all the error rendering seems to be done in DefaultWebResponseExceptionTranslator#handleOAuth2Exception. But implementing a custom WebResponseExceptionTranslator would not allow changing the format.
Any hints?
First of all,some knowledge for Spring Security OAuth2.
OAuth2 has two main parts
AuthorizationServer : /oauth/token, get token
ResourceServer : url resource priviledge management
Spring Security add filter to the filter chains of server container, so the exception of Spring Security will not reach #ControllerAdvice
Then, custom OAuth2Exceptions should consider for AuthorizationServer and ResourceServer.
This is configuration
#Configuration
#EnableAuthorizationServer
public class OAuthSecurityConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//for custom
endpoints.exceptionTranslator(new MyWebResponseExceptionTranslator());
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// format message
resources.authenticationEntryPoint(new MyAuthenticationEntryPoint());
resources.accessDeniedHandler(new MyAccessDeniedHandler());
}
}
MyWebResponseExceptionTranslator is translate the exception to ourOAuthException and we custom ourOAuthException serializer by jackson, which way is same by default the OAuth2 use.
#JsonSerialize(using = OAuth2ExceptionJackson1Serializer.class)
public class OAuth2Exception extends RuntimeException {
other custom handle class stuff
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
/**
* #author qianggetaba
* #date 2019/6/21
*/
public class MyWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
#Override
public ResponseEntity<OAuth2Exception> translate(Exception exception) throws Exception {
if (exception instanceof OAuth2Exception) {
OAuth2Exception oAuth2Exception = (OAuth2Exception) exception;
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(new CustomOauthException(oAuth2Exception.getMessage()));
}else if(exception instanceof AuthenticationException){
AuthenticationException authenticationException = (AuthenticationException) exception;
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(new CustomOauthException(authenticationException.getMessage()));
}
return ResponseEntity
.status(HttpStatus.OK)
.body(new CustomOauthException(exception.getMessage()));
}
}
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
* #author qianggetaba
* #date 2019/6/21
*/
#JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
public CustomOauthException(String msg) {
super(msg);
}
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
/**
* #author qianggetaba
* #date 2019/6/21
*/
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
public CustomOauthExceptionSerializer() {
super(CustomOauthException.class);
}
#Override
public void serialize(CustomOauthException value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField("code4444", value.getHttpErrorCode());
jsonGenerator.writeBooleanField("status", false);
jsonGenerator.writeObjectField("data", null);
jsonGenerator.writeObjectField("errors", Arrays.asList(value.getOAuth2ErrorCode(),value.getMessage()));
if (value.getAdditionalInformation()!=null) {
for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
String key = entry.getKey();
String add = entry.getValue();
jsonGenerator.writeStringField(key, add);
}
}
jsonGenerator.writeEndObject();
}
}
for custom ResourceServer exception
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* #author qianggetaba
* #date 2019/6/21
*/
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException)
throws ServletException {
Map map = new HashMap();
map.put("errorentry", "401");
map.put("message", authException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* #author qianggetaba
* #date 2019/6/21
*/
public class MyAccessDeniedHandler implements AccessDeniedHandler{
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
Map map = new HashMap();
map.put("errorauth", "400");
map.put("message", accessDeniedException.getMessage());
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
try {
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.getOutputStream(), map);
} catch (Exception e) {
throw new ServletException();
}
}
}
I found a similar question with answers that really helped my solving this - Handle spring security authentication exceptions with #ExceptionHandler
But my question is specifically about spring-security-oauth2 - so I think it is still worth stating the answer specific to spring-security-oauth2. My solution was picked from different answers to the question mentioned above.
My samples work for spring-security-oauth2 2.0.13
So the solution for me to achieve a different custom error structure for oauth2 errors on resource server resources was to register a custom OAuth2AuthenticationEntryPoint and OAuth2AccessDeniedHandler that I register using a ResourceServerConfigurerAdapter. It is worth mentioning that this is only changing the format for ResourceServer endpoints - and not the AuthorizationServer endpoints like the TokenEndpoint.
class MyCustomOauthErrorConversionConfigurerAdapter extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer configurer) throws Exception {
configurer.authenticationEntryPoint(new MyCustomOauthErrorOAuth2AuthenticationEntryPoint());
configurer.accessDeniedHandler(new MyCustomOauthErrorOAuth2AccessDeniedHandler());
}
}
I could not reuse the functionality in OAuth2AuthenticationEntryPoint and OAuth2AccessDeniedHandler because the relevant methods translate the exception and flush it in the same method. So I needed to copy some code:
public class MyCustomOauthErrorOAuth2AccessDeniedHandler extends OAuth2AccessDeniedHandler {
private final MyCustomOauthErrorOAuth2SecurityExceptionHandler oAuth2SecurityExceptionHandler = new MyCustomOauthErrorOAuth2SecurityExceptionHandler();
/**
* Does exactly what OAuth2AccessDeniedHandler does only that the body is transformed to {#link MyCustomOauthError} before rendering the exception
*/
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, org.springframework.security.access.AccessDeniedException authException)
throws IOException, ServletException {
oAuth2SecurityExceptionHandler.handle(request, response, authException, this::enhanceResponse);
}
}
public class ExceptionMessageOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
private final MyCustomOauthErrorOAuth2SecurityExceptionHandler oAuth2SecurityExceptionHandler = new MyCustomOauthErrorOAuth2SecurityExceptionHandler();
/**
* Does exactly what OAuth2AuthenticationEntryPoint does only that the body is transformed to {#link MyCustomOauthError} before rendering the exception
*/
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
oAuth2SecurityExceptionHandler.handle(request, response, authException, this::enhanceResponse);
}
}
#RequiredArgsConstructor
public class MyCustomOauthErrorOAuth2SecurityExceptionHandler {
private final WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();
private final OAuth2ExceptionRenderer exceptionRenderer = new DefaultOAuth2ExceptionRenderer();
private final HandlerExceptionResolver handlerExceptionResolver = new DefaultHandlerExceptionResolver();
/**
* This is basically what {#link org.springframework.security.oauth2.provider.error.AbstractOAuth2SecurityExceptionHandler#doHandle(HttpServletRequest, HttpServletResponse, Exception)} does.
*/
public void handle(HttpServletRequest request, HttpServletResponse response, RuntimeException authException,
BiFunction<ResponseEntity<OAuth2Exception>, Exception, ResponseEntity<OAuth2Exception>> oauthExceptionEnhancer)
throws IOException, ServletException {
try {
ResponseEntity<OAuth2Exception> defaultErrorResponse = exceptionTranslator.translate(authException);
defaultErrorResponse = oauthExceptionEnhancer.apply(defaultErrorResponse, authException);
//this is the actual translation of the error
final MyCustomOauthError customErrorPayload =
MyCustomOauthError.builder()
.errorId(defaultErrorResponse.getBody().getOAuth2ErrorCode())
.message(defaultErrorResponse.getBody().getMessage())
.details(defaultErrorResponse.getBody().getAdditionalInformation() == null ? emptyMap() : defaultErrorResponse.getBody().getAdditionalInformation())
.build();
final ResponseEntity<MyCustomOauthError> responseEntity = new ResponseEntity<>(customErrorPayload, defaultErrorResponse.getHeaders(), defaultErrorResponse.getStatusCode());
exceptionRenderer.handleHttpEntityResponse(responseEntity, new ServletWebRequest(request, response));
response.flushBuffer();
} catch (ServletException e) {
// Re-use some of the default Spring dispatcher behaviour - the exception came from the filter chain and
// not from an MVC handler so it won't be caught by the dispatcher (even if there is one)
if (handlerExceptionResolver.resolveException(request, response, this, e) == null) {
throw e;
}
} catch (IOException | RuntimeException e) {
throw e;
} catch (Exception e) {
// Wrap other Exceptions. These are not expected to happen
throw new RuntimeException(e);
}
}
}
Spring Boot version: 2.2.5
You really don't have to write that much code. All you need to do create a custom AuthenticationEntryPoint by extending OAuth2AuthenticationEntryPoint, override enhanceResponse method of it and register it via Resource Server configuration.
First part:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer config) {
config.authenticationEntryPoint(new CustomOauth2AuthenticationEntryPoint());
}
}
Second part:
public class CustomOauth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
#Override
protected ResponseEntity<String> enhanceResponse(ResponseEntity<?> response, Exception exception) {
return ResponseEntity.status(response.getStatusCode()).body("My custom response body.");
}
}
Keep in mind that according to spec 401 response must send WWW-Authenticate header. The enhanceResponse that we override sends that header. Take a look at the implementation and send that header if you return 401.
You have to setAuthenticationEntryPoint in TokenEndpointAuthenticationFilter Bean if you config by AuthorizationServer
#Bean
public TokenEndpointAuthenticationFilter tokenEndpointAuthenticationFilter() {
CustomOauth2AuthenticationEntryPoint entryPoint = new CustomOauth2AuthenticationEntryPoint();
TokenEndpointAuthenticationFilter filter = new TokenEndpointAuthenticationFilter(authenticationManager, requestFactory());
filter.setAuthenticationEntryPoint(entryPoint);
return filter;
}

Flash Attribute in custom AuthenticationFailureHandler

On login failure I want to redirect the user to an error page and display a meaningful error message. Is it possible to add Flash Attributes that will be passed to the subsequent request?
The code presented below doesn't work. RequestContextUtils.getOutputFlashMap() returns null.
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler
{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException
{
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
if (outputFlashMap != null)
{
outputFlashMap.put("error", "Error message");
}
response.sendRedirect(request.getContextPath() + "/error");
}
}
I encountered this same problem with Spring 4.3.17 and finally found a solution by stepping through the spring-webmvc code and making educated guesses about how to integrate Flash Attributes outside the normal framework. SessionFlashMapManager is the key to getting this to work. I believe this method should work for Spring 3.1.1+.
package org.myorg.spring.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.support.SessionFlashMapManager;
#Component
public final class FlashAuthenticationFailureHandler implements AuthenticationFailureHandler
{
/**
* Flash attribute name to save on redirect.
*/
public static final String AUTHENTICATION_MESSAGE = "FLASH_AUTHENTICATION_MESSAGE";
public FlashAuthenticationFailureHandler()
{
return;
}
#Override
public void onAuthenticationFailure(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException exception) throws IOException, ServletException
{
if (exception != null)
{
final FlashMap flashMap = new FlashMap();
// Don't send the AuthenticationException object itself because it has no default constructor and cannot be re-instantiated.
flashMap.put(AUTHENTICATION_MESSAGE, exception.getMessage());
final FlashMapManager flashMapManager = new SessionFlashMapManager();
flashMapManager.saveOutputFlashMap(flashMap, request, response);
}
response.sendRedirect(request.getHeader("referer"));
return;
}
}
Then in the controller(s) that requires the flash attribute, simply add a ModelAttribute with the same name:
#RequestMapping(value = {"/someview"}, method = RequestMethod.GET)
public String getSomePage(final Authentication authentication, #ModelAttribute(FlashAuthenticationFailureHandler.AUTHENTICATION_MESSAGE) final String authenticationMessage, final Model model) throws Exception
{
if (authenticationMessage != null)
{
model.addAttribute("loginMessage", authenticationMessage);
}
return "myviewname";
}
Then the page attribute containing the message can be accessed in your JSP as follows:
<c:if test="${not empty loginMessage}">
<div class="alert alert-danger"><c:out value="${loginMessage}" /></div>
</c:if>
I'd guess it's null because you are calling the function from the filter chain, whereas the flash map is maintained by Spring's DispatcherServlet which the request hasn't passed through at this point.
Why not just use a parameter? i.e
response.sendRedirect(request.getContextPath()+"/error?error=" + "Error Message");

Spring exhausting inputstream of the request

I am trying to pass a String array from a Web Service to a Spring web application.
The Web Service code is :
/**
*
*/
package lnt.remote.ws;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* #author 298790
*
* This class is a JAX-WS end-point implementation and contains
* method(s) to fire batch jobs pertaining to reports
*/
#WebService
public class BatchJobWS {
private static String remoteAppURL;
private static Logger log = LoggerFactory.getLogger(Constants.WS_LOGGER);
static {
try {
Properties props = new Properties();
props.load(BatchJobWS.class.getResourceAsStream("/url.properties"));
remoteAppURL = props.getProperty(Constants.REMOTE_APP_URL);
log.info("In BatchJobWS , remote app. url is {}", remoteAppURL);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
log.error("FileNotFoundException in static block of BatchJobWS", e);
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
log.error("IOException in static block of BatchJobWS", e);
}
}
#WebMethod
public String[] generateReportBatchJob(String... params) {
HttpURLConnection httpConn;
URL remotePayrollUrl = null;
ObjectOutputStream oos = null;
String[] returnValues = null;
log.info("In BatchJobWS.generateReportBatchJob(...),params = {}",
params);
if (params == null || params.length == 0) {
return null;
}
try {
remotePayrollUrl = new URL(remoteAppURL);
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
// e1.printStackTrace();
log.error(
"MalformedURLException in BatchJobWS.generateReportBatchJob(...)",
e1);
}
/*
* Give some thought to which exception(s) be handled and which must be
* thrown
*/
try {
httpConn = (HttpURLConnection) remotePayrollUrl.openConnection();
httpConn.setDoOutput(true);
httpConn.setUseCaches(false);
oos = new ObjectOutputStream(httpConn.getOutputStream());
log.info("Writing params to the outputstream");
oos.writeObject(params);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(
httpConn.getInputStream());
Object returnParams = ois.readObject();
log.info("Reading params from the inputstream");
if (returnParams.getClass().isArray()) {
returnValues = (String[]) returnParams;
}
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
log.error("IOException in BatchJobWS.generateReportBatchJob(...)",
e);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
log.error(
"ClassNotFoundException in BatchJobWS.generateReportBatchJob(...)",
e);
}
log.info(
"Returning from BatchJobWS.generateReportBatchJob(...),returnValues = {}",
returnValues);
return returnValues;
}
}
Initially, on the web application side, I had written a plain-old servlet as shown below :
package lnt.remote;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lnt.service.ReportService;
import lnt.utilities.BatchJobService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Servlet implementation class RemoteCallInterceptor
*/
public class RemoteCallInterceptor extends HttpServlet {
private static final long serialVersionUID = 1L;
private static Logger log = LoggerFactory
.getLogger(RemoteCallInterceptor.class);
/**
* #see HttpServlet#HttpServlet()
*/
public RemoteCallInterceptor() {
// super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
log.info("In Target Payroll. RemoteCallInterceptor.doGet()");
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
log.info(
"In Target Payroll. RemoteCallInterceptor.doPost(),reportService = {}",
reportService);
BatchJobService BatchJobService = new BatchJobService();
BatchJobService.runBatchJob(request, response);
}
}
I wrote a new class BatchJobService that calls a few existing Spring beans which , in turn, have multiple Spring beans injected using #Autowire. Hence, the code in BatchJobService(which is not a Spring-managed component) was failing with NullPointerException(asthe beans were not getting injected).
Hence, to ‘inject’ BatchJobService(thereby, injecting the beans needed in BatchJobService ) in RemoteCallInterceptor, I made the latter a Spring Controller(using #Controller) and modified the doPost(…) as shown :
package lnt.remote;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lnt.service.ReportService;
import lnt.utilities.BatchJobService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Servlet implementation class RemoteCallInterceptor
*/
#Controller
public class RemoteCallInterceptor extends HttpServlet {
private static final long serialVersionUID = 1L;
private static Logger log = LoggerFactory
.getLogger(RemoteCallInterceptor.class);
#Autowired
#Qualifier("ReportService")
ReportService reportService;
/**
* #see HttpServlet#HttpServlet()
*/
public RemoteCallInterceptor() {
// super();
// TODO Auto-generated constructor stub
}
/**
* #see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
log.info("In Target Payroll. RemoteCallInterceptor.doGet()");
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
#RequestMapping(value = "/RemoteCallInterceptor.do", method = RequestMethod.POST)
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
log.info(
"In Target Payroll. RemoteCallInterceptor.doPost(),reportService = {}",
reportService);
BatchJobService BatchJobService = new BatchJobService();
BatchJobService.runBatchJob(request, response);
}
}
But now the issue is that the code in BatchJobService that reads the object(String array written by the Web Service) from the input stream gets an EOFException.
I suppose the #RequestMapping thing caused the input stream to be consumed - is my assumption correct ? If not, how should I retrieve the String [] params – which is neither a parameter nor an attribute, in web application? If yes, what can be the work-around?
I suspect that it's broken because the Spring MVC application is broken, and that your WS client is being sent an error response. Your BatchJobWS isn't checking the HTTP response code, and is just assuming everything's fine. It's not surprising that it gets an exception.
You need to do two things. Firstly, add an explicit response status check to BatchJobWS, e.g.
HttpURLConnection httpConn;
...
oos.writeObject(params);
oos.flush();
oos.close();
if (httpConn.getResponseCode() != 200) {
// error - throw an exception, or whatever
}
Secondly, there's no point in annotating an HttpServlet with #Controller - use one or the other, not both. Remove the extends HttpServlet and make doPost public. The protected may be what's causing the error.

Resources