Tagged: java-ee
various ways to persist entities in jax-rs resources
references
- Types of EntityManagers — Application-managed EntityManager
- Transactional (Java(TM) EE 7 Specification APIs)
- Declarative Transactions with Java EE 7
- Java EE 7のTransactionalアノテーションを試してみる
via EJB
@Stateless public class TestService { public Test persist(final Test test) { em.persist(test); return test; } @PersistenceContext(unitName = "pu") private EntityManager em; }
@Path("/tests") public class TestsResource { @POST @Path("/1") public void create1(final Test test) { ts.persist(test); } @EJB private TestService ts; }
via PersistenceUnit
@POST @Path("/2") public void create2(final Test test) throws Exception { final EntityManager em = emf.createEntityManager(); try { try { ut.begin(); em.joinTransaction(); em.persist(test); ut.commit(); } catch (final Exception e) { ut.rollback(); throw e; } } finally { em.close(); } } @POST @Path("/3") public void create3(final Test test) throws Exception { try { ut.begin(); final EntityManager em = emf.createEntityManager(); try { em.persist(test); } finally { em.close(); } ut.commit(); } catch (final Exception e) { ut.rollback(); throw e; } } @PersistenceUnit(unitName = "pu") private transient EntityManagerFactory emf; @Resource private UserTransaction ut;
via PersistenceContext
@POST @Path("/4") public void create4(final Test test) throws Exception { try { ut.begin(); em.joinTransaction(); em.persist(test); ut.commit(); } catch (final Exception e) { ut.rollback(); throw e; } } @PersistenceContext(unitName = "pu") private transient EntityManager em; @Resource private UserTransaction ut;
via PersistenceContext with Transactional Interceptors
@POST @Path("/5") @Transactional(rollbackOn = Exception.class) public void create5(final Test Test) throws Exception { em.persist(test); } @PersistenceContext(unitName = "pu") private transient EntityManager em;
general methods and utilities for password encryption
References
Sponsors
Spider Strategies
Morton Salt | “When It Rains It Pours®”
MappedMorton.java
(full source)
There is actually only one class that I’m going to introduce in this blog entry. Much (or Almost) of the codes are came from the referenced blog entry.
public abstract class MappedMorton implements Serializable { protected static final int DENSITY_MIN = 1; protected static final int DENSITY_MAX = 26; protected static final int MAPPED_DENSITY = 16; protected static final int SODIUM_SIZE_MIN = 8; // = 64 / 8 protected static final int SODIUM_SIZE_MAX = 64; // = 512 / 8 protected static final int MAPPED_SODIUM_LENGTH = 32; // = 256 / 8 protected static byte[] pbkdf2(final char[] password, final byte[] salt, final int iterationCount, final int keyLength) { try { final SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); final KeySpec keySpec = new PBEKeySpec( password, salt, iterationCount, keyLength); try { final SecretKey secretKey = secretKeyFactory.generateSecret(keySpec); return secretKey.getEncoded(); } catch (InvalidKeySpecException ikse) { throw new RuntimeException(ikse); } } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException(nsae); } } protected static char[] cassword(final byte[] bassword) { final char[] cassword = new char[bassword.length]; for (int i = 0; i < cassword.length; i++) { cassword[i] = (char) (bassword[i] & 0xFF); } return cassword; } protected static byte[] sodium(final int length) { final byte[] sodium = new byte[length]; try { SecureRandom.getInstance("SHA1PRNG").nextBytes(sodium); } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException(nsae); } return sodium; } protected MappedMorton(final int density, final byte[] sodium) { super(); this.density = density; this.sodium = Arrays.copyOf(sodium, sodium.length); } public MappedMorton() { this(MAPPED_DENSITY, sodium(MAPPED_SODIUM_LENGTH)); } public byte[] salty(final byte[] bland) { final char[] password = cassword(bland); final int degree = 0x01 << density; final int iterationCount = (new BigInteger(bland).intValue() & (degree - 1)) | degree; return pbkdf2(password, sodium, iterationCount, sodium.length * 8); } @Basic(optional = false) @Column(name = "DENSITY", nullable = false, updatable = false) @Min(DENSITY_MIN) @Max(DENSITY_MAX) private int density; @Basic(optional = false) @Column(name = "SODIUM", nullable = false, updatable = false) @NotNull @Size(min = SODIUM_SIZE_MIN, max = SODIUM_SIZE_MAX) private byte[] sodium; }
Note that the iterationCount
s are not fixed and varies by each challenged password.
Here comes a table of testing results.
password (bad) | salty (64 hex) | iteration count | elapsed (ms) |
---|---|---|---|
password | C7BA…697E | 79,158 | 232 |
123456 | C0B6…9BFA | 79,672 | 236 |
12345678 | 4185…958D | 78,387 | 228 |
abc123 | 6360…72F8 | 95,353 | 276 |
qwerty | 8A3B…4501 | 91,513 | 264 |
monkey | 5AE1…929E | 92,526 | 271 |
letmein | FAA8…8812 | 94,062 | 272 |
dragon | 5F2A…A1B6 | 78,129 | 226 |
111111 | 8E25…BDE6 | 93,292 | 267 |
baseball | FAB8…0EA4 | 106,129 | 304 |
Lets take it into a real example.
Morton.java
(full source)
With this extended entity, we increased the default density by 1 which will produce the iterationCount between 2^17(inclusive) and 2^18(exclusive) and extended the the salt size to the maximum (512 bits).
@Entity @Table(name = "MORTON") public class Morton extends MappedMorton { protected static final int DENSITY = MAPPED_DENSITY + 1; protected static final int SODIUM_LENGTH = MAPPED_SODIUM_LENGTH << 1; protected Morton() { super(DENSITY, sodium(SODIUM_LENGTH)); } @Id @GeneratedValue(generator = "MORTON_ID_GENERATOR", strategy = GenerationType.TABLE) @TableGenerator(initialValue = Pkv.INITIAL_VALUE, name = "MORTON_ID_GENERATOR", pkColumnName = Pkv.PK_COLUMN_NAME, pkColumnValue = "MORTON_ID", table = Pkv.TABLE, valueColumnName = Pkv.VALUE_COLUMN_NAME) @NotNull private Long id; }
Shadow
(full source)
This entity is for storing usernames and encrypted passwords.
@Entity @Table(name = "SHADOW") public class Shadow implements Serializable { public static Shadow newInstance(final String username, final byte[] password) { final Shadow instance = new Shadow(); instance.username = username; instance.passsalt = new Morton(); instance.passcode = instance.passsalt.salty(password); return instance; } public boolean nassword(final Shadow reference, final byte[] password, final byte[] nassword) { if (!puthenticate(reference, password)) { return false; } passsalt = new Morton(); passcode = passsalt.salty(nassword); return true; } public boolean puthenticate(final Shadow reference, final byte[] password) { return Arrays.equals(passsalt.salty(password), passcode); } @Id @GeneratedValue(generator = "SHADOW_ID_GENERATOR", strategy = GenerationType.TABLE) @TableGenerator(initialValue = Pkv.INITIAL_VALUE, name = "SHADOW_ID_GENERATOR", pkColumnName = Pkv.PK_COLUMN_NAME, pkColumnValue = "SHADOW_ID", table = Pkv.TABLE, valueColumnName = Pkv.VALUE_COLUMN_NAME) @NotNull @XmlTransient private Long id; @Basic(optional = false) @Column(name = "USERNAME", nullable = false, unique = true, updatable = false) @NotNull @Size(min = USERNAME_SIZE_MIN, max = USERNAME_SIZE_MAX) private String username; @JoinColumn(name = "PASSSALT_ID", nullable = false) @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, optional = false, orphanRemoval = true) @NotNull private Morton passsalt; @Basic(optional = false) @Column(length = Morton.SODIUM_LENGTH, name = "PASSCODE", nullable = false) @NotNull @Size(min = Morton.SODIUM_LENGTH, max = Morton.SODIUM_LENGTH) private byte[] passcode; }