How to properly extend WebAuthenticationDetails? - java

Building Spring Boot application, deploying(by copying to webapps folder while Tomcat is down) to local Tomcat8. Always get an error:
No thread-bound request found:
Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread?
If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet:
In this case, use RequestContextListener or RequestContextFilter to expose the current request.
As I understand, problems are while instantiating bean with WebAuthenticationDetails extending:
#Component
public class AuthDetails extends WebAuthenticationDetails{
private final AuthTarget authTarget;
public AuthDetails(HttpServletRequest request) {
super(request);
this.authTarget = AuthTarget.valueOf(request.getParameter("target"));
}
public AuthTarget getAuthTarget(){
return this.authTarget;
}
}
It cannot provide HttpServletRequest for bean constructing, but I don't know how to evade it.
Tried to add RequestContextListener, in xml or as implementation(and marking as #WebListener), no effect.
Out of ideas, how to fix it. Tried example from here: https://github.com/Baeldung/spring-security-registration , no changes - same error at the similar place.
Any help is greatly welcome.

The key was simple: I should define filter bean explicitly in security configuration extends WebSecurityConfigurerAdapter:
#Bean
AuthFilter authFilter() throws Exception{
AuthFilter authFilter = new AuthFilter();
return authFilter;
}
instead of autowiring it.

Related

get HttpServletRequest in Spring MVC?

I am using Spring MVC. I am not using Spring boot dependencies.
I created the AuthenticationSuccessEventListener class, this class implements the ApplicationListener interface.
How do I get the HttpServletRequest in the AuthenticationSuccessEventListener class? I tried many options, but I still could not find the answer to my question.
When trying to get the HttpServletRequest, I get an exception.
Message: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Who can, tell me please, what am I doing wrong?
#Component
public class AuthenticationFailureListener implements ApplicationListener<AuthenticationSuccessEvent> {
// This option doesn't work.
// #Autowired
// HttpServletRequest httpServletRequest;
// This option doesn't work.
// HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
#Override
public void onApplicationEvent(AuthenticationSuccessEvent a) {
}
}

Accessing a request scoped bean in a different thread (that handles websocket traffic)

I am having issues accessing a bean that is defined with a request centric scope in a thread that is not the request's thread.
My scenario is as follows:
Execution starts from a REST request coming from a client. In this request I define a bean that allows me to access data in a DB. The location of the DB depends on the user performing the request itself, hence why the bean with which the db is accessed is bound to the request itself. I get the user details from the request's auth and use them to init the bean.
During the HTTP request the code may call to an external service over a websocket connection. The ws traffic is handled by different StompFrameHandler classes. When these handle traffic they do so on a dedicated thread, which is not the same as the initial http request (and rightfully so!).
Some of these StompFrameHandler classes need to access the DB relevant to the user in context for the current (REST) request.
At 3 is where I encounter the issue:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I understand what the error is telling me and the fact that due to how I have defined my bean (request scope bound) spring does not allow me to access it in other threads. However I still need to use that db accessing bean from the ws traffic handling thread.
Here is a simplified version of my code:
The bean configuration:
#Configuration
public class DbClientRequestScopeConfiguration {
private DbClientFactoryI dbClientFactory;
private AuthenticationFacadeI authenticatedUserInfo;
#Autowired
public DbClientRequestScopeConfiguration(DbClientFactoryI dbClientFactory, AuthenticationFacadeI authenticatedUserInfo) {
this.dbClientFactory = dbClientFactory;
this.authenticatedUserInfo = authenticatedUserInfo;
}
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public DbClientI getDbClient() {
Authentication auth = authenticatedUserInfo.getAuthentication();
return dbClientFactory.getDbClient(auth.getDetails());
}
}
The DataService using the bean (this runs in the request's thread)
#Service
public class DataService {
private DbClientI dbClient;
#Autowired
public DataService(DbClientI dbClient) {
this.dbClient = dbClient;
}
...
}
The frame handler working with the WS traffic Note that this in not initialised by Spring's context, instead it is manually initialised by a class running in the request's thread, which gives it the instance of the #DataService coming from the Context.
public class SaveDataFrameHandler implements StompFrameHandler{
private DataService dataService;
public SaveDataFrameHandler(DataService dataService) {
this.dataService = dataService;
}
#Override
public Type getPayloadType(StompHeaders headers) {
return JsonNode.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
// This method will be called on a separate thread
JsonNode jsonPayload = (JsonNode) payload;
dataService.saveRecord(jsonPayload);
}
}
I am looking for suggestions on how I could actually use that bean in my ws thread or how to rearchitect the solution so that I do not run into this problem.
Thanks in advance!
UPDATE:
I have managed to get around the problem for now even though I am not 100% happy with the solution. To avoid repetition, I posted my current solution in a new question, since I am facing a different issue now still related to this code:
Proxied prototype bean is created every time a method is invoked from it

Wildfly Swarm RESTeasy hides webapp/index.html

I am working on a project based on Wildfly Swarm. The problem I currently have is that RESTeasy hides my index.html (and other html files) which are placed below /webapp since RESTeasy is listening on root level.
My Main Application:
#ApplicationPath("/")
public class XYZnlineApplication extends Application {
}
One of my resources:
#Path("protected/api/admin")
public class AdminResource {
#GET
#Path("public/api/offer/reduced")
#Produces("application/json")
public List<XYZ> getXYZ() {
...
}
#GET
#Path("protected/api/offer/full")
#Produces("application/json")
public List<XYZ> getAllXYZ() {
...
}
}
The thing is. If I start my wildfly swarm app and access one of the restendpoint above, everything works fine (e.g. http://localhost:8080/app/public/api/offer/reduced)
But if I d'like to access one of my html (e.g. login.html) files which are directly below /webapp, I get a 404 although the file is bundled correctly (e.g. on trying to access http://localhost:8080/app/login.html). So in my opinion what happens is that RESTeasy hides this html file cause it listens on root (/).
Since the first part of my url is the context (which is injected by a proxy) I can't set anything else than root (/) as ApplicationPath in my XYZApplication.
Do you have any idea on how I could solve this issue?
Thanks a lot in advance for your help.
You'll need to change the ApplicationPath to be something like "/services" or "/svc" or whatever works for you. Ultimately you need to partition the URL namespace between static resources and services. You don't need to worry about the context when specifying the ApplicationPath.
Major Edit:
Your comment really explains what's going on. I'm not sure what type of security you're using exactly but ultimately you likely need to have a filter of some sort in front of your services. I would have something like:
#Provider
#Priority(Priorities.AUTHENTICATION)
#PreMatching
public class AuthFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest httpServletRequest;
#Context
private HttpServletResponse httpServletResponse;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if( containerRequestContext.getUriInfo().getPath().contains("/public/") )
return; // user is in public area - doesn't matter if they are authenticated
// guess at how to check if user is authenticated
if( httpServletRequest.getSession().get("user_is_ok") )
return;
httpServletResponse.sendRedirect("/login");
// or maybe
httpServletResponse.sendError(SC_UNAUTHORIZED);
}
}
Again, this is a bit of a guess but this is a pretty common way of handling your challenge.

Spring Websocket getServerName in service

I'm implementing routing datasources in my Spring application. One of method to get lookup key is accessing HttpServletRequest and getting current domain as key by invoking method
request.getServerName()
HttpServletRequest is accessing via RequestContextHolder as below
(ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest()
or just by autowiring HttpServletRequest
#Autowired
private HttpServletRequest request;
Everything is working correctly when I'm in request scope, using standard #RequestMapping annotation in controller.
I want to do same thing in WebSocket method annotated by #MessageMapping. There is no request scope so Autowiring HttpServletRequest and invoking methot getServerName() gives exception as below
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
Message is passed to controller using Stomp and SockJs. Here is my configuration
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub");
config.setApplicationDestinationPrefixes("/some");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
And Controller
#Autowired
private SomeService service;
#MessageMapping("/someUrl/{param}")
public void subscribeForMessage(#DestinationVariable Long param) {
service.doSmth();
}
I do not want to pass it from controller to service because invocation chain is much longer and getting current lookup key have to be transparent.
It is possible to get server name in a way similiar to Autowiring HttpServletRequest? It is possible to inject some "scope" of websocket request or session without passing it ?
You have to initialize bean of RequestContextListener to request object other than #Controller ( Request scope).
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}

Overriding RolesAllowedDynamicFeature in Jersey 2.5.1

Short and sweet:
I want to be able to filter incoming requests to authenticate the user then take the roles defined in my database and use them in the Jersey 2.5.1 Service classes.
e.g.
#RolesAllowed("Custom1", "Custom2")
#Post
.....
Currently I have the following, which seems to work with the basic #PermitAll and #DenyAll annotations, I am just not sure how to overload/what to overload to get some custom code working with Jersey 2.5.1. I've seen examples for Jersey1. Should I just create a request Filter and do it in there and set the securityContext? Any help would be appreciated.
public class TestApi extends ResourceConfig {
public TestApi() {
super(AuthenticateResource.class);
register(RolesAllowedDynamicFeature.class);
}
}
Figured out my problem. Injected Resource Info then pulled out the annotation. This works if it's not pre-matching
#Context
private ResourceInfo resourceInfo;
Annotation[] annotations = resourceInfo.getResourceMethod().getDeclaredAnnotations();
SecurityContext is either set by the underlying container or it's set manually in your application (usually in ContainerRequestFilter). If your container is sophisticated enough to set the correct security context for you (with correct principal) you can go this way. Otherwise create a custom ContainerRequestFilter similar to the one in Jersey example ContainerAuthFilter.

Categories

Resources