-
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 (somehow preserving Play's statelessness).
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)