Java for Web學習筆記(一零七):Spring架構中使用JPA(7)密碼和BCrypt__Java

來源:互聯網
上載者:User
密碼安全存放

密碼安全有兩個方面: 使用者密碼傳遞的安全性,可以使用https來保護,也有將密碼進行雜湊(例如MD5)後進行傳遞的(複雜一點的,密碼是參與雜湊,還包含一些動態參數,例如時間戳記等進行salt),或者兩者結合起來 資料庫密碼儲存的安全性,一旦被拖庫,或者別的被黑,仍能安全保護好使用者的密碼。

目前,一般可採用BCrypt加密方式,我們絕不能將密碼的明文,或者經過弱雜湊(如MD5和SHA)就存放在資料庫中。BCrypt作為工業級產品,為每個密碼產生不同的salt,使得字典生產困難得多,而MD5和SHA的破譯則簡單得多。

資料庫表格 BCrypt加密後需要60位元組存放,下面是一個存放表格的例子:

CREATE TABLE UserPrincipal (  UserId BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,  Username VARCHAR(30) NOT NULL,  HashedPassword BINARY(60) NOT NULL,  UNIQUE KEY UserPrincipal_Username (Username)) ENGINE = InnoDB;

bcrypt加密後的字串形如:$2a$10$vacuqbDw9I7rr6RRH8sByuktOzqTheQMfnK3XCT2WlaL7vt/3AMby,其中:$是分割符,無意義;2a是bcrypt加密版本號碼;10是cost的值;而後的前22位是salt值;再然後的字串就是密碼的密文了;

我們將使用UserPrincipal作為Entity,代碼如下:

/** 考慮到在session中存放Principal,我們將實現Principal介面,並提供Colonable介面 * 對於principal,我們如何判斷其相同(本例採用使用者名稱),將重寫對象的hashcode()、equals()和toString()。*/@Entity@Table(uniqueConstraints={ @UniqueConstraint(name = "UserPrincipal_Username",columnNames="Username")})public class UserPrincipal implements Principal,Cloneable,Serializable{private static final long serialVersionUID = 1L;private static final String SESSION_ATTRIBUTE_KEY = "cn.wei.flowingflying.customer_support.user.principal";private long id;private String username;private byte[] password;@Id@Column(name="UserId")@GeneratedValue(strategy=GenerationType.IDENTITY)public long getId() {return id;}public void setId(long id) {this.id = id;}@Basicpublic String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@Basic@Column(name = "HashedPassword")public byte[] getPassword() {return password;}public void setPassword(byte[] password) {this.password = password;}@Override@Transientpublic String getName() {return this.username;}/* 對於Principal而言,重要的是兩者之間的比較,因此重寫object的下面幾個方法 */@Override@Transientpublic int hashCode() {return this.username.hashCode();}@Override@Transientpublic boolean equals(Object obj) {return obj instanceof UserPrincipal && ((UserPrincipal)obj).username.equals(this.username);}@Override@Transientprotected UserPrincipal clone() {try {return (UserPrincipal) super.clone();} catch (CloneNotSupportedException e) {throw new RuntimeException(e); //轉成RuntimeException,使得transaction可以復原}}@Override@Transientpublic String toString() {return this.username;}/* 對於提供靜態方法供調用,以便在session中存放和擷取principal */public static Principal getPrincipal(HttpSession session){return session == null ? null : (Principal)session.getAttribute(SESSION_ATTRIBUTE_KEY);}public static void setPrincipal(HttpSession session, Principal principal){session.setAttribute(SESSION_ATTRIBUTE_KEY, principal);}}
密碼校正 引入jbcrypt

jbcrypt:OpenBSD-style Blowfish password hashing for Java industry-standard jBCrypt Java implementation of the BCrypt hash algorithm.

在pom.xml中

<dependency>    <groupId>org.mindrot</groupId>    <artifactId>jbcrypt</artifactId>    <version>0.4</version></dependency>
repository介面的實現 之前已經介紹了通用的repository介面的實現,我們需要加上通過UNIQUE KEY(username)的擷取,具體如下:

public interface UserRepository extends GenericRepository<Long, UserPrincipal>{    UserPrincipal getByUsername(String username);}
@Repositorypublic class DefaultUserRepository extends GenericJpaRepository<Long, UserPrincipal> implements UserRepository{    @Override    public UserPrincipal getByUsername(String username) {//      【方式1】通過Java Persistence query,即JPQL//        return this.entityManager.createQuery(//                "SELECT u FROM UserPrincipal u WHERE u.username = :username",//                UserPrincipal.class//        ).setParameter("username", username).getSingleResult();//      【方式2】通過JPA的標準介面         CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();         CriteriaQuery<UserPrincipal> query = builder.createQuery(this.entityClass);         Root<UserPrincipal> root = query.from(this.entityClass);         return this.entityManager.createQuery(                            query.select(root).where(builder.equal(root.get("username"), username))                            ).getSingleResult();}}
密碼校正相關代碼
@Validatedpublic interface AuthenticationService {Principal authenticate(@NotBlank(message = "{validate.authenticate.username}") String username, @NotBlank(message = "{validate.authenticate.password}") String password);void saveUser(@NotNull(message = "{validate.authenticate.saveUser}") @Valid UserPrincipal principal,            String newPassword    );}
@Servicepublic class DefaultAuthenticationService implements AuthenticationService{    private static final Logger log = LogManager.getLogger();    private static final SecureRandom RANDOM;    private static final int HASHING_ROUNDS = 10;    static {        try {            RANDOM = SecureRandom.getInstanceStrong();        } catch (NoSuchAlgorithmException e) {            throw new IllegalStateException(e);        }    }    @Inject UserRepository userRepository;    @Override    @Transactional    public Principal authenticate(@NotBlank(message = "{validate.authenticate.username}") String username,                                  @NotBlank(message = "{validate.authenticate.password}") String password) {        UserPrincipal principal = this.userRepository.getByUsername(username);        if(principal == null){            log.warn("Authentication failed for non-existent user {}.", username);            return null;        }        if(!BCrypt.checkpw(password, new String(principal.getPassword(),StandardCharsets.UTF_8))){            log.warn("Authentication failed for user {}.", username);            return null;        }        log.debug("User {} successfully authenticated.", username);        return principal;    }    @Override    @Transactional    public void saveUser(@NotNull(message = "{validate.authenticate.saveUser}") @Valid UserPrincipal principal,                         String newPassword) {        if(newPassword != null && newPassword.length() > 0){            String salt = BCrypt.gensalt(HASHING_ROUNDS, RANDOM);            principal.setPassword(BCrypt.hashpw(newPassword, salt).getBytes());        }        if(principal.getId() < 1)            this.userRepository.add(principal);        else            this.userRepository.update(principal);    }}
相關連結: 我的Professional Java for Web Applications相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.