-
Notifications
You must be signed in to change notification settings - Fork 104
Security configuration
You need to define the authentication mechanisms (Client
) and authorization checks (Authorizer
) you want.
The configuration (org.pac4j.core.config.Config
) contains all the clients and authorizers required by the application to handle security.
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(PlaySessionStore.class).toInstance(playCacheSessionStore);
bind(PlaySessionStore.class).to(PlayCacheSessionStore.class);
// callback
final CallbackController callbackController = new CallbackController();
callbackController.setDefaultUrl("/");
callbackController.setMultiProfile(true);
bind(CallbackController.class).toInstance(callbackController);
// logout
final LogoutController logoutController = new LogoutController();
logoutController.setDefaultUrl("/?defaulturlafterlogout");
//logoutController.setDestroySession(true);
bind(LogoutController.class).toInstance(logoutController);
}
@Provides
protected FacebookClient provideFacebookClient() {
final String fbId = configuration.getString("fbId");
final String fbSecret = configuration.getString("fbSecret");
return new FacebookClient(fbId, fbSecret);
}
@Provides
protected TwitterClient provideTwitterClient() {
return new TwitterClient("HVSQGAw2XmiwcKOTvZFbQ", "FSiO9G9VRR4KCuksky0kgGuo8gAVndYymr4Nl7qc8AA");
}
@Provides
protected FormClient provideFormClient() {
return new FormClient(baseUrl + "/loginForm", new SimpleTestUsernamePasswordAuthenticator());
}
...
@Provides
protected SAML2Client provideSaml2Client() {
final SAML2ClientConfiguration cfg = new SAML2ClientConfiguration("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());
return new SAML2Client(cfg);
}
@Provides
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) {
final Clients clients = new Clients(baseUrl + "/callback", facebookClient, twitterClient, formClient,
indirectBasicAuthClient, casClient, saml2Client, oidcClient, parameterClient, directBasicAuthClient,
new AnonymousClient(), casProxyReceptor, directFormClient);
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$"));
config.setHttpActionAdapter(new DemoHttpActionAdapter());
return config;
}
}
See a full example here.
In Scala:
class SecurityModule(environment: Environment, configuration: Configuration) extends AbstractModule {
val baseUrl = configuration.getString("baseUrl").get
override def configure(): Unit = {
bind(classOf[PlaySessionStore]).to(classOf[PlayCacheSessionStore])
// callback
val callbackController = new CallbackController()
callbackController.setDefaultUrl("/?defaulturlafterlogout")
callbackController.setMultiProfile(true)
bind(classOf[CallbackController]).toInstance(callbackController)
// logout
val logoutController = new LogoutController()
logoutController.setDefaultUrl("/")
bind(classOf[LogoutController]).toInstance(logoutController)
// security components used in controllers
bind(classOf[SecurityComponents]).to(classOf[DefaultSecurityComponents])
}
...
@Provides
def provideCasProxyReceptor: CasProxyReceptor = new CasProxyReceptor()
@Provides
def provideCasClient(casProxyReceptor: CasProxyReceptor) = {
val casConfiguration = new CasConfiguration("http://localhost:8888/cas/login") // ("https://casserverpac4j.herokuapp.com/login")
casConfiguration.setProtocol(CasProtocol.CAS20)
casConfiguration.setProxyReceptor(casProxyReceptor)
new CasClient(casConfiguration)
}
@Provides
def provideOidcClient: OidcClient[OidcProfile] = {
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[OidcProfile](oidcConfiguration)
oidcClient.addAuthorizationGenerator(new RoleAdminAuthGenerator)
oidcClient
}
@Provides
def provideParameterClient: ParameterClient = {
val jwtAuthenticator = new JwtAuthenticator()
jwtAuthenticator.addSignatureConfiguration(new SecretSignatureConfiguration("12345678901234567890123456789012"))
val parameterClient = new ParameterClient("token", jwtAuthenticator)
parameterClient.setSupportGetRequest(true)
parameterClient.setSupportPostRequest(false)
parameterClient
}
@Provides
def provideDirectBasicAuthClient: DirectBasicAuthClient = new DirectBasicAuthClient(new SimpleTestUsernamePasswordAuthenticator)
@Provides
def provideConfig(facebookClient: FacebookClient, twitterClient: TwitterClient, formClient: FormClient, indirectBasicAuthClient: IndirectBasicAuthClient,
casClient: CasClient, saml2Client: SAML2Client, oidcClient: OidcClient[OidcProfile], parameterClient: ParameterClient, directBasicAuthClient: DirectBasicAuthClient,
casProxyReceptor: CasProxyReceptor): Config = {
val clients = new Clients(baseUrl + "/callback", facebookClient, twitterClient, formClient,
indirectBasicAuthClient, casClient, saml2Client, oidcClient, parameterClient, directBasicAuthClient,
new AnonymousClient(), casProxyReceptor)
val config = new Config(clients)
config.addAuthorizer("admin", new RequireAnyRoleAuthorizer[Nothing]("ROLE_ADMIN"))
config.addAuthorizer("custom", new CustomAuthorizer)
config.addMatcher("excludedPath", new PathMatcher().excludeRegex("^/filter/facebook/notprotected\\.html$"))
config.setHttpActionAdapter(new DemoHttpActionAdapter())
config
}
}
See a full example here.
http://localhost:8080/callback
is the url of the callback endpoint, which is only necessary for indirect clients.
Notice that you can also configure a specific HttpActionAdapter
to handle specific HTTP actions (like redirections, forbidden / unauthorized pages) via the setHttpActionAdapter
method of the Config
object. The default available implementation is the DefaultHttpActionAdapter
, but you can subclass it to define your own HTTP 401 / 403 error pages for example.
Notice that you can also define matchers via the addMatcher(name, Matcher)
method.
You can also define a specific SecurityLogic
via the setSecurityLogic
method.
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 PlaySessionStore
:
Java:
bind(PlaySessionStore.class).to(PlayCacheSessionStore.class);
or
bind(PlaySessionStore.class).to(PlayCookieSessionStore.class);
Scala:
bind(classOf[PlaySessionStore]).to(classOf[PlayCacheSessionStore])
or
bind(classOf[PlaySessionStore]).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)