Accessing Tomcat JAAS-secured (Form-based) JAX-RS endpoint, from within Java EE client

Unlike my previous post here, sometimes we do need to access JAX-RS resource which is secured by the “traditional” approach of using FORM-based authentication (i.e. jsecuritycheck).

I thought it would be good to share how I do it from within Java client.

Please note that this post is based on (Bien 2013).

Firstly, the thing about ‘jsecurity’-check in Tomcat…

After much reading, I found some help from the following entry at a href=”www.stackoverflow.com” target=”_blank”>stackoverflow.com:

that to get some resource on the tomcat server with j_security_check auth it is necessary to implement three steps:

  1. Send GET request to the needed private resource, in response you get a cookie (Header “Set cookie”)
  2. Send request with cookie (from step 1) to the j_security_check. On response you should get code 302 – “Moved Temporarily”
  3. Now you can repeat request to the private resource with same cookie, on responce you get needed resource data.

(Alexander 2011)

Let’s look at some code

I have put comments in the code to explain what is going on in relation to the three points above. But basically what is happening is that we intercept (Kops 2013) the request in order to first make another request authenticating against jsecuritycheck.

– Register the ‘Authenticator’ when making the call

final Client client = ClientBuilder.newClient().register(new Authenticator("http://myapplication.com/myweb", "username", "password"));
final WebTarget target = client.target("http://myapplication.com/myweb/resources/person");
final JsonObject response = target.request(MediaType.APPLICATION_JSON_TYPE).get(JsonObject.class);
// do something with the response

– Our ‘Authenticator’, which is a ‘javax.ws.rs.client.ClientRequestFilter’

package id.co.lucyana.hr.util;

// imports omitted

@Provider
public final class Authenticator implements ClientRequestFilter {
    private static final String COOKIE = "Cookie";

    // security params
    private static final String J_SECURITY_CHECK = "j_security_check";
    private static final String J_USERNAME = "j_username";
    private static final String J_PASSWORD = "j_password";

    private final String username;
    private final String password;
    private final String baseUri;
    
    // requires by @Provider
    public Authenticator() {
        this.username = null;
        this.password = null;
        this.baseUri = null;
    }

    public Authenticator(final String baseUri, final String username, final String password) {
        this.username = username;
        this.password = password;
        this.baseUri = baseUri;
    }

    @Override
    public void filter(final ClientRequestContext requestContext) throws IOException {
        final List<Object> cookies = new ArrayList<>();

        /*
         * This is hitting the URL as requested by the ClientBuilder.
         * (Refer to the line: "1. Send GET request to the needed private resource,
         * in response you get a cookie (Header “Set cookie”)"
         */
        final Client initialClient =  ClientBuilder.newClient();
        final Response responseInitial = initialClient
                .target(requestContext.getUri())
                .request(requestContext.getAcceptableMediaTypes().get(0))
                .method(requestContext.getMethod());
        initialClient.close();

        /*
         * This section is getting the cookie above and use it to make the call against jsecuritycheck
         * (Refer to the line: 2. Send request with cookie (from step 1) to the j_security_check.
         * On response you should get code 302 – “Moved Temporarily”)
         */
        responseInitial.getCookies().values().stream().forEach((cookie) -> {
            cookies.add(cookie.toCookie());
        });
        final Client loginClient = ClientBuilder.newClient();
        final Form form = new Form();
        form.param(J_USERNAME, this.username);
        form.param(J_PASSWORD, this.password);
        final Response loginResponse = loginClient.target(this.baseUri).path(J_SECURITY_CHECK)
                .request(MediaType.APPLICATION_FORM_URLENCODED)
                .header(COOKIE, cookies)
                .post(Entity.form(form));
        loginClient.close();
        loginResponse.getCookies().values().stream().forEach((cookie) -> {
            cookies.add(cookie.toCookie());
        });

        /*
         * This is right before making the actual call. So we add the cookie (which the server will be
         * able to tell that this call has been authenticated).
         * (Refer to the line: 3. Now you can repeat request to the private resource with same cookie,
         * on responce you get needed resource data.)
         */
        requestContext.getHeaders().put(COOKIE, cookies);
    }
}

References

Alexander, 2011, ‘Request to j_security_check return 408 error only with right paramters’, stackoverflow.com, accessed on 28 November 2016

Bien, A, 2013, ‘Client-side HTTP Basic Authentication With JAX-RS 2.0’, Adam Bien’s weblog, accessed on 28 November 2016

Kops, M, 2013, ‘JAX-RS 2.0 REST Client Features by Example’, hasCode.com, accessed on 28 November 2016

Advertisements

Sending email on Apache TomEE

Recently I am playing with Apache TomEE, since I want to create an application with only a subset of Java EE functionality. So, TomEE as Java EE Web-profile fits well 😉

This time around I am blogging about my findings on sending email with TomEE. Actually having done all the troubleshooting, it is quite simple, and hence it is going to be quite a short article.

Setup

Setting up TomEE is very simple, so I will not go into that. Just a note here I am using GMail account, so go and create your GMail account.

Here is another important part. Make sure you you turn ON 'Allow less secure apps' on Google settings here.

Configuring ‘tomee.xml’

Next, configure the file on <installation location>/conf/tomee.xml

<?xml version="1.0" encoding="UTF-8"?>
<tomee>
    <!-- see http://tomee.apache.org/containers-and-resources.html -->

    <!-- activate next line to be able to deploy applications in apps -->
    <!-- <Deployments dir="apps" /> -->
    <Resource id="tomee/mail/GMailSMTP" type="javax.mail.Session">
	mail.smtp.host=smtp.gmail.com
	mail.smtp.starttls.enable=true
	mail.smtp.port=587
	mail.transport.protocol=smtp
	mail.smtp.auth=true
	mail.smtp.user=<!-- your email address -->
	password=<!-- your password, and not 'mail.smtp.password' -->	
    </Resource>
</tomee>

Use it by injection of ‘@Resource’

Now you can use it via injection

// imports omitted

@Stateless
@LocalBean
@Path(value = "workline")
public class MailService {
    @Resource(mappedName = "java:comp/env/tomee/mail/GMailSMTP")
    private Session smtpSession;

    public boolean sendMail() throws NamingException {
        final Message message = new MimeMessage(this.smtpSession);
        try {
            message.setRecipients(Message.RecipientType.TO, new Address[]{
                new InternetAddress("someone@gmail.com")
            });
            message.setSubject("Email from TomEE");
            message.setSentDate(new Date());
            message.setText("Email from TomEE");
            Transport.send(message);
        } catch (Exception e) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, e);
            return false;
        }
        return true;
    }
}

Troubleshooting:

I had an issue whereby the @Resource injection was null, with the log warning states: WARNING: Injection: No such property .... After hours of google-ing around, apparently this was caused by conflict of the mail library which may be included in my apps, against what is provided by the underlying TomEE server. Marking the scope as provided in my pom.xml fix this (darkzone, 2014).

References

darkZone, 2014, ‘java.lang.ClassCastException: javax.mail.Session cannot be cast to javax.mail.Session’, Stackoverflow.com, accessed on 17 February 2016

virtualskynet, 2013, Code sample: tomee.xml, GitHub, accessed on 17 February 2016