I want to implement following solution (described in a image) using Java Web Services
When ever a user request with a valid credentials using web services , a session is created over server and that server (who receives the request ) creates a connection with the other server i.e. Meta Trader's Server.
Here each user has a different session to maintain their connection and a state with meta trader server.
Note:
Currently i am not maintaining any session when a user request instead i am saving the connection object in a
#javax.ws.rs.core.Context
ServletContext servletContext;
MyApplication application = new MyApplication();
servletContext.setAttribute("application", application);
But this solution doesn't serve multiple users naturally. so please anyone has an idea how to solve the issue of serving multiple clients then please reply.
I am using Glassfish and JAX-RS ( Jersery 1.1 ) , JAXB
Simply use the annotation #javax.ws.rs.core.Context to get the HttpServletRequest and use its session within the container in which Jersey is deployed.
The code below is a simple example of a jersey resource that gets the session object and stores values in the session and retrieves them on subsequent calls.
#Path("/helloworld")
public class HelloWorld {
#GET
#Produces("text/plain")
public String hello(#Context HttpServletRequest req) {
HttpSession session= req.getSession(true);
Object foo = session.getAttribute("foo");
if (foo!=null) {
System.out.println(foo.toString());
} else {
foo = "bar";
session.setAttribute("foo", "bar");
}
return foo.toString();
}
}
But you should NOT use RESTful API like this. It meant to be used as web service which is stateless, not web application. Check the following answers which I got the example and advice from
(jersey security and session management)
https://stackoverflow.com/a/922058
https://stackoverflow.com/a/7752250
(How to manage state in JAX-RS?)
https://stackoverflow.com/a/36713305
(Get ServletContext in JAX-RS resource)
https://stackoverflow.com/a/1814788
Related
I am starting an application that uses a REST api which makes calls to an EJB layer on JBoss Wildfly (RestEasy).
The REST services are inside a war which then calls the ejb layer.
I know how to achieve BASIC or any custom form of authenthication on REST with an RestEasy Interceptor that checks Headers etc.
Basically like described here: http://howtodoinjava.com/2013/06/26/jax-rs-resteasy-basic-authentication-and-authorization-tutorial/
The problem now is - this is just a check on the REST facade. Inside the EJB layer I don't know the user that authenticated against the REST service.
To clear this - when using RMI and Remote EJB calls with authentication, the user name is stored in Session Context:
#Stateless
public class LoginService {
#Resource
private SessionContext sessionContext;
public String getCurrentUser() {
Principal principal = sessionContext.getCallerPrincipal();
return principal.getName(); //I need this to be the username from REST auth
//Currently it's anonymous
}
}
Is there a way to propagate the username in some standard way? E.g. putting a custom principal to SessionContext?
You can use the Subject's doAs method.
See the JavaDocs here.
When makings calls from the war to the EJB do it with the authenticated subject's doAs method. This way the subject is propagated to the context of the ejb. (eg. #RolesAllowed will work fine)
You can config the authentication in the web.xml as usual if you want.
To get the subject in the war, try this Subject userSubject=(Subject)PolicyContext.getContext("javax.security.auth.Subject.container");
Shiro's SessionDAO is used to store session in a db(as a blob).
This session is used across multiple web applications(shiro enabled)
Now the problem is each web application is trying to set attributes(custom classes) in the session.
when tried to deserialize in other web application, it throws back ClassDefNotFoundException as it doesn't exist.
Is there any way I can solve this?
What should be the ideal approach?
Separate your singular session into multiple sessions. Start with a globally accessible session that contains the user's principal and the application specific keys for all other web applications that are using the same session (and nothing else). Then when accessing an isolated web application create a new "session" in the cache and put the key for it into the global session. Then when a user accesses the isolated web application a filter should retrieve the key from the global session and bind the isolated web application specific session to the current thread context (and then remove it after execution has completed).
main-user-session
|
+--user-principal
|
+--app1-user-session-key
|
+--app2-user-session-key
void filter (HttpRequest req, HttpResponse resp) {
var app1key = sessionCache.get("main-user-session-key")
.get("app1-user-session-key");
Session app1Sess = sessionCache.get(app1key);
threadContext.bind("SESSION", app1Sess);
try {
execute(request, response);
} finally (Exception e) {
threadContext.unbind("SESSION");
}
}
I'm trying to understand how Jax-WS web services work but can't find any resources on the specifics of them. By way of example:
I create a very simple Java web service using Jax-WS annotations, like so
#WebService(name = "MyService", serviceName = "MyService", portName = "MyServicePort")
public class MyService {
private int val;
public MyService() {
val = 0;
}
#WebMethod(action = "setVal")
public void setVal(#WebParam(name = "arg") int arg) {
val = arg;
}
#WebMethod(action = "getVal")
public int getVal() {
return val;
}
}
If I generate a web service from this (using WS-Gen, say), build a client and make the following calls from a single client: setVal(5), getVal(), what value will be returned to the client? Why?
What about if client A calls setVal(5), and client B calls getVal(), what value will be returned to the client? Why?
Naturally I could build a web-service myself and test it, but I was hoping for an explination to go with the answer. Does Jax-Ws create a new instance of the annotated class for each request? Does it somehow map the same source to the same annotated class instance? Does it just map all requests to a singleton instance of the annotated class? Is there a finite pool of annotated class instances that picked from in some fashion?
Web Services are stateless by default because of the underlying HTTP protocol. The server processes each web service request as a new interaction even though it is from the same client
Normally, a JAX-WS Web service is stateless: that is, none of the
local variables and object values that you set in the Web service
object are saved from one invocation to the next. Even sequential
requests from a single client are treated each as independent,
stateless method invocations.
There are Web service use cases where a client may want to save data
on the service during one invocation and then use that data during a
subsequent invocation. For example, a shopping cart object may be
added to by repeated calls to the addToCart web method and then
fetched by the getCart web method. In a stateless Web service, the
shopping cart object would always be empty, no matter how many
addToCart methods were called. But by using HTTP Sessions to maintain
state across Web service invocations, the cart may be built up
incrementally, and then returned to the client.
Enabling stateful support in a JAX-WS Web service requires a minimal
amount of coding on both the client and server.
http://docs.oracle.com/cd/E17904_01/web.1111/e13734/stateful.htm
I'm creating a GWT application that will be accessed by a POST request, which contains parameters I care about (user id, etc.).
All the reading I've done so far has led me to believe that I should create a servlet (I'm using tomcat) that will handle the POST parameters and then forward to my GWT application. I've gotten this working, but I'm still having trouble passing this data to my application. I've seen 3 suggested approaches:
Save data to context: I have this working right now, but I'm not happy with it. When the servlet is accessed, I parse the parameters and update the context of my GWT web application and then forward to the application where I make an RPC call to read the context. This does what I want it to, but this creates a race condition when multiple users try to access the application at the same and the context is rapidly changing.
Store data in session: I've tried saving the data to the request session in my servlet, and then accessing the session in my RPC, but I always get a new/different session, so I assume I'm mucking this up somewhere.
Save session on servlet
HttpSession session = request.getSession();
session.setAttribute("test", "testValue");
response.sendRedirect(response.encodeRedirectURL("/GWT_Application"));
Access session in RPC
HttpSession session = this.getThreadLocalRequest().getSession();
session.getAttribute("test");
This returns a different session, which results in the "test" attribute being null.
Pass data in URL: My application will be opened in an iframe, meaning Window.location.getParameter() will not be usable.
Any help would be greatly appreciated! I'm still learning GWT and web development in general so don't be afraid to call me out on any obvious or silly mistakes.
Thanks!
SOLUTION
I figured out what the issue was with my session approach: the servlet in which I was previously trying to save the session data was in a separate tomcat web app from my GWT application. Moving them to the same web app solved my problems and it now works. I'm not sure, but I'm guessing that this was a problem because redirecting to another web app switches the context. I'll outline my whole approach in the hopes this saves someone else some time later:
Put your servlet code in the server folder of your GWT project:
package GWTApplication.server;
public class myServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
session.setAttribute("myAttribute", request.getParameter("myParam");
// handle rest of POST parameters
response.sendRedirect(response.encodeRedirectURL("/GWTApplication");
}
}
Map servlet in your GWT application's web.xml:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>GWTApplication.myServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
This servlet should now be accessible at .../GWTApplication/myServlet
Next make a standar RPC. Within whatever method you will be calling in the ServiceImpl class on the server:
HttpSession session = this.getThreadLocalRequest().getSession();
return session.getAttribute("myAttribute");
Finally, make your RPC call in the onModuleLoad() method of you GWT application. As a recap:
Send the original POST request to the servlet
Save POST parameters to session variables
Redirect to GWT application
Make RPC call in onModuleLoad()
Read session variables in ServiceImpl class
You can talk with servlets through RPC call in GWT
You need to make a RPC call in the starting point of GWT application.
Set that data to serverside session and get the session data in servceImpl call of GWT which extends to RemoteServiceServlet.
Example :
YourServiceImpl extends RemoteServiceServlet {
#ovveride
doGet(){
//you can access session here
}
#ovveride
doPost(){
//you can access session here
}
#ovveride
doPut(){
//you can access session here
}
----your other methods
}
A brief Example I wrote here:How to make an GWT server call(GWT RPC?)
Since RemoteServiceServlet extends HttpServlet, you can just override doPost() method to access your POST requests. Don't forget to call super.doPost() EDIT: This doesn't work because the method is finalized in AbstractRemoteServiceServlet so it cannot be overridden.
Also, GWT Servlets POST data using the proprietary GWT RPC format. Read more about that format and how to interpret it here: GWT RPC data format
EDIT
There are several methods you can override in your ServiceImpl class that extends RemoteServiceServlet:
public String processCall(String payload) will give you a String representation of the incoming request.
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) will give you a RPCRequest object that has an array of parameters, along with the method that was called.
public void service(ServletRequest request, ServletResponse response) will give you all the Attributes of the HTTP request.
I want to call a remote EJB from a different EJB server. I've Remote EJB's deployed on OpenEJB ,then I will call them from Glassfish HTTPServlet. I know on local EJB i can do #EJB annotation but I want user to "authenticate" remote OpenEJB server from Glassfish servlet.
on OpenEJB:
//OpenEJB server at 192.168.10.12
public class AdminManager {
#RolesAllowed("admin")
public void test() {
System.out.println("Admin called this method");
}
}
Glassfish Servlet
//Glassfish servlet at 192.168.10.10
public class AdminManage extends HttpServlet {
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Properties p = new Properties();
p.put("java.naming.factory.initial", "org.apache.openejb.client.RemoteInitialContextFactory");
p.put("java.naming.provider.url", "ejbd://192.168.10.12:4201");
// user and pass optional
String userName = req.getSession().getAttribute("username");
String password = req.getSession().getAttribute("password");
p.put("java.naming.security.principal", userName );
p.put("java.naming.security.credentials", "password );
final InitialContext ctx = new InitialContext(p);
final AdminManagerRemote myBean = (MyBean) ctx.lookup("AdminManagerRemote");
try {
myBean.test();
} catch(Exception epx) {
resp.sendRedirect(resp.encodeRedirectURL("/login"));
}
}
}
Okay this seems a solution but this is not "good" solution for me.
Everytime authenticating to openejb sucks , like this;
String userName = req.getSession().getAttribute("username");
String password = req.getSession().getAttribute("password");
p.put("java.naming.security.principal", userName );
p.put("java.naming.security.credentials", "password );
And the code is not clean.
I want user to authenticate only once and call ejb methods.
Is there any better solution then this?
Can I combine Glassfish HTTP authentication with remote openEJB authentication?
I mean when user authenticates to Glassfish HTTP server , i want openEJB authenticate as well.
This is really making me mad. what solution is good to escape from spagetti codes?
Two ideas:
Authenticate to OpenEJB when the user logs into Glassfish, store the EJB stub in the user session and reuse it later. I fear you will need to implement some EJB 2.x inteface for the serialization of the stub (so-called handle) to work.
Implement a cache where you store the EJB stubs. If a stub for the given username/password is in the cache, reuse it, if not, authenticate. The cache is a static object, it could be created for instance with Guava's CacheBuilder. This violates the spec (you should not use static to share information between servlets), but since it's a cache, it's not a big deal. It would even work in a cluster.
You can try introducing Service Locator Pattern to eliminate the redundancy in the code & hide the underlying details & complexity of creating initial context, JNDI lookup etc.
You can cache remote home interface EJBHome & then re-use it later when required.
Can refer Core J2EE Patterns - Service Locator documentation for more details.
What about Kerberos ? Someone know kerberos solution ?
Login to all remote servers with TGS.
But there is no sample for that on internet.
And its so evil hard to install kerberos with openldap.