我正在创建一个使用单点登录的登录。为此,如果用户登录的公司启用了SSO,则将打开JavaFX WebView
,允许用户登录。此登录将完全基于Web,因此我不需要真正的数据捕获。
我有两个班级,LoginManager
和SSO
。 LoginManager
检查公司是否为SSOEnabled,如果是,则转到SSO
。在这里,我创建了用户界面,然后使用Platform.runLater()
,转到打开webview
的方法。但是,我的程序转到initJFXPanel
,然后立即返回LoginManager
以执行代码,这会导致问题。
我希望能够遍历所有SSO
,直到它到达某个网页,然后我使用frame.dispose(),然后返回LoginManager
继续。我想我需要使用Stage
和showAndWait()
,但我不确定如何解决这个问题。
我尝试在initJFXPanel
内创建一个舞台,但是在它有机会做任何事情之前它回到LoginManager
并没有工作,我也尝试在createPopup
设置舞台{1}}但我不确定该用作舞台或场景。
这是我的代码:
LoginManager类
public class LoginManager {
private boolean bSSOEnabled = true;
private void Login() {
// get company details
if(bSSOEnabled) {
new SSO();
}
// once SSO is done, continue with execution
}
}
SSO课程
public class SSO {
private JFrame frame;
private JFXPanel fxPanel;
private JPanel containerPanel;
final static String WEBVIEW = "WebView";
public SSO(){
createPopup();
}
private void createPopup(){
Dimension size = new Dimension(480, 80);
frame = new JFrame("Web Login");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
containerPanel = new JPanel(new CardLayout());
fxPanel = new JFXPanel();
fxPanel.setSize(size);
Platform.runLater(() -> initJFXPanel(frame, fxPanel));
containerPanel.add(fxPanel, WEBVIEW);
frame.add(containerPanel, BorderLayout.CENTER);
frame.setPreferredSize(size);
frame.setResizable(false);
frame.getRootPane().setWindowDecorationStyle(JRootPane.NONE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initJFXPanel(final JFrame frame, final JFXPanel fxPanel){
Group group = new Group();
Scene scene = new Scene(group);
fxPanel.setScene(scene);
WebView webview = new WebView();
group.getChildren().add(webview);
webview.setMinSize(500, 640);
webview.setMaxSize(500, 640);
CookieManager cookieManager = new CookieManager();
java.net.CookieHandler.setDefault(cookieManager);
webview.getEngine().load("http://google.com/");
if(webview.getEngine().getLocation().equalsIgnoreCase("cookie url")) {
// harvest the cookies
}
else if(webview.getEngine().getLocation().equalsIgnoreCase("end url")) {
frame.dispose();
}
}
}
感谢任何帮助。我已经被困在这一整天了,我不知道该怎么做。
答案 0 :(得分:0)
如果您的应用程序是Swing应用程序,并且您需要使用WebView
,那么正确的方法就是您在代码中显示的方法,即使用Swing窗口并嵌入WebView
在JFXPanel
中。混合来自两个框架的窗口通常是一个坏主意,因此我建议完全避免使用Stage
。
在用户关闭窗口之前在Swing块中创建代码的方法,即Swing等效于JavaFX Stage.showAndWait()
调用,是使用JDialog
并使其成为模态。除了阻止任何其他窗口的输入外,这将阻止执行(AWT事件调度线程)直到对话框被解除。
使这有点棘手的是你有三个线程可以使用:AWT事件调度线程,JavaFX应用程序线程和主线程。显示模态对话框将阻止AWT事件调度线程,但您想要(如果我正确理解了问题)要阻止的主线程。此外,您只能在AWT事件调度线程上创建,修改和显示Swing组件,并且您只能在JavaFX应用程序线程上创建,修改和显示JavaFX节点。
所以我认为你想要这样的东西:
public class SSO {
private JDialog dialog;
private JFXPanel fxPanel;
private JPanel containerPanel;
final static String WEBVIEW = "WebView";
public SSO(){
// creates Swing components: MUST be called on AWT Event thread
createPopup();
}
private void createPopup(){
Dimension size = new Dimension(480, 80);
dialog = new JDialog();
dialog.setTite("Web Login");
// make dialog modal:
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
// oops: don't want this:
// dialog.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// but this:
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
containerPanel = new JPanel(new CardLayout());
fxPanel = new JFXPanel();
fxPanel.setSize(size);
Platform.runLater(() -> initJFXPanel(dialog, fxPanel));
containerPanel.add(fxPanel, WEBVIEW);
dialog.add(containerPanel, BorderLayout.CENTER);
dialog.setPreferredSize(size);
dialog.setResizable(false);
dialog.getRootPane().setWindowDecorationStyle(JRootPane.NONE);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
}
private void initJFXPanel(final JDialog dialog, final JFXPanel fxPanel){
Group group = new Group();
Scene scene = new Scene(group);
fxPanel.setScene(scene);
WebView webview = new WebView();
group.getChildren().add(webview);
webview.setMinSize(500, 640);
webview.setMaxSize(500, 640);
CookieManager cookieManager = new CookieManager();
java.net.CookieHandler.setDefault(cookieManager);
webview.getEngine().load("http://google.com/");
if(webview.getEngine().getLocation().equalsIgnoreCase("cookie url")) {
// harvest the cookies
}
else if(webview.getEngine().getLocation().equalsIgnoreCase("end url")) {
// this will hide the dialog and release the event dispatch thread
// that is waiting for it, but it must be called on the event dispatch thread
// not sure of your exact requirements, but you may need to do this
// in the if clause as well?
SwingUtilities.invokeLater(() -> dialog.setVisible(false));
}
}
}
现在,如果您实例化SSO
,代码将会阻止,直到对话框被解除。但是,您必须在AWT Event Dispatch线程上执行此操作。等待主线程完成的技巧是将new SSO()
的调用表示为FutureTask
,将FutureTask
提交给SwingUtilities.invokeLater()
,然后重新开启主线程,在get()
上调用FutureTask
:这将阻塞,直到任务完成。你需要在这里小心:如果你无意中在事件调度线程上执行此操作,最终会陷入死锁,因为事件调度线程将阻塞,等待事件调度线程发生某些事情...所以你可能想要一个防止这是安全的。
所以你的代码可能需要看起来像这样:
public class LoginManager {
private boolean bSSOEnabled = true;
private void Login() {
// get company details
if(bSSOEnabled) {
if (SwingUtilities.isEventDispatchThread()) {
// already on EDT, just invoke the SSO which will block
// until the dialog is dismissed:
new SSO();
} else {
// create FutureTask to show dialog, etc:
FutureTask<Void> ssoTask = new FutureTask<>(SSO:new, null);
// submit task to AWT event dispatch thread:
SwingUtilities.invokeLater(ssoTask);
// wait for task to complete, i.e. for dialog to be dismissed:
try {
ssoTask.get();
} catch (InterruptedException exc) {
// thread was interrupted, representing cancelation of some kind:
System.out.println("Thread interrupted");
Thread.currentThread().interrupt();
} catch (ExecutionException ee) {
// something went wrong...
throw new RuntimeException(ee);
}
}
}
// once SSO is done, continue with execution
}
}