How to use GWT with Apache Shiro hashed and salted

In this short tutorial, I will show you how to create a GWT module that is responsible for registration and login.

Password gets hashed using Sha256 and salted.

+4
source share
1 answer

Download and Install

Download for Apache Shiro: http://shiro.apache.org/download.html ; I used Shrio-All (1.2.2 Binary Distribution) http://tweedo.com/mirror/apache/shiro/1.2.2/shiro-root-1.2.2-source-release.zip

After downloading, include shiro-all-1.2.2.jar in the lib folder. enter image description here

We can also include other .jar files that we will need later.

Remember to add your jars to the build path.

web.xml

Add this to your 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

Put your shiro.ini in 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 module

Create a module for logging in. The name of the Login module and the name of the package leitfaden.login:

Add this to your 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 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

Now users can register in this application. But Ciro does not know how to compare salted passwords with a given user. To do this, we need to realize our own kingdom. Realm is essentially a DAO .

MyRealm.java receives the user with the given email address and returns SaltedAuthenticationInfo. With this, SaltedAuthenticationInfo Shiro knows how to compare user input with user from the database.

 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 The important thing is that you correctly decode the salt in getCredentialsSalt (). I used 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)); } } 

Now users can register and log in. You will only need a code representation in the login module that calls LoginService.

+6
source

Source: https://habr.com/ru/post/1486913/


All Articles