Wednesday, 12 March 2014

Shiro JPA realm

This time code only.

1. Entity classes


You will also have to write simple DAOs for entity classes.

1.1 User


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package pl.dziurdziak.shiro.jpaRealm.user;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import pl.dziurdziak.shiro.jpaRealm.permission.Permission;
import pl.dziurdziak.shiro.jpaRealm.role.Role;

@Entity
@Table(name = "users")
public class User {

 @Id
 @GeneratedValue
 private long id;

 @NotNull
 private String login;

 @NotNull
 @Column(length = 64)
 private String password;

 @NotNull
 @Column(length = 80)
 private String salt;

 @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
 private Set<Role> roles;

 @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
 private Set<Permission> permissions;
 
 // getters and setters omitted
}

1.2 Role


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package pl.dziurdziak.easyReckoning.model.role;

import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.validation.constraints.Size;

import pl.dziurdziak.shiro.jpaRealm.user.User;

@Entity
@Table(name = "roles")
public class Role {

 @Id
 @GeneratedValue
 private long id;

 @Size(max = 100)
 private String roleName;

 @ManyToMany(mappedBy = "roles")
 private Set<User> users;

 // getters and setters ommitted
}


1.3 Permission



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package pl.dziurdziak.shiro.jpaRealm.permission;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.Size;

import pl.dziurdziak.shiro.jpaRealm.User;

@Entity
@Table(name = "permissions")
public class Permission {

 @Id
 @GeneratedValue
 private long id;

 @Size(max = 100)
 private String permission;

 @ManyToOne()
 @JoinColumn(name = "USER_ID")
 private User user;

 // getters and setters omitted
}


2. Realm



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package pl.dziurdziak.shiro.jpaRealm.realm;

import java.util.HashSet;
import java.util.Set;

import javax.enterprise.inject.Default;
import javax.inject.Inject;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAccount;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import pl.dziurdziak.shiro.jpaRealm.permission.Permission;
import pl.dziurdziak.shiro.jpaRealm.role.Role;
import pl.dziurdziak.shiro.jpaRealm.user.User;
import pl.dziurdziak.shiro.jpaRealm.user.UserDao;

public class JpaAuthorizingRealm extends AuthorizingRealm {

 public static final String REALM_NAME = "MY_REALM";
 public static final int HASH_ITERATIONS = 200;

 @Inject
 private UserDao userDao;

 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
  final String username = (String) principals.getPrimaryPrincipal();

  final User user = userDao.findByLogin(username);

  final Set<String> roles = new HashSet<>(user.getRoles().size());
  for (final Role role : user.getRoles()) {
   roles.add(role.getRoleName());
  }

  final Set<pl.dziurdziak.shiro.jpaRealm.permission.Permission> permissions = new HashSet<>(user.getPermissions().size());
  for (final Permission permission : user.getPermissions()) {
   permissions.add(new WildcardPermission(permission.getPermission()));
  }

  final SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roles);
  authorizationInfo.setObjectPermissions(permissions);

  return authorizationInfo;
 }

 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) throws AuthenticationException {
  if (!(token instanceof UsernamePasswordToken)) {
   throw new IllegalStateException("Token has to be instance of UsernamePasswordToken class");
  }

  final UsernamePasswordToken userPassToken = (UsernamePasswordToken) token;

  if (userPassToken.getUsername() == null) {
   throw new AccountException("Null usernames are not allowed by this realm.");
  }

  final User user = userDao.findByLogin(userPassToken.getUsername());

  final SimpleAccount simpleAccount = new SimpleAccount(user.getLogin(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()),
    REALM_NAME);

  return simpleAccount;
 }

 @Override
 @Inject
 public void setCredentialsMatcher(final CredentialsMatcher credentialsMatcher) {
  super.setCredentialsMatcher(credentialsMatcher);
 }

}

3. Utils class



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package pl.dziurdziak.shiro.jpaRealm;

import javax.enterprise.inject.Default;
import javax.enterprise.inject.Produces;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.hash.SimpleHash;

import pl.dziurdziak.shiro.jpaRealm.realm.JpaAuthorizingRealm;

public final class JpaSecurityUtil {

 public static final int SALT_LENGTH = 80;
 public static final int PASSWORD_LENGTH = 64;

 public static String getSalt() {
  return new SecureRandomNumberGenerator().nextBytes(60).toBase64();
 }

 public static String hashPassword(final String value, final String salt) {
  final Sha256Hash sha256Hash = new Sha256Hash(value, salt, VaadinAuthorizingRealm.HASH_ITERATIONS);
  return sha256Hash.toHex();
 }

 @Produces
 @Default
 public CredentialsMatcher getCredentialMatcher() {
  final HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
  credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
  credentialsMatcher.setHashIterations(JpaAuthorizingRealm.HASH_ITERATIONS);
  return credentialsMatcher;
 }

 @Produces
 @Default
 public SimpleHash getHash() {
  final Sha256Hash sha256Hash = new Sha256Hash();
  sha256Hash.setIterations(JpaAuthorizingRealm.HASH_ITERATIONS);
  return sha256Hash;
 }
}

4. Usage



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package pl.dziurdziak.shiro.jpaRealm;

import pl.dziurdziak.shiro.jpaRealm.user.User;
import pl.dziurdziak.shiro.jpaRealm.user.UserDao;

public class TestClass {

 @Inject
 private UserDao userDao;

 public void register(final String login, final String password) {
  User user = new User();
  user.setLogin(login);
  user.setSalt(JpaSecurityUtil.getSalt());
  user.setPassword(JpaSecurityUtil.hashPassword(password, user.getSalt()));
  userDao.persist(user);
 }
}