I am using RequestFactory with GWT. It all working fine. I have a RequestContext interface that point to my DAO methodes.
Now I want to implement some kind of security check before calling the DAO. The first thing that comes to my mind is to use a FrontController and centralize the security in it, but I don't know to implement it with the RequestFactory. Any thought ?
If you want to test whether the user is authenticated, you can use a servlet filter on the server-side and a custom RequestTransport on the client-side. See the guice-rf-activity archetype at https://github.com/tbroyer/gwt-maven-archetypes for an example.
You can also check on the method-level by using a custom ServiceLayerDecorator and implementing the invoke method, calling report() when the user isn't authorized/authenticated (and handling the onFailure on the client-side). I implemented such a thing, that authorized the user based on #RolesAllowed annotations on the service method or class: https://gist.github.com/tbroyer/6091533
Here's how I implemented the security checking:
On the server side I check to see that every RequestFactory request is associated with a user who has previously logged in. To do this, the web.xml file (in the war/WEB-INF directory) must have a mapping for the servlet class. Here's the entry from the web.xml file:
<servlet>
<servlet-name>requestFactoryServlet</servlet-name>
<servlet-class>org.greatlogic.rfexample2.server.RFERequestFactoryServlet</servlet-class>
<init-param>
<param-name>symbolMapsDirectory</param-name>
<param-value>WEB-INF/classes/symbolMaps/</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>requestFactoryServlet</servlet-name>
<url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
The RFERequestFactoryServlet class contains the following code:
public class RFERequestFactoryServlet extends RequestFactoryServlet {
#Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws IOException, ServletException {
if (!userIsLoggedIn(request)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
else {
super.doPost(request, response);
}
}
private boolean userIsLoggedIn(final HttpServletRequest request) {
boolean result = false;
HttpSession session = request.getSession();
if (session != null) {
User user = (User)session.getAttribute("User");
result = user != null;
}
return result;
}
}
On the client side I needed to intercept every RequestFactory response to check for the SC_UNAUTHORIZED error. You have to tell the RequestFactory object to use a specific RequestTransport in the RequestFactory#initialize invocation, like this:
MyRequestFactory requestFactory = GWT.create(MyRequestFactory.class);
requestFactory.initialize(eventBus, new RFERequestTransport());
My RFERequestTransport class extends the DefaultRequestTransport class:
public class RFERequestTransport extends DefaultRequestTransport {
private final class RFERequestCallback implements RequestCallback {
private RequestCallback _requestCallback;
private RFERequestCallback(final RequestCallback requestCallback) {
_requestCallback = requestCallback;
}
#Override
public void onError(final Request request, final Throwable exception) {
_requestCallback.onError(request, exception);
}
#Override
public void onResponseReceived(final Request request, final Response response) {
if (response.getStatusCode() == Response.SC_UNAUTHORIZED) {
// the login processing goes here
}
else {
_requestCallback.onResponseReceived(request, response);
}
}
} // end of the RFERequestCallback class
#Override
protected RequestCallback createRequestCallback(final TransportReceiver receiver) {
return new RFERequestCallback(super.createRequestCallback(receiver));
}
}
When RequestFactory creates a request callback it calls my method, which creates my own version of a RequestCallback. If the user is logged in (as determined by the servlet) then it just performs the normal RequestFactory processing; otherwise, I go through the login process with the user. Part of the login process involves communication with the server to verify the login ... if the login is successful then I create an object on the server and store a reference to it in the "User" attribute - this is then checked in the userIsLoggedIn method in the servlet class.
Setup a filter in your web.xml so as every RF request is filtered to validate the session.
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>my.namespace.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
Here you have an example class, checking if a certain parameter is in session which could be set in the login process to your app, this is just an example, you could use your own mechanism.
public class AuthFilter implements Filter {
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
if (req.getSession().getAttribute("VALID_SESSION") == null) {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
if (null != filterChain) {
filterChain.doFilter(req, resp);
}
}
}
Related
I have a REST endpoint implemented with Spring MVC #RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.
Is it possible with Spring MVC #RestController and if so, could you please show an example ?
Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");
Something like:
#RestController
public class FooController {
#RequestMapping("/foo")
void handleFoo(HttpServletResponse response) throws IOException {
response.sendRedirect("some-url");
}
}
To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.
Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:
#RestController
public class FooController {
#RequestMapping("/foo")
public RedirectView handleFoo() {
return new RedirectView("some-url");
}
}
redirect means http code 302, which means Found in springMVC.
Here is an util method, which could be placed in some kind of BaseController:
protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
response.sendRedirect(url);
return null;
}
But sometimes might want to return http code 301 instead, which means moved permanently.
In that case, here is the util method:
protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.
Using a ControllerAdvice
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {
NotLoggedInException.class
})
protected ResponseEntity<Object> handleNotLoggedIn(
final NotLoggedInException ex, final WebRequest request
) {
final String bodyOfResponse = ex.getMessage();
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", ex.getRedirectUri());
return handleExceptionInternal(
ex, bodyOfResponse,
headers, HttpStatus.FOUND, request
);
}
}
The exception class in my case:
#Getter
public class NotLoggedInException extends RuntimeException {
private static final long serialVersionUID = -4900004519786666447L;
String redirectUri;
public NotLoggedInException(final String message, final String uri) {
super(message);
redirectUri = uri;
}
}
And I trigger it like this:
if (null == remoteUser)
throw new NotLoggedInException("please log in", LOGIN_URL);
if you #RestController returns an String you can use something like this
return "redirect:/other/controller/";
and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse
Is it possible to resend a RequestFactory transmission? I'd like to do the equivalent of this: How to resend a GWT RPC request when using RequestFactory. It is fairly simple to resend the same payload from a previous request, but I also need to place a call to the same method. Here's my RequestTransport class, and I am hoping to just "refire" the original request after taking care of, in this case, a request to the user for login credentials:
package org.greatlogic.rfexample2.client;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.web.bindery.requestfactory.gwt.client.DefaultRequestTransport;
/**
* Every request factory transmission will pass through the single instance of this class. This can
* be used to ensure that when a response is received any global conditions (e.g., the user is no
* longer logged in) can be handled in a consistent manner.
*/
public class RFERequestTransport extends DefaultRequestTransport {
//--------------------------------------------------------------------------------------------------
private IClientFactory _clientFactory;
//==================================================================================================
private final class RFERequestCallback implements RequestCallback {
private RequestCallback _requestCallback;
private RFERequestCallback(final RequestCallback requestCallback) {
_requestCallback = requestCallback;
} // RFERequestCallback()
#Override
public void onError(final Request request, final Throwable exception) {
_requestCallback.onError(request, exception);
} // onError()
#Override
public void onResponseReceived(final Request request, final Response response) {
if (response.getStatusCode() == Response.SC_UNAUTHORIZED) {
_clientFactory.login();
}
else {
_clientFactory.setLastPayload(null);
_clientFactory.setLastReceiver(null);
_requestCallback.onResponseReceived(request, response);
}
} // onResponseReceived()
} // class RFERequestCallback
//==================================================================================================
#Override
protected void configureRequestBuilder(final RequestBuilder builder) {
super.configureRequestBuilder(builder);
} // configureRequestBuilder()
//--------------------------------------------------------------------------------------------------
#Override
protected RequestCallback createRequestCallback(final TransportReceiver receiver) {
return new RFERequestCallback(super.createRequestCallback(receiver));
} // createRequestCallback()
//--------------------------------------------------------------------------------------------------
void initialize(final IClientFactory clientFactory) {
_clientFactory = clientFactory;
} // initialize()
//--------------------------------------------------------------------------------------------------
#Override
public void send(final String payload, final TransportReceiver receiver) {
String actualPayload = _clientFactory.getLastPayload();
TransportReceiver actualReceiver;
if (actualPayload == null) {
actualPayload = payload;
actualReceiver = receiver;
_clientFactory.setLastPayload(payload);
_clientFactory.setLastReceiver(receiver);
}
else {
actualReceiver = _clientFactory.getLastReceiver();
}
super.send(actualPayload, actualReceiver);
} // send()
//--------------------------------------------------------------------------------------------------
}
Based upon Thomas' suggestion I tried sending another request, and just replaced the payload and receiver in the RequestTransport.send() method, and this worked; I guess there is no further context retained by request factory, and that the response from the server is sufficient for RF to determine what needs to be done to unpack the response beyond the request and response that are returned to the RequestCallback.onResponseReceived() method. If anyone is interested in seeing my code then just let me know and I'll post it here.
It's possible, but you have a lot to do.
I had the same idea. And i was searching for a good solution for about 2 days. I tried to intercept the server call on RequestContext.java and on other classes. But if you do that you have to make your own implementation for nearly every class of gwt requestfactories. So i decided to go a much simpler approach.
Everywhere where I fired a Request, i handled the response and fired it again.
Of course you have to take care, that you don't get in to a loop.
I need to read data from an xml file that is under the WAR directory.
I'm using RequestBuilder for creating the GET request.
It looks like this:
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET,"customerRecord.xml");
try {
requestBuilder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
requestFailed(exception);
}
public void onResponseReceived(Request request,Response response) {
renderXML(response.getText());
}
});
} catch (RequestException ex) {
requestFailed(ex);
}
Now, the thing is that I don't want to load all of the data. I want to send a parameter that tells the server which part to bring, (let's say - how many lines of data) and then override the doGet method of the servlet and deal with the parameter.
I have 2 questions:
1) how do I declare the path of the servlet? where is the connection between the servlet and the request??
2) What do I write in the url of the RequestBuilder (instead of "customerRecord.xml")? do I need to refer to the servlet there or I can keep it like
May be You mean GWT Service?
You need to create 2 interfaces - Service and ServiceAsync and implementation of Service in server package (on same level as client package). Then You define implementation as servlet (in my JBoss 7.1 it just annotation. in older version servlet mapping):
#WebServlet(name="YourService", urlPatterns={"/%module%/YourService"})
public class YourServiceImpl extends RemoteServiceServlet implements YourService
in Your modeule.xml write:
<servlet path="/YourService" class="org.name.YourServiceImpl"/>
and in the end You can call this service from Your code
YourService.App.getInstance().getSomething(new AsyncCallback<Collection<Something>>() {
#Override
public void onFailure(Throwable caught) {
new MessagePopup("Error: " + caught.getMessage()).center();
}
#Override
public void onSuccess(Collection<Something> result) {
}
});
Interfaces You can create from Your beloved IDE. It's much simpler)
One think which still bothering me - I cannot specify path for servlet in another module.
I created a app with GWT+requestfacotry(MVP)+GAE. There are some service or method exposed to GWT client ,such as
1.create
2.remove
3.query
I want to add authorization function to "create" and "remove" ,but not to "query".
I did it with servlet filter :
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
UserService userService = UserServiceFactory.getUserService();
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (!userService.isUserLoggedIn()) {
response.setHeader("login", userService.createLoginURL(request.getHeader("pageurl")));
// response.setHeader("login", userService.createLoginURL(request.getRequestURI()));
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
filterChain.doFilter(request, response);
}
My question is how to identify what request (I mean the request will route to which class and service )coming in ? There are some head fields contain the module name ,but I don't it is the security way to do.
Is it possible to get RequestFacotry relevant class from http request ?
Thanks
It's hard to do this within the servlet-filter. Instead you can provide a custom decorator within the RF ServiceLayerDecorator chain. Implementation can looks like this:
import com.google.web.bindery.requestfactory.server.ServiceLayerDecorator;
public class SecurityDecorator extends ServiceLayerDecorator {
#Override
public Object invoke( Method domainMethod, Object... args ) {
if ( !isAllowed( domainMethod) ) {
handleSecurityViolation();
}
return super.invoke( domainMethod, args );
}
}
To register the additional decorator, provide a custom RF servlet:
import com.google.web.bindery.requestfactory.server.RequestFactoryServlet;
public class SecurityAwareRequestFactoryServlet extends RequestFactoryServlet {
public SecurityAwareRequestFactoryServlet() {
super( new DefaultExceptionHandler(), new SecurityDecorator() );
}
}
and register it in your web.xml:
<servlet>
<servlet-name>gwtRequest</servlet-name>
<servlet-class>com.company.SecurityAwareRequestFactoryServlet</servlet-class>
</servlet>
This question already has answers here:
How implement a login filter in JSF?
(2 answers)
Closed 7 months ago.
I need to allow only logged-in users to most of the pages of my application.
I am developing a Java Enterprise application with JSF 2.
Does anyone know how I can do that? maybe with a config file?
I have a login component in the home page and I would like the users to be redirected to the home page when they click any other items of the page except a few.
There are different ways to do that . Firstly you can use filters to control page access or you can use phase listeners that listens jsf phases .
I wanna give you two examples for them ;
public class SecurityFilter implements Filter{
FilterConfig fc;
public void init(FilterConfig filterConfig)throws ServletException {
fc = filterConfig;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException{
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse resp = (HttpServletResponse) response;
HttpSession session = req.getSession(true);
String pageRequested = req.getRequestURI().toString();
if(session.getAttribute("user") == null && !pageRequested.contains("login.xhtml")){
resp.sendRedirect("login.xhtml");
}else{
chain.doFilter(request, response);
}
}
public void destroy(){
}
}
And you should add this filter to web.xml;
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>com.webapp.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Phase Listener example ;
public class SecurityFilter implements PhaseListener {
public void beforePhase(PhaseEvent event) {
}
public void afterPhase(PhaseEvent event) {
FacesContext fc = event.getFacesContext();
boolean loginPage =
fc.getViewRoot().getViewId().lastIndexOf("login") > -1 ? true : false;
if (!loginPage && !isUserLogged()) {
navigate(event,"logout");
}
}
private boolean isUserLogged() {
//looks session for user
}
private void navigate(PhaseEvent event, String page) {
FacesContext fc = event.getFacesContext();
NavigationHandler nh = fc.getApplication().getNavigationHandler();
nh.handleNavigation(fc, null, page);
}
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
So if you want to use listener you should add this to your faces-config.xml ;
Note : "logout" is a navigation rule which is defined in faces-config
<lifecycle>
<phase-listener>com.myapp.SecurityFilter</phase>
</lifecycle>
Edit :
The navigation rule ;
<navigation-rule>
<from-view-id>/*</from-view-id>
<navigation-case>
<from-outcome>logout</from-outcome>
<to-view-id>/login.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
You can put your user to session in login method like that ;
FacesContext context = FacesContext.getCurrentInstance();
HttpSession session =
(HttpSession)context.getExternalContext().getSession(true);
session.setAttribute("user", loggedUser);
There are many ways to achieve that. The easiest and probably the most popular way would be to use servlet filter, you can find more information about such a mechanism here: Basic Security in JSF