Hot questions for Using GlassFish in security

Question:

I have a JAX-RS service where I want all my users to access my services, but just those who have rights to see the result. Roles based security and existing REALMS and atuhentication methods doesn't fit my requirement.

For example:

  1. user authenticates against one REST service and I send him JWT token with his ID
  2. user asks for other resource and sends his JWT with his ID in each request
  3. I check his user id (from JWT) and if the business logic returns result I send them back, else I send empty result set or specific HTTP status

Question is: Where should I check for users ID, in some separate filter, security context or in every REST method implementation? How to provide REST methods with this ID, can securityContext be injected in every method after filtering request by ID?

I'm using GlassFish 4.1 and Jersey JAX-RS implementation.


Answer:

You can perform this logic in a ContainerRequestFilter. It pretty common to handle custom security features in here.

Some things to consider

  1. The class should be annotated with @Priority(Priorities.AUTHENTICATION) so it is performed before other filters, if any.

  2. You should make use of the SecurityContext, inside the filter. What I do is implement a SecurityContext. You can really implement it anyway you want.

Here's a simple example without any of the security logic

@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        SecurityContext originalContext = requestContext.getSecurityContext();
        Set<String> roles = new HashSet<>();
        roles.add("ADMIN");
        Authorizer authorizer = new Authorizer(roles, "admin", 
                                               originalContext.isSecure());
        requestContext.setSecurityContext(authorizer);
    }

    public static class Authorizer implements SecurityContext {

        Set<String> roles;
        String username;
        boolean isSecure;
        public Authorizer(Set<String> roles, final String username, 
                                             boolean isSecure) {
            this.roles = roles;
            this.username = username;
            this.isSecure = isSecure;
        }

        @Override
        public Principal getUserPrincipal() {
            return new User(username);
        }

        @Override
        public boolean isUserInRole(String role) {
            return roles.contains(role);
        }

        @Override
        public boolean isSecure() {
            return isSecure;
        }

        @Override
        public String getAuthenticationScheme() {
            return "Your Scheme";
        } 
    } 

    public static class User implements Principal {
        String name;

        public User(String name) {
            this.name = name;
        }

        @Override
        public String getName() { return name; }   
    }
}

A few things to notice

  • I've created a SecurityContext
  • I've added some roles, and used them for the isUserInRole method. This will be used for authorization.
  • I've created a custom User class, that implements java.security.Principal. I returned this custom object
  • Finally I set the new SecurityContext in the ContainerRequestContext

Now what? Let's look at a simple resource class

@Path("secure")
public class SecuredResource {
    @GET
    @RolesAllowed({"ADMIN"})
    public String getUsername(@Context SecurityContext securityContext) {
        User user = (User)securityContext.getUserPrincipal();
        return user.getName();
    }
}

A few things to notice:

  • SecurityContext is injected into the method.
  • We get the Principal and cast it to User. So really you can create any class that implements Principal, and use this object however you want.
  • The use of the @RolesAllowed annotation. With Jersey, there is a filter that checks the SecurityContext.isUserInRole by passing in each value in the @RolesAllowed annotation to see if the User is allowed to access the resource.

    To enable this feature with Jersey, we need to register the RolesAllowedDynamicFeature

    @ApplicationPath("/api")
    public class AppConfig extends ResourceConfig {
    
        public AppConfig() {
            packages("packages.to.scan");
            register(RolesAllowedDynamicFeature.class);
        }
    }
    

Question:

I am working on a Java EE application (Netbeans IDE 8.0.2, Glassfish 4.1, JDK 1.8.0_45).

The access to the pages of the application should be secured by https, so I modified my web.xml:

<security-constraint>
    <web-resource-collection>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <user-data-constraint>
        <description/>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

When I deploy & start Glassfish via Netbeans I can access my application via

https://localhost:8181/MyApp/

When I bundle my application in a .war file and deploy it manually to a copy of the same glassfish installation files netbeans uses, I get an exception while trying to connect to the application:

    WARNING (35) GRIZZLY0013: Exception during FilterChain execution
java.lang.RuntimeException: Could not generate dummy secret
  at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1429)
  at sun.security.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:535)
  at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:813)
  at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
  at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
  at org.glassfish.grizzly.ssl.SSLConnectionContext.unwrap(SSLConnectionContext.java:172)
  at org.glassfish.grizzly.ssl.SSLUtils.handshakeUnwrap(SSLUtils.java:263)
  at org.glassfish.grizzly.ssl.SSLBaseFilter.doHandshakeStep(SSLBaseFilter.java:603)
  at org.glassfish.grizzly.ssl.SSLBaseFilter.doHandshakeStep(SSLBaseFilter.java:552)
  at org.glassfish.grizzly.ssl.SSLBaseFilter.handleRead(SSLBaseFilter.java:273)
  at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
  at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
  at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
  at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
  at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
  at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
  at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
  at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
  at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
  at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
  at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
  at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
  at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
  at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: Could not generate dummy secret
  at sun.security.ssl.RSAClientKeyExchange.<init>(RSAClientKeyExchange.java:132)
  at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:238)
  at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
  at sun.security.ssl.Handshaker$1.run(Handshaker.java:919)
  at sun.security.ssl.Handshaker$1.run(Handshaker.java:916)
  at java.security.AccessController.doPrivileged(Native Method)
  at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369)
  at org.glassfish.grizzly.ssl.SSLUtils.executeDelegatedTask(SSLUtils.java:247)
  at org.glassfish.grizzly.ssl.SSLBaseFilter.doHandshakeStep(SSLBaseFilter.java:638)
  ... 16 more
Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/PKCS1Padding
  at javax.crypto.Cipher.getInstance(Cipher.java:540)
  at sun.security.ssl.JsseJce.getCipher(JsseJce.java:229)
  at sun.security.ssl.RSAClientKeyExchange.<init>(RSAClientKeyExchange.java:115)
  ... 24 more

If I repeat the manual deployment process (.war) without the CONFIDENTIAL transport-guarantee in the web.xml I can access my application on

http://localhost:8080/MyApp/

What am I doing wrong? What does Netbeans right that I am doing wrong manually?


Answer:

I was facing the same problem, when I tried to switch my glassfish 4.1.1 domain to a custom JVM. I updated the domain.xml - which led to a similar Stacktrace.

I resolved the issue by resetting the domain.xml (no specific JVM or java home is specified in there anymore) and configured AS_JAVA in glassfish\config\asenv.bat like this:

SET AS_JAVA=C:\Program Files\Java\...

Question:

The end goal is to provide application client downloads using Java webstart from Glassfish 4.

I've been trying to get this working for 3 days, researching every method I can find and no matter what I try, webstart is blocked.

  • Exception list. Doesn't work.
  • Adding the certificate as a trusted certificate. Doesn't work.
  • Sandbox which doesn't need any permissions. Doesn't work.
  • Updating Java. Doesn't work.
  • I can't seem to find the deployment rule sets option but this sounds like something that needs full windows server integration etc.
  • There is no medium option in the Java console security settings as I am using java 8.0.31.
  • Simple test app that has nothing but static main void which prints a message to command line. Cannot get it to work...

It is starting to drive me crazy that it is impossible to develop anything using webstart, the only options I can see are purchasing a certificate for local development or totally dropping webstart...

How I added the certifacte to my machine - the certificate is shown in my Java console.

Here is the simple scenario I cannot get working:

package com.cbprogramming;

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        System.out.println("Test");
    }


    public static void main(String[] args) {
        launch(args);
    }
}

I then used IntelliJ Idea to create a JavaFX application that packages it including the webstart jar file, JNLP file and html web page including custom manifest fields for permissions: sandbox and codebase.

The JNLP file, I also tried with the security and permissions tags, both all-permissions and sandbox.

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" xmlns:jfx="http://javafx.com" href="WebstartTest.jnlp">
  <information>
    <title>Webstart Test</title>
    <vendor>Testing</vendor>
    <description>A Java Webstart testing app</description>
    <offline-allowed/>
  </information>
  <resources>
    <jfx:javafx-runtime version="8.0+" href="http://javadl.sun.com/webapps/download/GetFile/javafx-latest/windows-i586/javafx2.jnlp"/>
  </resources>
  <resources>
    <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se"/>
    <jar href="WebstartTest.jar" size="1190" download="eager" />
  </resources>
<security>
  <all-permissions/>
</security>
  <applet-desc  width="600" height="400" main-class="com.javafx.main.NoJavaFXFallback"  name="WebstartTest" >
    <param name="requiredFXVersion" value="8.0+"/>
  </applet-desc>
  <jfx:javafx-desc  width="600" height="400" main-class="com.test.Main"  name="WebstartTest" />
  <update check="background"/>
</jnlp>

And the manifest file:

Manifest-Version: 1.0
permissions: sandbox
codebase: file:///d:/test/
JavaFX-Version: 8.0
Class-Path: 
Created-By: JavaFX Packager
Main-Class: com.test.Main

Name: com/test/Main.class
SHA-256-Digest: 8BK5m/ojirCK/QEx8Oe+9z/L6P8JXin0CMDK4R2mkAI=

I have added the jnlp, jar and html files to the exceptions list, I've tried both with file:// and file:///, I've also tried adding the glassfhish URL to the exception list, http and https...

I am developing on a Win 8.1 pro machine using Jdk 8.0.31.

Every forum I have read users are saying any one of these options fix their problem. What am I doing so wrong?!? Is 8.0.31 broken? Or is webstart just not worth using?


Answer:

Here is what I found incase others find it useful.

It looks like Glassfish 4.1 has a webstart bug when using Java 7 update 25 or later (currently 8.0.31). The workaround is to use an older version of Java.

  • I never could get the java console exceptions list to work.
  • Adding the certificate as trusted let webstart work from a local file/html file but it still didn't work through glassfish.
  • The tags needed to be removed from jnlp files now that they are in the jar manifest file or the application was blocked, these tags are added automaticlly by glassfish and intellij JavaFX packager.
  • Another thought is to setup a local certificate authority and add it as trusted through the java console - this way it isn't a self signed certificate.

Also, to get a glassfish application client debugging in IntelliJ:

  • Create a batch file: start "name" cmd /c "<installdir>\glassfish\bin\appclient.bat -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -client <dir>\TEMPSClient.jar"
  • Create a remote debugging configuration and set it to run the created script using the external tool in the before launch section.

This uses the default ports etc. for remote debugging, and will run the application jar in the glassfish client container before attaching the debugger to it. To get console output, redirect stdout and stderr to a log file and attach the log file to your remote debug configuration.

I first tried using the embedded ACC but couldn't get that working (copy/paste from docs has functions that don't even exist...). It would be great if someone knows of a good tutorial for using the embedded ACC.