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.
--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.
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.
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. ___
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.
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.
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