2013-06-18 47 views
4

在這個小教程中,我向您展示瞭如何構建一個負責註冊和登錄的GWT模塊。如何使用GWT與Apache Shiro散列和鹽漬

密碼與Sha256哈希並被鹽漬。

+0

既然是什麼地方提交教程? – koma

+1

我做了很多研究,我花了很長時間才弄清楚shiro如何與gwt搭配使用。鹹味密碼也是一樣。我沒有在互聯網上找到類似我的教程。所以我想我想分享我的知識。這就是stackoverflow的全部內容。不是嗎? –

+2

最好把你的公告改成一個問題(然後你自己回答)。正如已經提到的那樣,SO不是轉儲關於您的開發旅程的教程或博客的地方。 – Veger

回答

6

下載並安裝

下載的Apache四郎:http://shiro.apache.org/download.html; 我用Shrio - 所有(1.2.2二進制分發)http://tweedo.com/mirror/apache/shiro/1.2.2/shiro-root-1.2.2-source-release.zip

下載後在您的lib文件夾四郎,全1.2.2.jar。 enter image description here

我們還可以包含其他需要的.jar文件。

  1. MySQL驅動:http://www.java2s.com/Code/Jar/c/Downloadcommysqljdbc515jar.htm(com.mysql.jdbc_5.1.5.jar)
  2. SLF4J日誌記錄:http://www.slf4j.org/download.html(SLF4J-API-1.7.5.jar,SLF4J-簡單1.7.5.jar)
  3. 阿帕奇百科全書的BeanUtils:http://repo2.maven.org/maven2/commons-beanutils/commons-beanutils/1.7.0/(公地的BeanUtils-1.7.0.jar)

不要忘了你的罐子添加到您的構建路徑。

的web.xml

添加到您的web.xml

<!-- Apache Shero --> 
<listener> 
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> 
</listener> 
<filter> 
    <filter-name>ShiroFilter</filter-name> 
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> 
</filter> 
<!-- Make sure any request you want accessible to Shiro is filtered. /* catches all --> 
<!-- requests. Usually this filter mapping is defined first (before all others) to --> 
<!-- ensure that Shiro works in subsequent filters in the filter chain: --> 
<filter-mapping> 
    <filter-name>ShiroFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
    <dispatcher>REQUEST</dispatcher> 
    <dispatcher>FORWARD</dispatcher> 
    <dispatcher>INCLUDE</dispatcher> 
    <dispatcher>ERROR</dispatcher> 
</filter-mapping> 

shiro.ini

把你shiro.ini到WEB-INF:

[main] 
authc.loginUrl = /Login.html?gwt.codesvr=127.0.0.1:9997 
authc.successUrl = /Leitfaden.html 
logout.redirectUrl = /login.html 

# ------------------------ 
# Database 

# Own Realm 
jdbcRealm = leitfaden.login.server.MyRealm 

# Sha256 
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher 
# base64 encoding, not hex in this example: 
sha256Matcher.storedCredentialsHexEncoded = false 
sha256Matcher.hashIterations = 1024 

jdbcRealm.credentialsMatcher = $sha256Matcher 

# User Query 
# default is "select password from users where username = ?" 
jdbcRealm.authenticationQuery = SELECT password, salt FROM USER WHERE email = ? 

# Connection 
ds = com.mysql.jdbc.jdbc2.optional.MysqlDataSource 
ds.serverName = localhost 
ds.user = root 
ds.password = root 
ds.databaseName = leitfaden 
jdbcRealm.dataSource=$ds 

authc.usernameParam = email 
authc.passwordParam = password 
authc.failureKeyAttribute = shiroLoginFailure 

# Use Built-in Chache Manager 
builtInCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager 
securityManager.cacheManager = $builtInCacheManager 

# ----------------------------------------------------------------------------- 
[urls] 
/yourMainUrl.html = authc 

GWT模塊

創建一個登錄模塊。模塊名「登錄」和包名「leitfaden.login」:

添加到您的web.xml

<servlet> 
    <servlet-name>LoginService</servlet-name> 
    <servlet-class>leitfaden.login.server.LoginServiceImpl</servlet-class> 
</servlet> 
<servlet-mapping> 
    <servlet-name>LoginService</servlet-name> 
    <url-pattern>/leitfaden.login.Login/LoginService</url-pattern> 
</servlet-mapping> 

LoginService.java

@RemoteServiceRelativePath("LoginService") 
public interface LoginService extends RemoteService { 
    public Boolean isLoggedIn(); 
    public Boolean tryLogin(String email, String password, Boolean rememberMe); 
    public void logout(); 
    public void registrate(String email, String password); 
} 

LoginServiceAsync.java

public interface LoginServiceAsync { 
    public void isLoggedIn(AsyncCallback<Boolean> callback); 
    public void tryLogin(String email, String password, Boolean rememberMe, AsyncCallback<Boolean> callback); 
    public void logout(AsyncCallback<Void> callback); 
    public void registrate(String email, String password, AsyncCallback<Void> callback); 
} 

LoginServiceImpl

public class LoginServiceImpl extends RemoteServiceServlet implements LoginService { 

    private static final long serialVersionUID = -4051026136441981243L; 
    private static final transient Logger log = LoggerFactory 
      .getLogger(LoginServiceImpl.class); 

    private org.apache.shiro.subject.Subject currentUser; 

    public LoginServiceImpl() { 
     Factory<SecurityManager> factory = new IniSecurityManagerFactory(); 
     SecurityManager securityManager = factory.getInstance(); 
     SecurityUtils.setSecurityManager(securityManager); 
    } 

    @Override 
    public Boolean isLoggedIn() { 
     currentUser = SecurityUtils.getSubject(); 

     if (currentUser.isAuthenticated()) { 
      return true; 
     } else { 
      return false; 
     } 
    } 

    @Override 
    public Boolean tryLogin(String username, String password, Boolean rememberMe) { 
     // get the currently executing user: 
     currentUser = SecurityUtils.getSubject(); 

     // let's login the current user so we can check against roles and 
     // permissions: 
     if (!currentUser.isAuthenticated()) { 
      //collect user principals and credentials in a gui specific manner 
      //such as username/password html form, X509 certificate, OpenID, etc. 
      //We'll use the username/password example here since it is the most common. 
      UsernamePasswordToken token = new UsernamePasswordToken(username,password); 
      //this is all you have to do to support 'remember me' (no config - built in!): 
      token.setRememberMe(rememberMe); 

      try { 
       currentUser.login(token); 
       log.info("User [" + currentUser.getPrincipal().toString() + "] logged in successfully."); 
       return true; 
      } catch (UnknownAccountException uae) { 
       log.info("There is no user with username of " 
         + token.getPrincipal()); 
      } catch (IncorrectCredentialsException ice) { 
       log.info("Password for account " + token.getPrincipal() 
         + " was incorrect!"); 
      } catch (LockedAccountException lae) { 
       log.info("The account for username " + token.getPrincipal() 
         + " is locked. " 
         + "Please contact your administrator to unlock it."); 
      } catch (AuthenticationException ae) { 
       log.error(ae.getLocalizedMessage()); 
      } 
     } 

     return false; 
    } 

    @Override 
    public void logout() { 
     currentUser = SecurityUtils.getSubject(); 
     currentUser.logout(); 
    } 

    @Override 
    public void registrate(String email, String plainTextPassword) { 
     RandomNumberGenerator rng = new SecureRandomNumberGenerator(); 
     Object salt = rng.nextBytes(); 

     // Now hash the plain-text password with the random salt and multiple 
     // iterations and then Base64-encode the value (requires less space than Hex): 
     String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt,1024).toBase64(); 

     User user = new User(email, hashedPasswordBase64, salt.toString(), 0); 
     this.createUser(user); 
    } 

    private void createUser(User user) { 
     UserDAL.connect(); 

     UserDAL.beginTransaction(); 
     new UserDAL().createUser(user); 
     log.info("User with email:" + user.getEmail() + " hashedPassword:"+ user.getPassword() + " salt:" + user.getSalt()); 
     UserDAL.commitTransaction(); 

     UserDAL.disconnect(); 
    } 

} 

MyRealm.java

,用戶可以通過這個應用程序註冊。但是Shiro不知道如何比較醃製密碼和給定的用戶輸入。爲此,我們需要實現我們自己的領域。一個領域本質上是一個安全特定的DAO

MyRealm。java通過給定的電子郵件獲取用戶並返回一個SaltedAuthenticationInfo。通過SaltedAuthenticationInfo Shiro知道如何將用戶輸入與數據庫中的用戶進行比較。

public class MyRealm extends JdbcRealm { 
    private static final Logger log = LoggerFactory.getLogger(MyRealm.class); 

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

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

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

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

     // return salted credentials 
     SaltedAuthenticationInfo info = new MySaltedAuthentificationInfo(username, passwdSalt.password, passwdSalt.salt); 

     return info; 
    } 

    private PasswdSalt getPasswordForUser(String username) { 
     User user = getUserByEmail(username); 
     if (user == null) { 
      return null; 
     } 
     return new PasswdSalt(user.getPassword(), user.getSalt()); 
    } 

    private User getUserByEmail(String email) { 
     UserDAL.connect(); 
     User user = new UserDAL().getUserByEmail(email); 
     UserDAL.disconnect(); 
     return user; 
    } 

    class PasswdSalt { 
     public String password; 
     public String salt; 

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

} 

MySaltedAuthentificationInfo 重要的是你在getCredentialsSalt()正確解碼的鹽。我已經使用Base64編碼。

public class MySaltedAuthentificationInfo implements SaltedAuthenticationInfo { 

    private static final long serialVersionUID = -2342452442602696063L; 

    private String username; 
    private String password; 
    private String salt; 

    public MySaltedAuthentificationInfo(String username, String password, String salt) { 
     this.username = username; 
     this.password = password; 
     this.salt = salt; 
    } 

    @Override 
    public PrincipalCollection getPrincipals() { 
     PrincipalCollection coll = new SimplePrincipalCollection(username, username); 
     return coll; 
    } 

    @Override 
    public Object getCredentials() { 
     return password; 
    } 

    @Override 
    public ByteSource getCredentialsSalt() { 
     return new SimpleByteSource(Base64.decode(salt)); 
    } 

} 

用戶現在可以註冊並登錄。您只需在您的登錄模塊中調用LoginService的代碼視圖。

+0

它是否考慮到salt是一個二進制字符串並且可以包含'\ 0'字符?特別是在數據庫中存儲可能是一個問題,但我不能判斷這一點。 – martinstoeckli

+0

我還沒有想過這件事。但我在數據庫中存儲沒有問題。在MySQL中它工作正常。 –