Authentication function in Play Framework [Access restrictions]

This is a continuation of Last Post. When creating a web application, you often want to register user information and restrict page access. This time, I implemented the authentication function using Play Framework. The source can be found on GitHub.

Functions to be realized

--Authentication by registration and verification of user information (Last post) --Restricted access to the home screen --Transfer of authentication authority when the same user as the authenticating user signs in from another browser

In the previous post, I wrote about saving user information in the DB and signing in. After signing in, the screen transitions to the home screen, but there are times when you want only the signed-in user (browser with user information authentication) to access the home screen. In this post, I would like to see the session of the browser and judge whether it is currently authenticating or not and make the screen transition.

play_session.JPG

I want to make a transition roughly like this. In Play, the session is the information held by the browser cookie, and the cache is the information held in the server memory.

UUID issuance

You need to identify your browser to restrict access. For that I would like to issue a UUID and give it to the session. It implements the play.http.ActionCreator interface. Then define the implemented class name in application.conf.

AppActionCreator.java


package common.global;

//import statement omitted

public class AppActionCreator implements ActionCreator {
	/**UUID session key*/
	public static final String UUID = "UUID";
	
	@Override
	public Action<?> createAction(Request arg0, Method arg1) {
        return new Action.Simple() {
            @Override
            public CompletionStage<Result> call(Http.Context ctx) {
            	/*
            	 *If the UUID does not exist in the session
            	 *Generate a UUID and set it in the session.
            	 */
                if(ctx.session().get(UUID) == null){
                	String uuid = java.util.UUID.randomUUID().toString();
                	ctx.session().put(UUID, uuid);
                }
                return delegate.call(ctx);
            }
        };
	}

}

conf/application.conf


play.http {
  actionCreator = "common.global.AppActionCreator"
}

If you implement this interface, it will go through it before it receives a request and enters routes. If the session does not have a UUID, issue a UUID and set it in the session. I use java.util.UUID to generate the ___UUID, but there is no guarantee that the UUID generated by this is completely unique (although it is unlikely that it is unique). We will not discuss whether it is appropriate for the UUID issuance method here, but when implementing it, consider the appropriate UUID issuance method. ___

Retention of authenticated user information

Once you can identify the browser, you need to know what user information the browser was authenticated with. To do this, save the user information authenticated by sign-in and the UUID of the browser that made the request in the server cache. It is done by setCacheUser (user) in the POST process of SigninController.java.

SigninController.java


package controllers;

//import statement omitted

@Singleton
public class SigninController extends AppController {

    //Omitting members other than cache storage

	/**User information key*/
	public static final String USER_KEY = "USER";
	
	/**
	 *Save user information in cache
	 * @param user user information
	 */
	public void setCacheUser(User user){
		setCache(USER_KEY, user);
		this.cache.set((USER_KEY + user.id), getClientId(), savetime);
	}
	
	/**
	 *Get user information from cache
	 * @return user information
	 */
	public User getCacheUser(){
		Object objectUser = getCache(USER_KEY);
		if(objectUser == null) return null;
		
		/*
		 *The session UUID of the browser that saved the user information
		 *Compare the UUIDs of the sessions you are currently accessing and
		 *If they are different, the user information is not acquired.
		 */
		User user = User.class.cast(objectUser);
		String uuid = this.cache.get(USER_KEY + user.id).toString();
		if(!uuid.equals(getClientId())) return null;
		
		return user;
	}
	
	/**
	 *Clear user information in cache
	 */
	public void clearCacheUser(){
		clearCache(USER_KEY);
	}
	
}

Save the UUID of the session using the user ID saved when saving the user information as a key, obtain the UUID from the cache from the user ID obtained when acquiring the user information, compare it with the UUID of the session, and if the UUID is different, enter the user information. I won't let you get it. This prevents multiple browsers from being authenticated with one user information at the same time.

Restrictions

Once you have identified your browser and retained your user information, all you have to do is verify your authentication when you access the page you want to restrict. You can do that with Play using the play.mvc.Security package. First, create a class that inherits Authenticator and implement two methods.

AppAuthenticator.java


package common.secure;

//import statement omitted

public class AppAuthenticator extends Authenticator {
	/**cache*/
	private CacheApi cache;
	@Inject
	public AppAuthenticator(CacheApi cache){
		this.cache = cache;
	}
	
	@Override
	public String getUsername(Context ctx) {
		/*
		 *Get user information from the cache.
		 *If the user information exists, it is authenticated and access is permitted.
		 */
		SigninController signinController = new SigninController(cache);
		User user = signinController.getCacheUser();
		if(user != null){
			signinController.setCacheUser(user);
			return user.toString();
		}else{
			return null;
		}
	}
	
	@Override
	public Result onUnauthorized(Context ctx) {
		/*
		 *If access is not granted
		 *Redirect to the sign-in screen.
		 */
		return redirect(routes.SigninController.get());
	}
}

If you return some string with getUsername (), you have permission. If null is returned, onUnauthorized () will be called. All you have to do now is add an Authenticated annotation to the home screen controller you want to limit.

IndexController.java


package controllers;

//import statement omitted

@Singleton
public class IndexController extends AppController {
	@Inject
	public IndexController(CacheApi cache) {
		super(cache);
	}

	@Authenticated(AppAuthenticator.class) // <-this
	@Override
    public Result get() {
		/*
		 *Create a home screen with user information and return it.
		 */
		User user = new SigninController(cache).getCacheUser();
        return ok(views.html.index.render(user));
    }

	@Authenticated(AppAuthenticator.class) // <-this
	@Override
	public Result post() {
		/*
		 *Clear user information from the cache
		 *Redirect to the sign-in screen.
		 */
		new SigninController(cache).clearCacheUser();
		return redirect(routes.SigninController.get());
	}

}

Just add annotation to the process you want to restrict and specify the class that implements authentication verification to restrict access.

Finally

I think there are many other ways to authenticate users, but this time I implemented the easiest and simplest sign-in and sign-up. I hope it will be helpful for you. GitHub

Recommended Posts

Authentication function in Play Framework [Access restrictions]
Validation function in Play Framework
Authentication function with Play Framework [Registration and authentication]
[Rails] Function restrictions in devise (login / logout)
Play Framework 2.6 (Java) environment construction in Eclipse
Create authentication function in Rails application using devise
Rails access restrictions
Play Framework study
Handle JSON in cross domain with Play Framework
Play Framework2.5 (Java) Tips
Play Framework studying test
play framework personal notes
If the submodule assets are not found in the Play Framework