How to use Shiro with JDBC on JavaEE6 and Glassfish
Tech-Today

How to use Shiro with JDBC on JavaEE6 and Glassfish


Before you proceed with this tutorial, it's best to from this link to know how to setup a JavaEE6 project with Shiro integrated:
http://czetsuya-tech.blogspot.com/2012/10/how-to-integrate-apache-shiro-with.html

From there we will focus on the changes:
1.) Update shiro.ini

[main]
saltedJdbcRealm=com.ctr.mdl.commons.web.security.shiro.JdbcRealmImpl

# any object property is automatically configurable in Shiro.ini file
saltedJdbcRealm.jndiDataSourceName=dummyDS

# the realm should handle also authorization
saltedJdbcRealm.permissionsLookupEnabled=true

# If not filled, subclasses of JdbcRealm assume "select password from users where username = ?"
# first result column is password, second result column is salt
saltedJdbcRealm.authenticationQuery = SELECT password, salt FROM crm_users WHERE username = ?

# If not filled, subclasses of JdbcRealm assume "select role_name from user_roles where username = ?"
saltedJdbcRealm.userRolesQuery = SELECT name FROM crm_roles a INNER JOIN crm_user_roles b ON a.id=b.role_id INNER JOIN crm_users c ON c.id=b.user_id WHERE c.username = ?

# If not filled, subclasses of JdbcRealm assume "select permission from roles_permissions where role_name = ?"
saltedJdbcRealm.permissionsQuery = SELECT action FROM crm_permissions WHERE role = ?

# password hashing specification, put something big for hasIterations
sha256Matcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
sha256Matcher.hashAlgorithmName=SHA-256
sha256Matcher.hashIterations=1
saltedJdbcRealm.credentialsMatcher = $sha256Matcher

cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
securityManager.cacheManager=$cacheManager

shiro.loginUrl = /login.xhtml

[urls]
/login.xhtml = authc
/logout = logout

Things to take note:
1.) We should extend JdbcRealm to implement a salted password.
2.) Create a datasource in Glassfish named: dummyDS. For this project, I've use postgresql.
3.) I've enabled permission lookup.
4.) I've enabled ehcache, by adding dependency to pom.xml:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.1</version>
</dependency>

New/Updated classes:
JdbcRealmImpl:
package com.ctr.mdl.commons.web.security.shiro;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.util.JdbcUtils;
import org.apache.shiro.util.SimpleByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Edward P. Legaspi
* @since Oct 15, 2012
*/
public class JdbcRealmImpl extends JdbcRealm {
private static final Logger log = LoggerFactory
.getLogger(JdbcRealmImpl.class);

protected String jndiDataSourceName;

public JdbcRealmImpl() {

}

public String getJndiDataSourceName() {
return jndiDataSourceName;
}

public void setJndiDataSourceName(String jndiDataSourceName) {
this.jndiDataSourceName = jndiDataSourceName;
this.dataSource = getDataSourceFromJNDI(jndiDataSourceName);
}

private DataSource getDataSourceFromJNDI(String jndiDataSourceName) {
try {
InitialContext ic = new InitialContext();
return (DataSource) ic.lookup(jndiDataSourceName);
} catch (NamingException e) {
log.error("JNDI error while retrieving " + jndiDataSourceName, e);
throw new AuthorizationException(e);
}
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
// identify account to log to
UsernamePasswordToken userPassToken = (UsernamePasswordToken) token;
String username = userPassToken.getUsername();

if (username == null) {
log.debug("Username is null.");
return null;
}

// read password hash and salt from db
PasswdSalt passwdSalt = getPasswordForUser(username);

if (passwdSalt == null) {
log.debug("No account found for user [" + username + "]");
return null;
}

// return salted credentials
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,
passwdSalt.password, getName());
info.setCredentialsSalt(new SimpleByteSource(passwdSalt.salt));

return info;
}

private PasswdSalt getPasswordForUser(String username) {
PreparedStatement statement = null;
ResultSet resultSet = null;
Connection conn = null;
try {
conn = dataSource.getConnection();
statement = conn.prepareStatement(authenticationQuery);
statement.setString(1, username);

resultSet = statement.executeQuery();

boolean hasAccount = resultSet.next();
if (!hasAccount)
return null;

String salt = null;
String password = resultSet.getString(1);
if (resultSet.getMetaData().getColumnCount() > 1)
salt = resultSet.getString(2);

if (resultSet.next()) {
throw new AuthenticationException(
"More than one user row found for user [" + username
+ "]. Usernames must be unique.");
}

return new PasswdSalt(password, salt);
} catch (SQLException e) {
final String message = "There was a SQL error while authenticating user ["
+ username + "]";
if (log.isErrorEnabled()) {
log.error(message, e);
}
throw new AuthenticationException(message, e);

} finally {
JdbcUtils.closeResultSet(resultSet);
JdbcUtils.closeStatement(statement);
JdbcUtils.closeConnection(conn);
}
}

class PasswdSalt {
public String password;
public String salt;

public PasswdSalt(String password, String salt) {
super();
this.password = password;
this.salt = salt;
}
}
}

And the 3 model classes which basically contains:
User.java
@Entity
@Table(name = "CRM_USERS")
@SequenceGenerator(name = "ID_GENERATOR", sequenceName = "CRM_USERS_SEQ")
public class User implements Serializeable {
private static final long serialVersionUID = 6142315693769197546L;

@Id
@GeneratedValue(generator = "ID_GENERATOR")
private Long id;

@Column(name = "USERNAME", length = 50, unique = true)
private String userName;

@Column(name = "PASSWORD", length = 250)
private String password;

@Column(name = "SALT", length = 100)
private String salt;

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "CRM_USER_ROLES", joinColumns = @JoinColumn(name = "USER_ID"), inverseJoinColumns = @JoinColumn(name = "ROLE_ID"))
private List roles = new ArrayList();
}
Role.java
@Entity(name = "com.ctr.mdl.models.user.Role")
@Table(name = "CRM_ROLES")
@SequenceGenerator(name = "ID_GENERATOR", sequenceName = "CRM_ROLES_SEQ")
public class Role implements Serializable {
private static final long serialVersionUID = 6142315693769197546L;

@Id
@GeneratedValue(generator = "ID_GENERATOR")
private Long id;

@Column(name = "NAME", nullable = false, length = 50)
private String name;

@Column(name = "DESCRIPTION", nullable = false, length = 50)
private String description;

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "CRM_USER_ROLES", joinColumns = @JoinColumn(name = "ROLE_ID"), inverseJoinColumns = @JoinColumn(name = "USER_ID"))
private List users = new ArrayList();
}
Permission.java
@Entity
@Table(name = "CRM_PERMISSIONS")
@SequenceGenerator(name = "ID_GENERATOR", sequenceName = "CRM_PERMISSIONS_SEQ")
public class Permission implements Serializeable {
private static final long serialVersionUID = -2844386098501951453L;

@Column(name = "ROLE", nullable = false)
private String role;

@Column(name = "ACTION", nullable = false, length = 1500)
private String action;

public String getRole() {
return role;
}
}

 Reference: http://meri-stuff.blogspot.com/2011/04/apache-shiro-part-2-realms-database-and.html

Related:
Seam Security: http://czetsuya-tech.blogspot.com/2013/06/how-to-setup-seam3-security-in-jboss-7.html




- How To Implement A Lazydatamodel With Size In Primefaces
This solution is ideal if you have a list of entities or a named queries that return a list of entities. For example let's say we have an entity user and we want to return all the users with role=x. @NamedQueries({ @NamedQuery(name = "User.listUsersByRoles",...

- Jax-rs 2.0 Security Tutorial In Javaee And Jboss
This tutorial will summarize how the author was able to call a secured rest webservice using resteasy. We will not go into detail on how we build the entire project since the code is already pushed at github. Basically we will just note down the most...

- How To Integrate Apache Shiro With Javaee6
This tutorial will show the readers, how I was able to integrate Apache Shiro with JavaEE6. My technology Stack: JavaEE6, Glassfish3.1.2.2, Apache Shiro1.2 Steps: 1.) Create a maven project and include the following dependency. <dependencies> <dependency>...

- Create A Primary Key Column With Sequence Generated Value In Hibernate
For example you have a class User: package org.kalidad.seamexercises.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.SequenceGenerator; ...

- How To Connect Msaccess Database From Java Using Jdbc Driver
The method is almost similar to my previous code, and like that it's better to explain it by code: private static Connection getConnection() { String driver = "sun.jdbc.odbc.JdbcOdbcDriver"; String url = "jdbc:odbc:anime"; //anime is the database...



Tech-Today








.