Hi All,

for anybody interested in using MongoDB as a Datastore for his realm, here
are the relevant code snippets for doing so:

shiro.ini:
[main]
mongoRealm = de.versatec.mongo.shiro.MongoRealm
-----------------------
MongoRealm.java:
package de.versatec.mongo.shiro;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.query.Query;
import com.mongodb.MongoClient;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class MongoRealm extends AuthorizingRealm {

    private MongoProvider mongoProvider; // a singleton for accessing
MongoDB
    private MongoClient mongoClient = null;
    private Datastore ds = null;

    public MongoRealm() { // I do this to get a singleton Mongo client
(mongo java driver does connection pooling on its own)
        try {
            InitialContext initialContext = new InitialContext();
            BeanManager bm = (BeanManager)
initialContext.lookup("java:comp/BeanManager");
            Bean<MongoProvider> bean = (Bean<MongoProvider>)
bm.getBeans(MongoProvider.class).iterator().next();
            CreationalContext<MongoProvider> ctx =
bm.createCreationalContext(bean);
            mongoProvider = (MongoProvider) bm.getReference(bean,
MongoProvider.class, ctx);
            if (mongoClient == null && mongoProvider != null) {
                mongoClient = mongoProvider.getMongoClient();
            }
            if (ds == null && mongoProvider != null) {
                ds = mongoProvider.getDs();
            }
        } catch (NamingException ex) {
            Logger.getLogger(MongoRealm.class.getName()).log(Level.SEVERE,
null, ex);
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection
pc) {
        Set<String> roles = new HashSet<>();
        Set<Permission> permissions = new HashSet<>();
        for (Object tmp : pc.fromRealm(getClass().getName())) {
            User user =
ds.find(User.class).field("name").equal(tmp.toString()).get();
            if (user != null) {
                roles.addAll(user.getRoles());
                for (String temp : roles) { // in a multi-tenant environment
different tenants may use the same role names, therefore we have to reduce
the list of roles to those for the specific tenant
                    Query<Role> q = ds.createQuery(Role.class);
                    q.field("name").equal(temp);
                   
q.field("tenantId.identifier").equal(user.getTenantId().getIdentifier());
                    Role role = q.get();
                    if (role != null) {
                        permissions.addAll(role.getPermissions());
                    }
                }
            }
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(roles);
        info.setObjectPermissions(permissions);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
at) throws AuthenticationException {
        User user =
ds.find(User.class).field("name").equal(at.getPrincipal().toString()).get();
        if (user != null) {
            return new SimpleAuthenticationInfo(user.getName(),
user.getPassword(), getClass().getName());
        }
        throw new AuthenticationException();
    }
}
--------------------
MongoProvider.java:
package de.versatec.mongo.shiro;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.mongodb.MongoClient;
import java.net.UnknownHostException;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.faces.context.FacesContext;

@ApplicationScoped
public class MongoProvider {

// this resource is configured in glassfish 3.1.2 as JNDI -> Custom
Resources -> 'nosql/qmmongo', type: java.util.Properties
// this way I need not change source code or configuration file when
database connection parameters change
// *but you may just as well hard code your database connection parameters
instead*
    @Resource(name = "nosql/qmmongo")
    private Properties mongoConnection = null; 
    private MongoClient mongoClient = null;
    private Morphia morphia = null;
    private Datastore ds = null;

    public MongoProvider() {
    }

    @PostConstruct
    public void init() {
        String options = mongoConnection.getProperty("options");
        if (mongoClient == null) {
            try {
                mongoClient = new
MongoClient(mongoConnection.getProperty("host:port"));
            } catch (UnknownHostException ex) {
                ex.printStackTrace();
            }
        }
        if (morphia == null && mongoClient != null) {
            morphia = new Morphia();
            morphia.mapPackage("de.versatec.mongobase");
        }
        if (ds == null && morphia != null) {
            ds = morphia.createDatastore(mongoClient, "frameDB");
        }
    }
... // getters and setters
}
--------------------
User.java:
package de.versatec.mongo.shiro;

import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import org.bson.types.ObjectId;

@Entity
public class User implements Serializable {

    @Id
    private ObjectId id;
    private String name;
    private String password;
    @Embedded
    private TenantId tenantId;
    private Set<String> roles = new HashSet<>();

 ... // getters and setters
}
-----------------------
Role.java:
package de.versatec.mongo.shiro;

import com.google.code.morphia.annotations.Embedded;
import com.google.code.morphia.annotations.Entity;
import com.google.code.morphia.annotations.Id;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.authz.Permission;
import org.bson.types.ObjectId;

@Entity
public class Role {

    @Id
    private ObjectId id;
    private String name;
    private Set<Permission> permissions = new HashSet<>();
    @Embedded
    private TenantId tenantId;

    public Role(String name) {
        this.name = name;
    }

    public Role() {
    }
... // getters and setters
}
-------------------------
TenantId.java (yes we use a multi-tenant approach, but you do *not *have to
do the same):
package de.versatec.mongo.shiro;

import com.google.code.morphia.annotations.Embedded;

@Embedded
public class TenantId {

    private String identifier;

    public TenantId(String identifier) {
        this.identifier = identifier;
    }

    public TenantId() {
    }

    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }
}



--
View this message in context: 
http://shiro-user.582556.n2.nabble.com/Example-MongoDB-Realm-tp7579029.html
Sent from the Shiro User mailing list archive at Nabble.com.

Reply via email to