Monday, May 19, 2025

GCP PubSub Receive Example

This is part two of the GCP (Google Cloud Platform) PubSub example. Part one is GCP PubSub Send Example. In part one, we learned how to set up GCP admin stuff (e.g. billing), GCP PubSub (e.g. topic), Google Cloud SDK Shell, and a lot more. So if you want to know how to make this example work, please read part one first. I am going straight into the GCP PubSub receive code and will not be discussing about how things are set up. That's covered in part one, please do have a read.

Tools and Materials

These are the tools I used to make this example:

  • IntelliJ IDEA 2023.3.4 (Community Edition)
  • openjdk-17.0.10
  • Spring Boot v3.4.5
  • Windows 11
  • Google account
  • Google Cloud SDK Shell
  • Git Bash

The example code is here, github.com/jpllosa/gcp-pubsub. You may grab a copy of it.

Spring Boot GCP PubSub Receive Code

As with part one, GCP PubSub Send Example. There are no changes to our pom.xml. It will have the same dependencies.

  • Spring Web (<artifactId>spring-boot-starter-web</artifactId>) - Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.
  • Spring Integration (<artifactId>spring-boot-starter-integration</artifactId>, <artifactId>spring-integration-http</artifactId>) - Adds support for Enterprise Integration Patterns. Enables lightweight messaging and supports integration with external systems via declarative adapters.
  • Google Cloud Messaging (<artifactId>spring-cloud-gcp-starter-pubsub</artifactId>) - Adds the Google Cloud Support entry and all the required dependencies so that the Google Cloud Pub/Sub integration work out of the box.

Our GCP PubSub message receive code is short and sweet. Just like the send code. We subscribe to a GCP PubSub topic and when a message is receive, we print it out to the console. Here's the code. Explanation follows after.

  
package com.blogspot.jpllosa;

// imports snipped...

@SpringBootApplication
@RestController
public class GcpPubsubApplication {

// some send code snipped...

	@PostMapping("/postMessage")
	public RedirectView postMessage(@RequestParam("message") String message) {
		this.messagingGateway.sendToGcpPubsub(message);
		return new RedirectView("/");
	}

	// Receive
	@Bean
	public MessageChannel gcpPubsubInputChannel() {
		return new DirectChannel();
	}

	@Bean
	public PubSubInboundChannelAdapter messageChannelAdapter(
			@Qualifier("gcpPubsubInputChannel") MessageChannel inputChannel,
			PubSubTemplate pubSubTemplate) {
		PubSubInboundChannelAdapter adapter =
				new PubSubInboundChannelAdapter(pubSubTemplate, "gcp-pubsub-example-sub");
		adapter.setOutputChannel(inputChannel);

		return adapter;
	}

	@ServiceActivator(inputChannel = "gcpPubsubInputChannel")
	public void messageReceiver(String message) {
		System.out.println("Message received: " + message);
	}

}
  

The same with part one, only the GCP PubSub receive bits will be explained. Incoming messages will arrive on the MessageChannel (DirectChannel) we created. Simliar to sending, we have an inbound channel adapter that receives messages from GCP PubSub and forwards it to gcpPubsubInputChannel. The inbound channel adapter is linked to the GCP PubSub subscription name. The adapter is bound and listens for new messages from GCP PubSub gcp-pubsub-example-sub subscription. The method annotated with @ServiceActivator is called when new messages arrive at gcpPubsubInputChannel and we simply print the message on the console. Short and sweet.

Spring Boot GCP PubSub Receive in Action

Let's see our code in action. Run the Spring Boot app (e.g. mvnw spring-boot:run) or thru IntelliJ. When the app is ready, hit the /postMessage endpoint and you should have something like below printed on the console.

  
$ curl --data "message=GCP Pubsub send and receive!" localhost:8080/postMessage
  

We won't be able to see any messages in the GCP PubSub messages section because this happens very quick. If you want to see the messages sent on the messages tab, click PULL to delay the message being consumed. The delay is about 5-ish seconds. It would look like the below.

Spring Boot GCP PubSub Receive Wrap Up

There you have it. Your travel to GCP PubSub has come full circle, assuming you read part one of course. As per usual, we give thanks to Spring for making it easy for us by abstracting whatever it is happening under the hood. Alternatively, you can use other client libaries to connect to GCP PubSub. We created an input channel and attached it to a channel adapter that has subscribed to GCP PubSub. Viola and that is all there is to it! Thank you for reading.

Monday, May 12, 2025

GCP PubSub Send Example

If you are working on Shopify and you need to monitor webhook events (e.g. an order has been created), chances are you'll be directed to use Google Pub/Sub messaging service. In this blog, I'll show you how to setup PubSub on the Google Cloud Platform (GCP), then send a message and check the message on GCP PubSub cloud console.

Tools and Materials

These are the tools I used to make this example:

  • IntelliJ IDEA 2023.3.4 (Community Edition)
  • openjdk-17.0.10
  • Spring Boot v3.4.5
  • Windows 11
  • Google account
  • Google Cloud SDK Shell
  • Git Bash

The example code is here, github.com/jpllosa/gcp-pubsub. You may grab a copy of it.

GCP PubSub Setup

On GCP, create a new project. Like so.

Assign your new project with billing. As of this writing, Google provides free credits to new customers and free usage limits. For Google PubSub, it is 10 GB of messages per month. Please verify for yourselves if this is still the case. Here's what creating a billing account looks like and it being connected to the project ID.

Now that the administrative stuff has all been set up, it's time to create a PubSub topic and subscription.

Google Cloud SDK Shell Setup

Install and initialize the Google Cloud SDK. For installing the Google Cloud SDK Shell, it is better to get it from the horses' mouth. Please follow the Google Cloud documentation on how to install and initialiaze it for your project. Once initialized, enable GCP PubSub. Here are a few checkpoints so you'll know you are heading in the right direction. First and foremost, you must log in with application default credentials using the following command, gcloud auth application-default login. When you are logged, you should have something like below.

Next is to set the shell to your project, gcloud config set project <PROJECT-NAME>. Here's the command to see the available projects, gcloud config projects list. When you have done those, you should have something like below.

Lastly, enable GCP PubSub, gcloud services enable pubsub.googleapis.com. Output would look like below.

For Windows, we can verify that our Google credentials are saved by looking under C:\Users\username\AppData\Roaming\gcloud\application_default_credentials.json. This file should exist.

Spring Boot GCP PubSub Send Code

I used Spring Initialzr to create the skeleton project. The following are the dependencies we need:

  • Spring Web - Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.
  • Spring Integration - Adds support for Enterprise Integration Patterns. Enables lightweight messaging and supports integration with external systems via declarative adapters.
  • Google Cloud Messaging - Adds the Google Cloud Support entry and all the required dependencies so that the Google Cloud Pub/Sub integration work out of the box.

Our pom.xml should at least have the following dependencies.

  
<!-- snipped -->

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>com.google.cloud</groupId>
	<artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.integration</groupId>
	<artifactId>spring-integration-http</artifactId>
</dependency>

<!-- snipped -->
  

Our GCP PubSub message sender code is short and sweet. When an HTTP POST is received, it then submits the message to GCP PubSub. Here's the code. Explanation follows after.

  
package com.blogspot.jpllosa;

// imports snipped...

@SpringBootApplication
@RestController
public class GcpPubsubApplication {

	public static void main(String[] args) {
		SpringApplication.run(GcpPubsubApplication.class, args);
	}

	@MessagingGateway(defaultRequestChannel = "gcpPubsubOutputChannel")
	public interface GcpPubsubOutboundGateway {
		void sendToGcpPubsub(String text);
	}

	@Bean
	@ServiceActivator(inputChannel = "gcpPubsubOutputChannel")
	public MessageHandler messageSender(PubSubTemplate pubsubTemplate) {
		return new PubSubMessageHandler(pubsubTemplate, "gcp-pubsub-example");
	}

	@Autowired
	private GcpPubsubOutboundGateway messagingGateway;

	@PostMapping("/postMessage")
	public RedirectView postMessage(@RequestParam("message") String message) {
		this.messagingGateway.sendToGcpPubsub(message);
		return new RedirectView("/");
	}

}
  

Since this is a blog about GCP PubSub, I'll focus on explaining the code related to it. I won't be talking about the other annotations (e.g. @SpringBootApplication, @PostMapping, etc.). For example, @PostMapping is related to web and nothing to do with GCP PubSub. We just need that endpoint to trigger a send message to GCP PubSub. We can use other client libraries to communicate with GCP PubSub, not just Spring.

We use Spring Integration messaging gateway to write to a channel, gcpPubsubOutputChannel. This channel is still in Spring land. When a message is in this channel, it is then consumed by the MessageHandler (outbound channel adapter) which in turn publishes the message to GCP PubSub. The outbound channel adapter converts the Spring message into a GCP PubSub message and is published to the topic, gcp-pubsub-example. The @ServiceActivator annotation causes this MessageHandler to be applied to any new messages in inputChannel. Lastly, we got a REST endpoint listening for messages which then forward the message to GCP PubSub. Short and sweet.

Spring Boot GCP PubSub Send in Action

Alrighty, run the Spring Boot app. We can do it command line (e.g. mvn spring-boot:run) or thru IntelliJ. I would suggest running it first on Google Cloud SDK Shell. If it works there then I don't see any reason why it won't work on IntelliJ. When the app is ready, hit the /postMessage endpoint.

  
$ curl --data "message=Eagle has landed!" localhost:8080/postMessage
  

Now, head over to GCP PubSub messages section and we should see the messages appear. If not, try reloading the web page or do a PULL. It should look like below.

Spring Boot GCP PubSub Send Wrap Up

Did you enjoy playing around the GCP PubSub service? We now have an idea on how to send messages via the GCP PubSub messaging service. Spring has made it easy for us by abstracting whatever it is happening under the hood. Thank you for reading.

Friday, April 25, 2025

Managing HttpOnly Cookie Example

In my previous blog, Spring Boot HttpOnly Cookie Example, I demonstrated how to use the HttpOnly flag to protect your cookie. Since an HttpOnly cookie cannot be managed on the front-end, here is a demonstration on how to manage it via the back-end. For this example we'll just expire the cookie when the user clicks a button. Shouldn't be tricky so let's get right to it.

Demonstration

Before we begin, these are the tools I used to make this example:

  • IntelliJ IDEA 2023.3.4 (Community Edition)
  • openjdk-17.0.10
  • Spring Boot v3.4.1
  • Windows 11

The example code is here, github.com/jpllosa/httponly-cookie/tree/manage-httponly. Grab a copy of this branch or if you have done the Spring Boot HttpOnly Cookie Example, you can make updates to the code as you wish.

Code Changes

A couple of changes to the code. First, the endpoint to hit that then tells the browser to expire the cookie. Second, an update on the UI to trigger the cookie deletion.

LoginController.java

  
... code snipped ...
import org.springframework.web.bind.annotation.*;

@Controller
public class LoginController {

... code snipped ...

    @GetMapping("/clear-my-session")
    public @ResponseBody String clearMySession(HttpServletResponse response) {

        Cookie cookie = new Cookie("MY_SESSION", "deleted");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(0);
        response.addCookie(cookie);

        return "";
    }
}

  

The /clear-my-session resource simply responds by setting the MaxAge of the cookie to zero for it to be deleted on the browser. In some other languages, in Go for example, you'll have to set the MaxAge to -1 to expire the cookie. So please read the API documentation if you're wondering why MaxAge zero doesn't work. Since we are just running on our local machhine, we don't provide the Path and Domain. When not running on your local machine, most likely you'll need to supply a Path (e.g. "/") and a Domain (e.g. example.com) to make things work.

welcome.html

  
... code snipped ...
<body>
... code snipped ...
<button id="clearMySession">JS Clear MY_SESSION</button>
<button id="beClearMySession">BE Clear MY_SESSION</button>
</body>

<script th:inline="javascript">
    $(document).ready(function() {
        $("#clearMySession").on("click", function() {
            console.log("JS removing MY_SESSION");
            document.cookie = "MY_SESSION=; expires=Thu, 01-Jan-70 00:00:01 GMT;";
        });

        $("#beClearMySession").on("click", function() {
            console.log("BE removing MY_SESSION");

            $.get("/clear-my-session", function() {
                console.log("MY_SESSION removed");
            });
        });
    });
</script>
</html>

  

Here, we added a button that hits the endpoint we created above. Simples.

Ready, Set, Go

Run the Spring Boot app. Go to the login page. Just type any username and password. You should have something like below. Open Web Tools and head over to the Storage tab (it could be called a different name on a different browser). Take note of the cookie named MY_SESSION.

As you already know from the previous blog, clicking on "JS Clear MY_SESSION" (JS for JavaScript) will not do anything. Now, try clicking on "BE Clear MY_SESSION", BE for back-end :). The MY_SESSION cookie disappears right before our eyes and you should have something like below. You can also check the network and console tabs to see what's happening behind the scenes.

Managing HttpOnly Cookie Wrap Up

There you have it. A nice way of managing your cookie from the back-end. Having the HttpOnly flag set prevents thrid parties from accessing your very important cookie. Now, it's only the back-end that can manipulate it. Thank you for reading.

Sunday, January 12, 2025

Spring Boot HttpOnly Cookie Example

Want to protect your cookie? Well, not from Santa Claus. I'm talking about HTTP Cookie. Using the HttpOnly flag when generating a cookie helps mitigate the risk of a client side script accessing the protected cookie. In other words, it won't be accessible programmatically on the client side (e.g. JavaScript). The cookie will be driven by the backend.

Why do this? To mitigate cross-site scripting (XSS) attacks. If a cross-site scripting flaw exists, and a user accidentally accesses a link that exploits this flaw, the browser will not reveal the cookie to a third party. Currently, every major browser supports HttpOnly cookies. If a browser does not support HttpOnly and a website attempts to set an HttpOnly cookie, the HttpOnly flag will be ignored by the browser, thus creating a traditional, script accessible cookie. As a result, the cookie (typically your session cookie) becomes vulnerable to theft or modification by a malicious script. Majority of XSS attacks target theft of session cookies. A server could help mitigate this issue by setting the HttpOnly flag on a cookie it creates, indicating the cookie should not be accessible on the client. If a browser that supports HttpOnly detects a cookie containing the HttpOnly flag, and client side script code attempts to read the cookie, the browser returns an empty string as the result. This causes the attack to fail by preventing the malicious (usually XSS) code from sending the data to an attacker’s website.

Demonstration

Before we begin, these are the tools I used to make this example:

  • IntelliJ IDEA 2023.3.4 (Community Edition)
  • openjdk-17.0.10
  • Spring Boot v3.4.1
  • Windows 11

The example code is here, github.com/jpllosa/httponly-cookie. Download it as you please. Now, on with the show. Run the Spring Boot app. Go to the login page, you should see something like below. Please forgive the look, this demo is about HttpOnly and not about the UI.

Without HttpOnly

Log in. Just type any user name and password. You should have something like below. Open Web Tools and head over to the Storage tab (it could be called a different name on a different browser). Take note of the HttpOnly column of the cookie named MY_SESSION.

This is the code of the Controller serving the page. Take note that we have commented out the line that sets the HttpOnly flag.

  
package com.blogspot.jpllosa.httponly_cookie;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.CookieValue;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String getLogin() {
        return "login";
    }

    @PostMapping("/login")
    public String postLogin(
            @RequestParam(name="username", required=true) String username,
            @RequestParam(name="password", required=true) String password,
            HttpServletResponse response,
            HttpSession session) {

        session.setAttribute("username", username);
        session.setAttribute("password", password);

        Cookie cookie = new Cookie("MY_SESSION", "supercalifragilisticexpialidocious");
//        cookie.setHttpOnly(true);
        response.addCookie(cookie);

        return "redirect:/welcome";
    }

    @GetMapping("/welcome")
    public String welcomeUser(
            @CookieValue(value = "MY_SESSION") String mySession,
            Model model,
            HttpSession session) {

        model.addAttribute("username", session.getAttribute("username"));
        model.addAttribute("password", session.getAttribute("password"));
        model.addAttribute("mySession", mySession);

        return "welcome";
    }

}
  

Now, click "Clear MY_SESSION" button. Notice that the cookie is gone as below.

With HttpOnly

Alright, this time, we'll put in the HttpOnly flag. Uncomment cookie.setHttpOnly(true); and restart the Spring Boot app. Log in again and as usual, have the Storage tab open. What happens when you click the "Clear MY_SESSION" button now? It's still there no matter how many times we click the button as shown on the console logs.

Spring Boot HttpOnly Cookie Conclusion

There you have it. A nice way of protecting your cookie. Having the HttpOnly flag set prevents thrid parties from accessing you very important cookie. It's now the task of the backend to manage the cookie. Thank you for reading.