I have built my first GWT app. giving no compilation errors neither run-time errors. However, when the application is loaded into the browser (using Interner Explorer) and I enter username and password field to validate the user, it throws exceptions. Using GWT-RPC method, entire code and interfaces are provided.
I'm using HSQL for database connection(back end).
------------------CODE (CLIENT)
package com.vin.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
public class HelloWorld implements EntryPoint{
private UserServiceAsync UserService = (UserServiceAsync) GWT.create(UserService.class);
public void onModuleLoad() {
Button click=new Button("Click Here");
Label name=new Label("Enter Name");
Label passwrd=new Label("Enter Password");
final TextBox t_name=new TextBox();
final PasswordTextBox t_passwrd=new PasswordTextBox();
click.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent ev) {
String temp_user=t_name.getText();
String temp_pass=t_passwrd.getText();
UserService.loginuser(temp_user, temp_pass, new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
Window.alert("Please enter valid details");
}
public void onSuccess(String result) {
Window.alert("Welcome");
// Window.open("http://127.0.0.1:8888/ExWid.html?gwt.codesvr=127.0.0.1:9997", "Dem", null);
}
});
}
});
RootPanel.get().add(name);
RootPanel.get().add(t_name);
RootPanel.get().add(passwrd);
RootPanel.get().add(t_passwrd);
RootPanel.get().add(click);
}
}
-----------------------------CLIENT INTERFACE (1)
package com.vin.client;
import com.google.gwt.user.client.rpc.RemoteService;
public interface UserService extends RemoteService {
public String loginuser(String username, String password);
}
----------------------------CLIENT ASYNC INTERFACE
package com.vin.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface UserServiceAsync {
public void loginuser(String username, String password, AsyncCallback<String> callback);
}
--------------------------IMPLEMENTATION OF CLIENT USERSERVICE (SERVER)...DATABASE CONNECTION
package com.vin.server;
import java.sql.DriverManager;
import java.sql.ResultSet;
import com.google.gwt.dev.generator.ast.Statement;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.vin.client.UserService;
public class UserServiceImpl extends RemoteServiceServlet implements UserService{
private static final long serialVersionUID = 1L;
public String loginuser(String username,String password) {
try {
java.sql.Connection con = null;
Class.forName("org.hsqldb.jdbcDriver");
con = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/", "SA", "");
Statement st=(Statement) con.createStatement();
ResultSet rs=((java.sql.Statement) st).executeQuery("select username,password from lgfrm");
String user=rs.getString(1);
String pass=rs.getString(2);
if(username.equals(user) && password.equals(pass)) {
Window.alert("success");
}
}
catch (Exception ae) {}
return "success";
}
}
------------------THE EXCEPTION LIST WHILE I'M TRYING TO VALIDATE A USER
15:22:54.583 [ERROR] [helloworld] Uncaught exception escaped
com.google.gwt.event.shared.UmbrellaException: One or more exceptions
caught, see full set in UmbrellaException#getCauses
at com.google.gwt.event.shared.HandlerManager.fireEvent(HandlerManager.java:129)
at com.google.gwt.user.client.ui.Widget.fireEvent(Widget.java:129)
at com.google.gwt.event.dom.client.DomEvent.fireNativeEvent(DomEvent.java:116)
at com.google.gwt.user.client.ui.Widget.onBrowserEvent(Widget.java:177)
at com.google.gwt.user.client.DOM.dispatchEventImpl(DOM.java:1351)
And many more like these.
com.google.gwt.user.client.Window class provides access to the browser window's methods, properties, and events. So you can't use it in Serverside. Better you return String "success" when requirement meets, else return Exception, so that it is caught by onFailure on clientside.
I think you can't use Window.alert on server side (in UserServiceImpl class). There can be many clients and server can't know about what client it directed for.
But i'm not sure that it causes this error.
Related
So I am new to java RMI and making a GUI, but I got the RMI working. Now I have an Interface class called MessageService, a Server class called MessageServer and a Client class called MessageClient. I'd like to make a GUI with a field, where I can write a message, that will then be displayed on the server side. How is this achieved?
EDIT:
I have now done some kind of GUI. I also added a function to the Server. I also edited the Client code, so that when I run it, then it would launch my GUI class.
How would I implement it so I could write text into the input field and when sent, it would be seen on the server?
Here is the code for GUI:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class TheGUI extends Application{
Button sendButton;
TextField input;
public static void main(String[] args) throws RemoteException, NotBoundException {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("GUI");
input = new TextField();
sendButton = new Button();
sendButton.setText("send");
AnchorPane layout = new AnchorPane();
HBox hbox = new HBox(5, input, sendButton);
layout.getChildren().addAll(hbox);
AnchorPane.setTopAnchor(hbox, 10d);
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
public void handle(ActionEvent e)
{
String message = input.getText();
input.setText("");
}
};
input.setOnAction(event);
sendButton.setOnAction(event);
Scene scene = new Scene(layout, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
}
This is my Edited Interface:
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MessageService extends Remote {
public void newMessage (String clientID, String message) throws RemoteException;
}
This is my edited server class:
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class MessageServer extends UnicastRemoteObject implements MessageService {
protected MessageServer() throws RemoteException {
super();
}
#Override
public void newMessage(String clientID, String message) throws RemoteException {
System.out.println(clientID + " " + message);
}
public static void main (String[] argv)
{
try
{
Registry registry = LocateRegistry.getRegistry(1099);
MessageServer messageServer = new MessageServer();
registry.rebind("MessageService", messageServer); //register with naming service(bind with registry)
System.out.println("Server is Ready");
}
catch (RemoteException e)
{
System.out.println("ERROR: Could not create registry");
e.printStackTrace();
}
}
}
This is my Edited Client class:
import javafx.application.Application;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class MessageClient
{
public static void main (String[] argv) throws RemoteException, NotBoundException {
try
{
Registry registry = LocateRegistry.getRegistry("127.0.0.1");
MessageService messageService= (MessageService) registry.lookup("MessageService");
Application.launch(TheGUI.class);
}
catch (Exception e)
{
System.out.println ("MessageClient exception: " + e);
}
}
}
You are very close. Your code is almost complete.
RMI means communication between two, separate JVMs. The first JVM is your server process - which you have completed the coding for, hence I will not repeat it here. The second JVM is your client process. You can combine the GUI code with the RMI client code. And the GUI code you posted can be simplified.
Here is my version of your desired GUI to act as your RMI client.
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MessageClientGui extends Application {
private static final String CLIENT_ID = "George";
private static MessageService msgService;
private TextField message;
#Override
public void start(Stage primaryStage) throws Exception {
Label prompt = new Label("Message");
message = new TextField();
Button send = new Button("_Send");
send.setOnAction(this::sendMessage);
send.setTooltip(new Tooltip("Sends message to [RMI] server."));
HBox root = new HBox(5.0D, prompt, message, send);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private void sendMessage(ActionEvent actnEvnt) {
try {
msgService.newMessage(CLIENT_ID, message.getText());
}
catch (RemoteException x) {
x.printStackTrace();
}
}
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry("127.0.0.1");
msgService = (MessageService) registry.lookup("MessageService");
launch(args);
}
catch (Exception x) {
x.printStackTrace();
}
}
}
Here is an image of the running client.
In method main(), I initialize the MessageService client and save it in a static class member variable. When the user clicks on the Send button, method sendMessage() is called. In this method the remote method newMessage() is called with the contents of the TextField and some arbitrary client ID, namely George. As a result, in the console (i.e. standard output) of the server JVM, a string is printed that starts with: George
I'm new to dropwizard and i'm trying to create an Authenticator which gets credentials from the user, then it uses the rest api getUser method which i implemented in my UserResouce class to get the user with the username that is in the credentials from the db users table. However in my autheticator class i having troubles in figuring out how to use the user resource functions to get the user.
I was trying to do something like that:
public List<com.amitbaz.tss.db.User> getUsersFromDB(String username){
SessionFactory sessionFactory = TradingSystemServerApplication.hibernateBundle.getSessionFactory();
UserDAO userDAO = new UserDAO(sessionFactory);
List<com.amitbaz.tss.db.User> user = userDAO.getUser(username);
logger.debug(user.toString());
return user;
}
inside the autheticator and call it from the authenticte function but it says there is no session bound...
EDIT:
Ok so after much thinking i got to this:
I'm dropwizard authenticator and authorizer implementions with BasicCredentials and.
Autheticator (don't mind the VALID_USER thing..):
package com.amitbaz.tss.auth;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.NamedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.TradingSystemServerApplication;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class TradingSystemServerAuthenticator implements Authenticator<BasicCredentials, User> {
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthenticator.class);
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"amit", ImmutableSet.of("admin"),
"stav", ImmutableSet.of("broker")
);
private UserDAO userDAO;
public TradingSystemServerAuthenticator(UserDAO userDAO) {
// TODO Auto-generated constructor stub
this.userDAO = userDAO;
}
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
// TODO Auto-generated method stub
List<com.amitbaz.tss.db.User> user = userDAO.getUser(credentials.getUsername());
logger.debug(user.toString());
if("amit".equals(credentials.getPassword())){
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
Authorizer:
package com.amitbaz.tss.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.db.UserDAO;
import io.dropwizard.auth.Authorizer;
public class TradingSystemServerAuthorizer implements Authorizer<User>{
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthorizer.class);
private UserDAO userDAO;
public TradingSystemServerAuthorizer(UserDAO userDAO) {
super();
this.userDAO = userDAO;
}
#Override
public boolean authorize(User user, String role) {
// TODO Auto-generated method stub
logger.debug(userDAO.getUser(user.getName()).toString());
return user.getName().equals("amit") && user.getRole().contains(new String("admin"));
}
}
Now, In my Application class I do this:
package com.amitbaz.tss;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new
env.jersey().register(new UserResource(userDAO));
/...
BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TradingSystemServerAuthenticator(userDAO))
.setAuthorizer(new TradingSystemServerAuthorizer(userDAO))
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
}
And I have the annotation #RolesAllowed("role_name") on one of the rest api methods which with im trying to test the auth.
Now when i try to test this and i make a request to that rest api method, I
get the error No session currently bound to execution context where i do userDAO.getUser(...) in the authanticator and in the authorizer
EDIT 2:
UserDAO implementation:
package com.amitbaz.tss.db;
import java.util.List;
import org.hibernate.SessionFactory;
import io.dropwizard.hibernate.AbstractDAO;
public class UserDAO extends AbstractDAO<User>{
public UserDAO(SessionFactory sessionFactory) {
super(sessionFactory);
// TODO Auto-generated constructor stub
}
public List<User> getUser(String username){
return list(namedQuery("com.amitbaz.tss.db.user.getUser")
.setParameter("username", username));
}
}
EDIT 3:
Added #UnitOfWork to authenticate and authorize methods.
registered them as follow ( Notice the changes in hibernateBundle and run method):
package com.amitbaz.tss;
import javax.servlet.ServletRegistration;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereServlet;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
,TradingSystemServerAuthenticator.class, TradingSystemServerAuthorizer.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new UserRoleDAO(hibernateBundle.getSessionFactory());
final TradingSystemServerAuthorizer authorizer = new TradingSystemServerAuthorizer(userDAO);
final TradingSystemServerAuthenticator authenticator = new TradingSystemServerAuthenticator(userDAO);
env.jersey().register(new UserResource(userDAO));
env.jersey().register(new UserRoleResource(userRoleDAO));
env.jersey().register(authorizer);
env.jersey().register(authenticator);
env.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(authorizer)
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Your approach looks like a design issue. The issue I see is that you are trying to integrate via rest with a service that is already accessible for you within your application. That adds a lot of overhead and complicates things.
Fortunately, DW already has a fully integrated Authorization and Authentication system just waiting for you to plug in. You can read more about it here: http://www.dropwizard.io/1.0.2/docs/manual/auth.html
The essential thing to note here is that you should split the service used by your resource from your resource. In your case for example the UserDao, or you could split it into a UserService and UserResource, where the UserService provides access to your database layer. Up to you really.
Here is how you would implement this with DW integrated auth and how you would register this as well.
In my example I am skipping the Hibernate aspect of this as it isn't too relevant. you can read about it here: http://www.dropwizard.io/1.0.2/docs/manual/hibernate.html
Here's my code:
public class AuthenticatorTest extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
// register resource
environment.jersey().register(MyHelloResource.class);
// create the dao + dependencies
UserDao dao = new UserDao(null);
// register new authenticator
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(new UserAuth(dao)).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
// enables authentication via filter
environment.jersey().register(RolesAllowedDynamicFeature.class);
}
public static void main(String[] args) throws Exception {
new AuthenticatorTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class MyHelloResource {
#GET
#Path("asd")
#PermitAll
public String test(String x) {
return "Hello";
}
}
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
public static class UserDao {
private SessionFactory s;
public UserDao(final SessionFactory s) {
this.s = s;
}
public String getUser() {
return "pandaadb";
}
}
}
And this is the breakdown of what we are doing.
First, as per docs, you would register your HibernateBundle within the bootstrap method as shown (in docs). This gives you access to the SessionFactory you require for your authentication.
Your resource method will be annotated with a java security annotation. i am using PermitAll because I am disregarding roles.
In the run method, you then create your DAO, register your resource and use the DW builder to add the required Filter and the Authenticator. This one specifically is for BasicCredentials, however there is nothing stopping you from doing any kind of filter for this. DW already supports things like Ldap (in a different dependency), Basic Auth and so on.
Now, since you create your beans in the run method, and you added your Hibernate bundle in the bootstrap method, you have access to the SessionFactory and can instantiate the DAO accordingly. No need to have to pass it around.
You also don't have to do any rest-request to access your user (though there is nothing stopping you adding that resource anyway, in case you need external access to it.)
So, to sum up, the important parts are:
Add A security annotation to your resource (e.g. PermitAll to allow all roles)
Add an authenticator implementation (in my case UserAuth) that uses your DAO
Instantiate it in the run method provided by dropwizard and register it with the jersey environment.
Note, this requires your user to implement the javax.security.Principal interface. this is not a bad idea in general as a lot of security Frameworks make use of this.
This, also, gives you more options with regards to DW.
You can add an Authorization implementation and a filter, and you'll be able to inject the User object into any resource method by adding an #Auth annotated object (see docs).
Finally, the test of the standalone app from above:
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 17 Oct 2016 10:45:51 GMT
< WWW-Authenticate: Basic realm="SUPER SECRET STUFF"
< Content-Type: text/plain
< Content-Length: 49
<
* Connection #0 to host localhost left intact
Credentials are required to access this resource.
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -H "Authorization: Basic dXNlcjpwYXNz" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Basic dXNlcjpwYXNz
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Oct 2016 10:46:11 GMT
< Content-Type: application/json
< Vary: Accept-Encoding
< Content-Length: 5
<
* Connection #0 to host localhost left intact
Helloartur
I hope this helps you with your issue.
Of course you don't need to use the DW method for Authentication. However I would recommend going down that road as you will have more support and a lot of things out of the box.
However, the one thing you should rethink (if you don't use DW) is to not do a curl request to your Filter. Instead, instantiate it in the run method, and pass that instance to your Filter.
Note also, if you register your DAO with DW (as seen in hibernate docs), you will be able to use #Inject to inject your DAO into the Filter class that needs to use it.
Right, I think that's all the info you need :)
Let me know if you have any problems,
Artur
Edit:
I am doing an edit because I wrote a lot above and don't want to go over it.
I set up Hibernate to test this. The reason you are seeing issues is because the UnitOfWork is bound to the request scope. However, the resource annotations is matched AFTER the filter is invoked (since you need to do auth before executing the method). This is why you don't have a session.
This is the solution.
In your run method, register a proxy for your auth implementation:
UnitOfWorkAwareProxyFactory fac = new UnitOfWorkAwareProxyFactory(hibernate);
UserAuth proxy = fac.create(UserAuth.class, UserDao.class, dao);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(proxy).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
This creates a proxy around the UserAuth class so that it is aware of the UnitOfWork annotation.
In your UserAuth class (or mine rather) you do:
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
#UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
Note the UnitOfWork annotation on the authenticate. This now opens a new session for you. Please make sure to read up on UnitOfWork as it may have tricky side effects (or not) depending on how you use it.
Finally, this allowed my dao to talk to the database on an existing session.
Regards,
Artur
I finally, after much debugginf, find the proper way to open a session and execute a query.
I added this lines in Authenticator's authenticate mehod:
Session session = userDAO.getSessionFactory().openSession();
Transaction transaction = session.getTransaction();
Query userquery = session.createQuery("select u from User u where u.username = :username").setParameter("username", credentials.getUsername());
List<com.amitbaz.tss.db.User> u = userquery.list();
session.close();
And the same in Authorizater and it now works :)
I am using Guice + Jersey + Shiro to login via a REST API and then use the same HTTP session under which I logged in to and have my permissions work for that resource.
Below is my code.
Firstly, my servlet configuration:-
public class ServletConfiguration extends GuiceServletContextListener
{
private ServletContext mServletContext;
#Override
public void contextInitialized(ServletContextEvent inEvent)
{
mServletContext = inEvent.getServletContext();
super.contextInitialized(inEvent);
}
#Override
protected Injector getInjector()
{
mServletContext.addListener(new au.com.tt.agora.configuration.CbiCleanupHttpSessionListener());
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets()
{
install(new TTShiroWebModule(mServletContext));
install(new ShiroAopModule());
filter("/*").through(GuiceShiroFilter.class);
bind(ShiroLoginResource.class);
bind(ShiroResource.class);
filter("/*").through(GuiceContainer.class);
}
});
}
}
Now, this is my test realm:-
package au.com.tt.agora.configuration.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class TestRealm extends AuthorizingRealm
{
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken inToken) throws AuthenticationException
{
UsernamePasswordToken upToken = (UsernamePasswordToken) inToken;
if (upToken.getUsername().equals("Kamal") || upToken.getUsername().equals("NotKamal"))
return new SimpleAuthenticationInfo(upToken.getUsername(), upToken.getPassword(), getName());
return null;
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection inPrincipals)
{
String username = (String) inPrincipals.fromRealm(getName()).iterator().next();
SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
if (username.equals("Kamal"))
{
authzInfo.addStringPermission("PRODMA:READ:AU");
authzInfo.addStringPermission("PRODMA:WRITE:KB");
authzInfo.addStringPermission("SUPPMA:READ:KB");
}
else
{
authzInfo.addStringPermission("PRODMA:READ:AU");
authzInfo.addStringPermission("PRODMA:WRITE:KB");
}
return authzInfo;
}
}
This is the web module
package au.com.tt.agora.configuration.shiro;
import javax.servlet.ServletContext;
import org.apache.shiro.guice.web.ShiroWebModule;
public class TTShiroWebModule extends ShiroWebModule
{
public TTShiroWebModule(ServletContext inServletContext)
{
super(inServletContext);
}
#SuppressWarnings("unchecked")
#Override
protected void configureShiroWeb()
{
bindRealm().to(TestRealm.class);
addFilterChain("**/shiroResource/*", ANON);
}
}
Here is the resource I use to login:-
package au.com.tt.agora.configuration.jaxrs.resources;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import com.google.inject.Inject;
import au.com.tt.agora.configuration.option.ClientProvider;
import au.com.tt.agora.configuration.option.ConfigurationProvider;
import au.com.tt.agora.login.web.request.LoginRequest;
import au.com.tt.agora.login.web.request.LoginResponse;
import au.com.tt.agora.login.web.service.LoginHandler;
import au.com.tt.calypso.cbi.CalypsoException;
#Path("/{client}/shiroLogin")
public class ShiroLoginResource
{
private static final String ROUTING_TOKEN_HEADER = "proxy-jroute";
#POST
#Path("/standard")
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String login(#Context HttpServletRequest inServletRequest) throws CalypsoException
{
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken("Kamal", "Password", false));
return getSessionIdWithRouting(inServletRequest);
}
private String getSessionIdWithRouting(HttpServletRequest inRequest)
{
String sessionId = inRequest.getSession().getId();
return(sessionId);
}
}
And here is the resource I am calling:-
package au.com.tt.agora.configuration.jaxrs.resources;
import javax.servlet.http.HttpSession;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
#Path("/{client}/shiroResource")
public class ShiroResource
{
private static final Logger LOG = LoggerFactory.getLogger(ShiroResource.class);
#Inject
public ShiroResource()
{
}
#POST
#Path("requiresProdma.do")
#Produces(MediaType.TEXT_PLAIN)
#RequiresPermissions({ "PRODMA:*:*" })
public String prodmaRequired()
{
return "Success";
}
#POST
#Path("requiresSuppma.do")
#Produces(MediaType.TEXT_PLAIN)
#RequiresPermissions({ "SUPPMA:*:*" })
public String suppmaRequired()
{
Subject subject = SecurityUtils.getSubject();
subject.getPrincipal();
return "Success";
}
}
If I put a breakpoint into suppmaRequired and call this resource, I can see that subject is not authenticated.
My understanding on how Shiro works is obviously faulty, but I don't know what I am not doing. Can anyone point me in the right direction?
Not sure if it makes a difference, but I am using URL rewriting to access the web session.
Basically, I am using the fetch API to test this. Here is an example:-
fetch("http://localhost/app/tt/shiroLogin/standard", {
method: "POST",
headers: {
"Content-Type" : "application/json"
} ,
body: '{"username":"myName","password":"myPassword"}'
})
.then(function(res) {
return res.text();
})
.then(function(sessionId) {
return fetch("http://localhost/app/tt/shiroResource/requiresSuppma.do;JSESSIONID=" + sessionId,
{
method: "POST"
});
})
.then(function(res) {
return res.text();
})
.then(function(res) {
console.log(res);
});
I am also deploying to glassfish.
OK, this was not a Shiro problem in the end. I was using two different sessions going from the ShiroLoginResource to ShiroResource.
I forgot that you actually needed to inject with a session level object in Guice to force Guice to create a session. Stupid me.
Once I injected a session scoped dependency into ShiroLoginResource and interacted with it, then everything just worked.
I will keep this question open because it gives some useful code snippets.
here you can see how to send a message from client.
I have a client HelloServer.java, when i click on the button I want to send message to a server.
package gwt.user.client;
import org.jboss.errai.bus.client.ErraiBus;
import org.jboss.errai.bus.client.api.base.CommandMessage;
import org.jboss.errai.bus.client.api.base.MessageBuilder;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageBus;
import org.jboss.errai.bus.client.api.messaging.MessageCallback;
import org.jboss.errai.bus.client.api.messaging.RequestDispatcher;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
public class HelloServer implements EntryPoint{
private MyTable table;
private MessageBus bus = ErraiBus.get();
private RequestDispatcher dispatcher = ErraiBus.getDispatcher();
UserService usrSer;
private RequestDispatcher getDispatcher(){
return this.dispatcher;
}
public void onModuleLoad() {
table = new MyTable(null);
Button button = new Button("Click me");
// We can add style names
button.addStyleName("pc-template-btn");
// or we can set an id on a specific element for styling
VerticalPanel vPanel = new VerticalPanel();
vPanel.setWidth("100%");
vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
vPanel.add(button);
vPanel.add(table);
// add table and button to the RootPanel
RootPanel.get().add(vPanel);
// create the dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Welcome to GWT Server Communication!");
dialogBox.setAnimationEnabled(true);
Button closeButton = new Button("close");
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.setWidth("100%");
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
dialogVPanel.add(closeButton);
closeButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
dialogBox.hide();
}
});
// Set the contents of the Widget
dialogBox.setWidget(dialogVPanel);
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
UserServiceAsync service = (UserServiceAsync) GWT.create(UserService.class);
ServiceDefTarget serviceDef = (ServiceDefTarget) service;
serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "userService");
UserCallback myUserCallback = new UserCallback(table);
MessageBuilder.createMessage()
.toSubject("UserServiceImpl") // (1)
.signalling() // (2)
.noErrorHandling() // (3)
.sendNowWith(getDispatcher()); // (4)
service.getUserList(myUserCallback);
}
});
}
}
UserServiceImpl.java is the server where I want to receive the message in callback method.
package gwt.user.server;
import gwt.user.client.User;
import gwt.user.client.UserService;
import java.util.ArrayList;
import java.util.List;
import org.jboss.errai.bus.client.api.base.CommandMessage;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageCallback;
import org.jboss.errai.bus.server.annotations.Service;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
#Service
public class UserServiceImpl extends RemoteServiceServlet implements UserService, MessageCallback {
private static final long serialVersionUID = 1L;
private List<User> userList = new ArrayList<User>();
public UserServiceImpl() {
User user = new User();
user.setId("1");
user.setUsername("Peter");
user.setNumberOfHits("15");
userList.add(user);
user = new User();
user.setId("2");
user.setUsername("Hanz");
user.setNumberOfHits("25");
userList.add(user);
}
public User getUser(String id) {
for (Object object : userList) {
if (((User) object).getId().equals(id))
return ((User) object);
}
return null;
}
public List<User> getUserList() {
return userList;
}
#Override
public void callback(Message message) {
System.out.print("Message received");
}
public void callback(CommandMessage message) {
System.out.print("Message received");
}
}
When I click the button I get no subscribers to deliver error message:
org.jboss.errai.bus.client.api.base.NoSubscribersToDeliverTo: no subscribers to deliver to for subject: UserServiceImpl
org.jboss.errai.bus.client.framework.ClientMessageBusImpl.send(ClientMessageBusImpl.java:812)
org.jboss.errai.bus.client.ErraiBus$3.dispatch(ErraiBus.java:171)
org.jboss.errai.bus.client.api.base.CommandMessage.sendNowWith(CommandMessage.java:349)
org.jboss.errai.bus.client.api.base.DefaultMessageBuilder$1.sendNowWith(DefaultMessageBuilder.java:95)
gwt.user.client.HelloServer$2.onClick(HelloServer.java:84)
If I annotate UserServiceImpl.java class with #Service("UserServiceImpl"), it doesn't help and I get the same error.
When I add
bus.subscribe("UserServiceImpl", new UserServiceImpl());
before MessageBuilder.createMessage() in HelloServer.java
I get error message
[ERROR] No source code is available for type gwt.user.server.UserServiceImpl; did you forget to inherit a required module?
Does anybody know how to use messaging between Client and Server in GWT applications or show me a basic example?
Answer is in this thread. I forgot to add ErraiApp.properties file.
I'm creating a login application on Eclipse using Google Web Toolkit(GWT). The code checks for the username and password and if its correct, it shows the o/p as welcome. Still after compiling it is giving me errors.I'm sharing both code and the error message. Please help me out.
package com.vin.client;
import java.sql.DriverManager;
import java.sql.ResultSet;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dev.generator.ast.Statement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.*;
public class HelloWorld implements EntryPoint{
public void onModuleLoad() {
Button click=new Button("Click Here");
Label name=new Label("Enter Name");
Label passwrd=new Label("Enter Password");
final TextBox t_name=new TextBox();
final TextBox t_passwrd=new TextBox();
click.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent ev) {
try {
String temp_user=t_name.getText();
String temp_pass=t_passwrd.getText();
java.sql.Connection con = null;
Class.forName("org.hsqldb.jdbcDriver");
con = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/", "SA", "");
Statement st=(Statement) con.createStatement();
ResultSet rs=((java.sql.Statement) st).executeQuery("select username,password from lgfrm");
String user=rs.getString(1);
String pass=rs.getString(2);
if(temp_user.equals(user) && temp_pass.equals(pass)) {
Window.alert("Welcome");
}
else {
Window.alert("Please enter valid details");
}
}
catch (Exception ae) {}
}
});
RootPanel.get().add(name);
RootPanel.get().add(t_name);
RootPanel.get().add(passwrd);
RootPanel.get().add(t_passwrd);
RootPanel.get().add(click);
}
}
Error Message is----------
Compiling module com.vin.HelloWorld Exception in thread
"UnitCacheLoader" java.lang.RuntimeException: Unable to read from byte
cache at
com.google.gwt.dev.util.DiskCache.transferFromStream(DiskCache.java:166)
at
com.google.gwt.dev.util.DiskCacheToken.readObject(DiskCacheToken.java:87)
at sun.reflect.GeneratedMethodAccessor19.invoke(Unknown Source)
..............and many more like this....Please help me out
Try something like following for Server side :
UserService.java
#RemoteServiceRelativePath("userService")
public interface UserService extends RemoteService {
String loginUser(String username,String password);
}
UserServiceAsync.java
public interface UserServiceAsync {
void loginUser(String username, String password, AsyncCallback<String> callback);
}
UserServiceImpl.java
public class UserServiceImpl extends RemoteServiceServlet implements UserService {
public String loginUser(String username, String password){
//database interaction
return "result"; //return success or failure depending upon logic
}
}
Follow Communicate with a Server in GWT and the Anatomy of service
For Client Side :
public class HelloWorld implements EntryPoint{
//(1) Create the client proxy.
private UserServiceAsync userService = (UserServiceAsync) GWT.create(UserService.class);
public void onModuleLoad() {
Button click=new Button("Click Here");
Label name=new Label("Enter Name");
Label passwrd=new Label("Enter Password");
final TextBox t_name=new TextBox();
final TextBox t_passwrd=new TextBox();
click.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent ev) {
String temp_user=t_name.getText();
String temp_pass=t_passwrd.getText();
/// (2) Create an asynchronous callback and Make the call
userService.loginUser(temp_user, temp_pass, new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
Window.alert("Please enter valid details");
}
public void onSuccess(String result) {
Window.alert("Welcome");
}
});//end of service call
});//end of clickhandler
RootPanel.get().add(name);
RootPanel.get().add(t_name);
RootPanel.get().add(passwrd);
RootPanel.get().add(t_passwrd);
RootPanel.get().add(click);
}
}
You can not put DB related code in Entry point class, you need to call GWT-RPC on click method.
Actually this EntryPoint class would be compiled by GWT processor and it will create javascript in output which going to run in browser. So there is no justification you can call db in javascript.
GWT-RPC is asynchronous call which code reside in server. Here you can write all business logic, db interactivity etc.
LINK