-
Notifications
You must be signed in to change notification settings - Fork 104
Security configuration
You need to define the security configuration (authentication and authorization mechanisms) in a Config
component.
>> Read the documentation of the Config
component.
The Config
is bound for injection in a SecurityModule
(or whatever the name you call it):
In Java:
public class SecurityModule extends AbstractModule {
...
@Override
protected void configure() {
bind(HandlerCache.class).to(Pac4jHandlerCache.class);
bind(Pac4jRoleHandler.class).to(MyPac4jRoleHandler.class);
//final PlayCacheSessionStore playCacheSessionStore = new PlayCacheSessionStore(getProvider(SyncCacheApi.class));
//bind(SessionStore.class).toInstance(playCacheSessionStore);
bind(SessionStore.class).to(PlayCacheSessionStore.class);
// callback
final CallbackController callbackController = new CallbackController();
callbackController.setDefaultUrl("/");
callbackController.setRenewSession(true);
bind(CallbackController.class).toInstance(callbackController);
// logout
final LogoutController logoutController = new LogoutController();
logoutController.setDefaultUrl("/?defaulturlafterlogout");
logoutController.setDestroySession(true);
bind(LogoutController.class).toInstance(logoutController);
}
@Provides @Singleton
protected FacebookClient provideFacebookClient() {
final String fbId = configuration.getString("fbId");
final String fbSecret = configuration.getString("fbSecret");
final FacebookClient fbClient = new FacebookClient(fbId, fbSecret);
fbClient.setMultiProfile(true);
return fbClient;
}
@Provides @Singleton
protected TwitterClient provideTwitterClient() {
return new TwitterClient("HVSQGAw2XmiwcKOTvZFbQ", "FSiO9G9VRR4KCuksky0kgGuo8gAVndYymr4Nl7qc8AA");
}
@Provides @Singleton
protected FormClient provideFormClient() {
return new FormClient(baseUrl + "/loginForm", new SimpleTestUsernamePasswordAuthenticator());
}
@Provides @Singleton
protected IndirectBasicAuthClient provideIndirectBasicAuthClient() {
return new IndirectBasicAuthClient(new SimpleTestUsernamePasswordAuthenticator());
}
@Provides @Singleton
protected CasProxyReceptor provideCasProxyReceptor() {
return new CasProxyReceptor();
}
...
@Provides @Singleton
protected Config provideConfig(FacebookClient facebookClient, TwitterClient twitterClient, FormClient formClient,
IndirectBasicAuthClient indirectBasicAuthClient, CasClient casClient, SAML2Client saml2Client,
OidcClient oidcClient, ParameterClient parameterClient, DirectBasicAuthClient directBasicAuthClient,
CasProxyReceptor casProxyReceptor, DirectFormClient directFormClient) {
//casClient.getConfiguration().setProxyReceptor(casProxyReceptor);
final Clients clients = new Clients(baseUrl + "/callback", facebookClient, twitterClient, formClient,
indirectBasicAuthClient, casClient, saml2Client, oidcClient, parameterClient, directBasicAuthClient,
new AnonymousClient(), directFormClient);
PlayHttpActionAdapter.INSTANCE.getResults().put(HttpConstants.UNAUTHORIZED, unauthorized(views.html.error401.render().toString()).as((HttpConstants.HTML_CONTENT_TYPE)));
PlayHttpActionAdapter.INSTANCE.getResults().put(HttpConstants.FORBIDDEN, forbidden(views.html.error403.render().toString()).as((HttpConstants.HTML_CONTENT_TYPE)));
final Config config = new Config(clients);
config.addAuthorizer("admin", new RequireAnyRoleAuthorizer("ROLE_ADMIN"));
config.addAuthorizer("custom", new CustomAuthorizer());
config.addMatcher("excludedPath", new PathMatcher().excludeRegex("^/facebook/notprotected\\.html$"));
// for deadbolt:
config.setHttpActionAdapter(PlayHttpActionAdapter.INSTANCE);
return config;
}
}
See a full example here.
In Scala:
class SecurityModule(environment: Environment, configuration: Configuration) extends AbstractModule {
val baseUrl = configuration.get[String]("baseUrl")
override def configure(): Unit = {
val sKey = configuration.get[String]("play.http.secret.key").substring(0, 16)
val dataEncrypter = new ShiroAesDataEncrypter(sKey.getBytes(StandardCharsets.UTF_8))
val playSessionStore = new PlayCookieSessionStore(dataEncrypter)
bind(classOf[SessionStore]).toInstance(playSessionStore)
bind(classOf[SecurityComponents]).to(classOf[DefaultSecurityComponents])
bind(classOf[Pac4jScalaTemplateHelper[CommonProfile]])
// callback
val callbackController = new CallbackController()
callbackController.setDefaultUrl("/?defaulturlafterlogout")
bind(classOf[CallbackController]).toInstance(callbackController)
// logout
val logoutController = new LogoutController()
logoutController.setDefaultUrl("/")
bind(classOf[LogoutController]).toInstance(logoutController)
}
...
@Provides
def provideCasClient(casProxyReceptor: CasProxyReceptor) = {
val casConfiguration = new CasConfiguration("https://casserverpac4j.herokuapp.com/login")
//val casConfiguration = new CasConfiguration("http://localhost:8888/cas/login")
casConfiguration.setProtocol(CasProtocol.CAS20)
//casConfiguration.setProxyReceptor(casProxyReceptor)
new CasClient(casConfiguration)
}
@Provides
def provideSaml2Client: SAML2Client = {
val cfg = new SAML2Configuration("resource:samlKeystore.jks", "pac4j-demo-passwd", "pac4j-demo-passwd", "resource:openidp-feide.xml")
cfg.setMaximumAuthenticationLifetime(3600)
cfg.setServiceProviderEntityId("urn:mace:saml:pac4j.org")
cfg.setServiceProviderMetadataPath(new File("target", "sp-metadata.xml").getAbsolutePath)
new SAML2Client(cfg)
}
@Provides
def provideOidcClient: OidcClient = {
val oidcConfiguration = new OidcConfiguration()
oidcConfiguration.setClientId("343992089165-i1es0qvej18asl33mvlbeq750i3ko32k.apps.googleusercontent.com")
oidcConfiguration.setSecret("unXK_RSCbCXLTic2JACTiAo9")
oidcConfiguration.setDiscoveryURI("https://accounts.google.com/.well-known/openid-configuration")
oidcConfiguration.addCustomParam("prompt", "consent")
val oidcClient = new OidcClient(oidcConfiguration)
oidcClient.addAuthorizationGenerator(new RoleAdminAuthGenerator)
oidcClient
}
@Provides
def provideConfig(facebookClient: FacebookClient, twitterClient: TwitterClient, formClient: FormClient, indirectBasicAuthClient: IndirectBasicAuthClient,
casClient: CasClient, saml2Client: SAML2Client, oidcClient: OidcClient, parameterClient: ParameterClient, directBasicAuthClient: DirectBasicAuthClient): Config = {
val clients = new Clients(baseUrl + "/callback", facebookClient, twitterClient, formClient,
indirectBasicAuthClient, casClient, saml2Client, oidcClient, parameterClient, directBasicAuthClient,
new AnonymousClient())
val config = new Config(clients)
config.addAuthorizer("admin", new RequireAnyRoleAuthorizer("ROLE_ADMIN"))
config.addAuthorizer("custom", new CustomAuthorizer)
config.addMatcher("excludedPath", new PathMatcher().excludeRegex("^/facebook/notprotected\\.html$"))
config.setHttpActionAdapter(new DemoHttpActionAdapter())
config
}
}
See a full example here.
You have two options to store the profiles of your authenticated users:
-
the
PlayCacheSessionStore
saves your data in the Play cache -
the
PlayCookieSessionStore
saves your data in the Play session cookie (somehow preserving Play's statelessness) but with the drawback of removing some data (access token, refresh token...)
Bind the session store you want with the SessionStore
:
Java:
bind(SessionStore.class).to(PlayCacheSessionStore.class);
or
bind(SessionStore.class).to(PlayCookieSessionStore.class);
Scala:
bind(classOf[SessionStore]).to(classOf[PlayCacheSessionStore])
or
bind(classOf[SessionStore]).to(classOf[PlayCookieSessionStore])
By default, the PlayCookieSessionStore
internally uses a ShiroAesDataEncrypter
to encrypt your data, which requires the shiro-core
dependency to be explicitly declared.
You can use your own DataEncrypter
(or the ShiroAesDataEncrypter
with your specific key) via:
Java:
DataEncrypter encrypter = new MyDataEncrypter();
PlayCookieSessionStore playCookieSessionStore = new PlayCookieSessionStore(encrypter);
bind(PlaySessionStore.class).toInstance(playCookieSessionStore);
Scala:
val encrypter = new MyDataEncrypter()
val playCookieSessionStore = new PlayCookieSessionStore(encrypter)
bind(classOf[PlaySessionStore]).toInstance(playCookieSessionStore)