Simple Apache Shiro Introduction

Contents


<dependency>
<groupId>org.apache.shiro </groupId >
<artifactId>shiro-core </artifactId >
<version>1.5.1 </version>
</dependency>

The names of classes are marked in bold in this post. So you can easily get started. If you find any mistakes in this blog post please send me an email.

Concepts in Apache Shiro

Shiro is Framework Agnostic

Apache Shiro does not force you into a Framework like Spring Security. Its classes work well together, but can also be used alone.

Example Shiro Use Cases

Use Case 1: You want to model a Simple Authentication Scenario, perhaps for a Tutorial or in a Hackathon Scenario.
Shiro has you covered. Create a SimpleAccountRealm and .addAccount(...) some Accounts on it.
Your can .setCredentialsMatcher(new SimpleCredentialsMatcher()); on it.
This CredentialsMatcher will just compare usernames and passwords.
Use UsernamePasswordToken and SimpleAuthenticationInfo and then ask the SimpleCredentialsMatcher
you just configured to see if the provided token matches with the stored credentials.

Use Case 2: You want to secure a Web Application and do not want to buy into any of these bloated Frameworks like Spring or similar. Perhaps you are using Spark or Javelin, or similar microframework.
In this Case, you can benefit from HashedCredentialsMatcher, SimpleHash, Sha256Hash and similar classes.

Realm Concept

There can be multiple Realm. Such as SimpleAccountRealm. A SimpleAccountRealm is a simple implementation of the Realm Interface. Like every Realm, it can be given a Name. It supports adding Roles and Accounts (in this case, pairs of username,password) to its instance. After adding Accounts and Roles, the Realm can be questioned whether an account or role exists. A CredentialsMatcher can be set on the realm and accessed later on.

SimpleAccountRealm realm = new SimpleAccountRealm("myrealm");

//set credentials to be matched plain (without hashing or similar)
realm.setCredentialsMatcher(new SimpleCredentialsMatcher());
realm.addRole("Admin");
realm.addRole("Dev");

//add an account with username, password and a role
realm.addAccount("username","password","Admin");

realm.accountExists("username"); //returns true
realm.accountExists("peter"); //returns false

realm.roleExists("Admin"); //returns true
realm.roleExists("Magician"); //returns false

AuthenticationInfo and AuthenticationToken

For Authorization Purposes, we can create UsernamePasswordToken Object. It implements AuthenticationToken Interface. From the AuthenticationToken we can then use our users table in the database to create an AuthenticationInfo instance.


SimpleAccountRealm realm = initRealm(); // assume it exists
SecurityManager mg = new DefaultSecurityManager(realm);

SecurityUtils.setSecurityManager(mg);

//implements AuthenticationToken interface
UsernamePasswordToken token = new UsernamePasswordToken("username","password");

//obtain our credentials matcher (the SimpleCredentialsMatcher we configured on our realm earlier)
//and use it to match our credentials
AuthenticationInfo authInfo = new SimpleAuthenticationInfo(token.getPrincipal(),token.getCredentials(),"myrealm");

boolean match = realm
.getCredentialsMatcher()
.doCredentialsMatch(token,authInfo);

//match should be true

Using Hashes and Salts with Apache Shiro

Applications considered Secure should never store plaintext representations of Passwords in a Database. Also, Passwords should be salted with a random salt that is different for every User. Shiro provides Support for Hashing and Salting. The next section shows how to do the hashing part.

Instead of a SimpleCredentialsMatcher we can use a HashedCredentialsMatcher

HashedCredentialsMatcher hcm = new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME);
hcm.setStoredCredentialsHexEncoded(true);
hcm.setHashIterations(1024);
hcm.setHashSalted(true);
realm.setCredentialsMatcher(hcm);

Now it assumes the credentials to be stored in our database in hashed and salted form, and also hex encoded. How can we bring a password in this form?
We can use a custom method to hash and salt passwords before inserting them into the database.
Also we need to store the salt for each user in the database.
As an example, this time the salt is "1234" each time.


private static String hash(String password){
String salt = "1234";
return new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, salt, 1024).toHex();
}
Adjusting the insertion of accounts into our realm:

realm.addAccount("username", hash("password"),"Admin");
We also need to modify our AuthenticationInfo to be a SaltedAuthenticationInfo so that
our HashedCredentialsMatcher can use the salt.


String hashed_pw = hash("password");
SaltedAuthenticationInfo sai = new SimpleAuthenticationInfo(token.getPrincipal(),hashed_pw,new SimpleByteSource("1234"),"myrealm");

CredentialsMatcher matcher = realm.getCredentialsMatcher();

//should be true
boolean match = matcher.doCredentialsMatch(token, sai);

As you can see we were lucky, the hashed password and salt (should be from the database, in our case hardcoded),
returned through SaltedAuthenticationInfo instance were matching.

Roles and Authorization Intro

Similar to howe AuthenticationInfo represents Authentication Information from the database
to be compared with a AuthenticationToken instance (a UsernamePasswordToken instance in our case),
AuthorizationInfo represents a Collection of Permissions and Roles.

Set roles = new HashSet<>();
roles.add("myrole");
AuthorizationInfo ai = new SimpleAuthorizationInfo(roles);

if(ai.getRoles().contains("myrole")){
//will execute
System.out.println("myrole permission !");
}

Using JDBCRealm to connect Shiro to your Database

TODO