Advanced Features and Techniques in Play Framework

Charith Wickramasinghe
Telexar Technologies
5 min readJul 14, 2024

Security and Authentication

Securing web applications is a critical aspect of web development. Play Framework offers robust security features to protect applications against common threats such as cross-site scripting (XSS), cross-site request forgery (CSRF), and SQL injection.

  1. CSRF Protection: Play provides built-in CSRF protection by default. To enable or customize it, you can modify the configuration in application.conf
# conf/application.conf
play.filters {
csrf {
token.signing.key = "a-very-secure-signing-key"
token.creation = "play.filters.csrf.CSRF.token.createToken"
}
}

2. Authentication: Play supports various authentication mechanisms, including session-based authentication, token-based authentication, and OAuth integration.

Example of session-based authentication:

// app/controllers/AuthController.java

package controllers;

import play.mvc.*;
import play.data.*;
import javax.inject.Inject;
import models.User;

public class AuthController extends Controller {

private final FormFactory formFactory;

@Inject
public AuthController(FormFactory formFactory) {
this.formFactory = formFactory;
}

public Result login() {
return ok(views.html.login.render());
}

public Result authenticate() {
Form<User> loginForm = formFactory.form(User.class).bindFromRequest();
if (loginForm.hasErrors()) {
return badRequest(views.html.login.render());
} else {
session("user", loginForm.get().username);
return redirect(routes.HomeController.index());
}
}

public Result logout() {
session().clear();
return redirect(routes.AuthController.login());
}
}

Database Access with Play Framework

Play Framework supports various databases, including MySQL, PostgreSQL, and MongoDB. Play uses an ORM (Object-Relational Mapping) tool called Ebean for database operations.

  1. Setting Up Database Configuration: Configure the database connection in the application.conf file.
# conf/application.conf

db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost/playdb"
db.default.username="user"
db.default.password="password"

2. Defining Models: Define your models using Ebean annotations.

// app/models/User.java

package models;

import io.ebean.*;
import javax.persistence.*;

@Entity
public class User extends Model {

@Id
public Long id;
public String username;
public String password;

public static final Finder<Long, User> find = new Finder<>(User.class);
}

3. Database Operations: Perform database operations using Ebean’s finder methods.

// app/controllers/UserController.java

package controllers;

import play.mvc.*;
import models.User;

public class UserController extends Controller {

public Result getUser(Long id) {
User user = User.find.byId(id);
if (user == null) {
return notFound("User not found");
}
return ok(views.html.user.render(user));
}

public Result createUser(String username, String password) {
User user = new User();
user.username = username;
user.password = password;
user.save();
return ok("User created");
}
}

Handling WebSockets in Play Framework

WebSockets enable real-time communication between the client and the server. Play provides excellent support for WebSockets, allowing developers to create interactive applications like chat systems and live notifications.

  1. Defining a WebSocket Controller:
// app/controllers/WebSocketController.scala

package controllers

import akka.actor.ActorSystem
import akka.stream.Materializer
import javax.inject._
import play.api.mvc._
import play.api.libs.streams.ActorFlow
import actors.WebSocketActor

@Singleton
class WebSocketController @Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {

def socket = WebSocket.accept[String, String] { request =>
ActorFlow.actorRef { out =>
WebSocketActor.props(out)
}
}
}

2. Creating an Actor for WebSocket:

// app/actors/WebSocketActor.scala

package actors

import akka.actor._

object WebSocketActor {
def props(out: ActorRef) = Props(new WebSocketActor(out))
}

class WebSocketActor(out: ActorRef) extends Actor {
def receive = {
case msg: String =>
out ! s"Received: $msg"
}
}

3. Client-Side WebSocket Implementation:

<!-- public/javascripts/websocket.js -->

var socket = new WebSocket("ws://localhost:9000/socket");
socket.onmessage = function(event) {
var message = event.data;
console.log("Received message: " + message);
};
socket.onopen = function() {
socket.send("Hello, WebSocket!");
};

Caching in Play Framework

Caching can significantly improve the performance of your Play applications by storing frequently accessed data in memory.

  1. Enabling Cache: Play supports several cache backends, including Ehcache and Redis. Enable and configure your preferred cache in application.conf.
# conf/application.conf

play.modules.enabled += "play.api.cache.ehcache.EhCacheModule"

play.cache.bindCaches = ["db-cache", "session-cache"]

2. Using Cache in Controllers:

// app/controllers/CacheController.java

package controllers;

import play.cache.SyncCacheApi;
import play.mvc.*;

import javax.inject.Inject;

public class CacheController extends Controller {

private final SyncCacheApi cache;

@Inject
public CacheController(SyncCacheApi cache) {
this.cache = cache;
}

public Result cacheData(String key, String value) {
cache.set(key, value, 60); // Cache for 60 seconds
return ok("Data cached");
}

public Result getData(String key) {
String value = cache.get(key);
if (value == null) {
return notFound("No data found in cache");
}
return ok(value);
}
}

Handling Cookies in Play Framework

Cookies are a fundamental part of web development, allowing for the storage of small pieces of data on the client side. Play Framework provides comprehensive support for handling cookies, making it easy to read, write, and delete cookies within your web applications.

Setting Cookies

Setting cookies in Play Framework is straightforward. You can set cookies in the response using the withCookies method.

  1. Setting a Simple Cookie:
// app/controllers/CookieController.java

package controllers;

import play.mvc.*;

public class CookieController extends Controller {

public Result setCookie() {
Http.Cookie cookie = Http.Cookie.builder("username", "JohnDoe")
.withMaxAge(3600) // Cookie expires in 1 hour
.withPath("/")
.build();
return ok("Cookie set").withCookies(cookie);
}
}

2. Setting a Cookie with Additional Attributes:

// app/controllers/CookieController.java

package controllers;

import play.mvc.*;

public class CookieController extends Controller {

public Result setSecureCookie() {
Http.Cookie cookie = Http.Cookie.builder("sessionId", "abc123")
.withMaxAge(3600) // Cookie expires in 1 hour
.withPath("/")
.withSecure(true) // Secure flag ensures cookie is sent over HTTPS
.withHttpOnly(true) // HttpOnly flag prevents access from JavaScript
.build();
return ok("Secure cookie set").withCookies(cookie);
}
}

3. Reading Cookies in Java:

// app/controllers/CookieController.java

package controllers;

import play.mvc.*;

import javax.inject.Inject;

public class CookieController extends Controller {

@Inject
public CookieController() {
}

public Result readCookie(Http.Request request) {
Http.Cookie cookie = request.cookie("username");
if (cookie != null) {
String username = cookie.value();
return ok("Username from cookie: " + username);
} else {
return notFound("Cookie not found");
}
}
}

4. Deleting Cookies in Java:

// app/controllers/CookieController.java

package controllers;

import play.mvc.*;

import javax.inject.Inject;

public class CookieController extends Controller {

@Inject
public CookieController() {
}

public Result deleteCookie(Http.Request request) {
Http.Cookie cookie = Http.Cookie.builder("username", "")
.withMaxAge(0) // Immediately expire the cookie
.withPath("/")
.build();
return ok("Cookie deleted").withCookies(cookie);
}
}

Conclusion

In this continuation of our exploration of the Play Framework, we delved into more advanced features that make Play a powerful tool for modern web development. From security measures and database integration to real-time WebSocket communication and efficient caching, Play Framework equips developers with the tools necessary to build robust and scalable web applications. By leveraging the capabilities of both Java and Scala, developers can create highly responsive, maintainable, and secure applications that meet the demands of today’s digital landscape.

Whether you’re just starting with Play or looking to enhance your existing applications, the Play Framework provides a comprehensive and flexible environment that supports a wide range of web development needs.

--

--