How-To Creating a Custom Realm for Authentication
1 Introduction
Publishing server authentication is based on Apache Shiro. For a general understanding it is highly recommended to make yourself comfortable with the general concepts of Shiro.
2 When to use custom realm
Typical use case: A content system has a built-in user management and authentication provider. Users and roles of the content system should be able to login to priint suite using the same user names and passwords as in the content system.
Example code for 'Create a custom realm for authentication': DemoRealm.
3 Authentication data flow
Publishing server comes with a build-in shiro realm – called “pubServerRealm”. This will authenticate users against the internal pubserver repository. Never change or remove this realm, because it is necessary for “admin” or installer/updater access to the server.
If you add another realm authentication will try to authenticate the user for all configured realms until a realm signals authentication success. Authentication fails if the given login data lead to a fail in each realm. Normally the pubServerRealm is the last realm in the sequence.
Typically, when starting an authentication for a realm the login input will be checked if it matches the pattern for the realm. If it does not match the pattern the authentication fails early for the realm and the login data are sent to the next realm in the chain. Patterns can just be defined as regular expressions that must match (for include or exclude). E.g. if a login string looks like an email (e.g. “sandra@boing.eu”) it is evaluated by another realm than a login that resembles a qualified Windows user name (e.g. “SBS-BOINGsandra”).
4 How to use a custom realm
- Implement a custom realm as a standard Java project. As an example, use the code from the DemoRealm project.
- Export your realm project as a jar file and copy it into
<domainDir>/lib
dir. - Create a shiro.ini configuration in
<domainDir>/config
and customize it to your needs. - Restart application server Realm changes get only visible after a restart.
5 Design of a Realm Project in Java
The DemoProject contains the typical pattern for an additional pubserver.
The core pattern is to derive a realm class from Shiro’s AuthorizingRealm and to implement your custom stuff by overriding the doGetAuthenticationInfo method.
After a user login is verified by your custom logic you must assign the user to a user account within publishing server. A user account is represented by the Serveruser class of CometServer4SDK. This is not a part of the public API but of CometServer4SDK but you can use for the current purpose.
Assigning is done by a synchronizeUser business method from CometServerLight. This method also provides a possibility to assign special pubserver roles to the user. This is supported by the membershipMapping field of the AbstractRealmConfig class.
If the user is not already existing in publishing server then it will be created silently in the background by synchronizeUser.
6 Derive your config from AbstractRealmConfig
The fields of the config class are filled in the shiro.ini. Each field defined in your config is available in the shiro.ini, so the information of the config can be extended by new fields easily.
7 Create your own user class.
Available information about users depend on your authentication provider, so you will most likely have to define your own class for users, containing the information necessary for authentication.
In your realm you need to map your user class to Serveruser for user synchronization.
8 Derive your realm from AuthorizingRealm
There are two methods from interface, which must be implemented in your custom realm.
You should create an empty implementation for doGetAuthorizationInfo method. Authorization in pubserver is handling on another level of the system.
You must implement the doGetAuthenticationInfo method as implied by the interface. This is the core method we are dealing with here.
An additional method synchronizeUser with your own User-class should be implemented, to synchronize users into pubserver. This method must be called in doGetAuthenticationInfo.
9 doGetAuthorizationInfo
Should always return null and do nothing else.
10 doGetAuthenticationInfo
Return null if you detect that the login is not valid – by what means ever.
Throw an AuthenticationException if an unexpected error happens to come up. E.g. the contact to the authentication server was lost.
11 synchronizeUser
The simple magic is done by AdminLocal adminBean = CometServer4ServiceLocator.ServiceLocatorEnum.INSTANCE.getAdminLocal();
adminBean.synchronizeUser(serverUser, roleNames, config.getDefaultDataset(), config.getDefaultRole());
Where config is an instance derived from AbstractRealmConfig and serverUser is an instance of the Serveruser class representing a user reference in publishing server.
12 Using shiro.ini
Configuration is done by a special ini file located in the domain config folder. <domainDir>\\config\\shiro.ini
This file is not part of a standard installation of pubserver. In the standard installation an internal version of shiro.ini is used.
This file is read every time when the PubServerKernel is deployed, i.e. during a server start. This means that shiro.ini is not monitored by the system. Changes in the file will only be applied during a restart.
13 Default shiro.ini
This is the content of the default shiro.ini to start with. Just copy the contents into the config>/shiro.ini.
[main]
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager = $sessionManager
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager = $cacheManager
credentialsMatcher = org.apache.shiro.authc.credential.Md5CredentialsMatcher
pubCredentialsMatcher = com.priint.pubserver.auth.realm.PubServerCredentialsMatcher
pubServerAuthenticationToken = com.priint.pubserver.auth.realm.PubServerAuthenticationToken
authcListener1 = com.priint.pubserver.auth.PubServerAuthenticationListener
securityManager.authenticator.authenticationListeners = $authcListener1
cookie = org.apache.shiro.web.servlet.SimpleCookie
cookie.name = PubServSessID
cookie.path = /
securityManager.sessionManager.sessionIdCookie = $cookie
securityManager.sessionManager.globalSessionTimeout = 36000000
sessionListener = com.priint.pubserver.auth.PubServerSessionListener
securityManager.sessionManager.sessionListeners = $sessionListener
sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
sessionValidationScheduler.interval = 3600000
sessionValidationScheduler.sessionManager = $sessionManager
securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler
# DEFAULT REALM
pubServerRealm = com.priint.pubserver.auth.realm.PubServerRealm
pubServerRealm.credentialsMatcher = $pubCredentialsMatcher
# CUSTOM REALMS
# add your custom realms over here
# ...
# attach the realms to shiro
securityManager.realms = $pubServerRealm
14 Adding a custom realm to shiro.ini
Do not change any content before the "# CUSTOM REALMS".
To add a new realm just put the relevant realm configuration settings below the "# CUSTOM REALMS" and finally add the new realm to the list of securityManager.realms. That is all.
The following is a replica of the default shiro.ini with the addition of a DemoRealm. Additions marked in yellow. The most important rule is: Your new realm must precede the “pubServerRealm” when defining “securityManager.realms”. Never remove or change “pubServerRealm”.
Add fields of config to your shiro.ini with <realmname>.config.<fieldname>
.
[main]
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager.sessionDAO = $sessionDAO
securityManager.sessionManager = $sessionManager
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager = $cacheManager
credentialsMatcher = org.apache.shiro.authc.credential.Md5CredentialsMatcher
pubCredentialsMatcher = com.priint.pubserver.auth.realm.PubServerCredentialsMatcher
pubServerAuthenticationToken = com.priint.pubserver.auth.realm.PubServerAuthenticationToken
authcListener1 = com.priint.pubserver.auth.PubServerAuthenticationListener
securityManager.authenticator.authenticationListeners = $authcListener1
# CUSTOM REALM
pubServerRealm = com.priint.pubserver.auth.realm.PubServerRealm
pubServerRealm.credentialsMatcher = $pubCredentialsMatcher
# DEMO REALM
demoRealm = com.priint.pubserver.auth.realm.DemoRealm
demoRealm.config.url = file:///C:/devstack/workspace/demos/DemoRealm/example-users.xml
demoRealm.config.defaultRole = Normal User
demoRealm.config.defaultMenu = publications
demoRealm.config.defaultDataset = demo
demoRealm.config.membershipMapping = Tims:Administrator,Struppies:DTP-Operator
demoRealm.config.includePattern = ^.+@kontoso.com$
demoRealm.config.excludePattern = contoso.com$
securityManager.realms = $demoRealm, $pubServerRealm
# cookie for single sign on is set to PubServSessID and Root Path
# so that we can access it from any web app
cookie = org.apache.shiro.web.servlet.SimpleCookie
cookie.name = PubServSessID
cookie.path = /
securityManager.sessionManager.sessionIdCookie = $cookie
# Set session timeout to 36,000,000 milliseconds = 10 hours
securityManager.sessionManager.globalSessionTimeout = 36000000
sessionListener = com.priint.pubserver.auth.PubServerSessionListener
securityManager.sessionManager.sessionListeners = $sessionListener
# Set session wrapup schedule to 3,600,000 milliseconds = 1 hours
sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
sessionValidationScheduler.interval = 3600000
sessionValidationScheduler.sessionManager = $sessionManager
securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler