Shiro learning notes (6) - Encryption
Shiro has a very powerful function, which is to encapsulate the encryption algorithm. In our previous example, when our password returns authentication information in Realm, it is written in clear text. This way is actually very unsafe.
Generally, for highly sensitive fields such as passwords, we should use an unbreakable algorithm to encrypt them and store them in our database. When the user logs in, on the premise that the user name entered by the user is correct (exists in the database), the string obtained by encrypting the password entered by the user with the same algorithm is matched with the ciphertext password in the database. If the matching is successful, the authentication is passed.
The advantage of this processing is that even developers or database administrators who can contact the production environment cannot know the password of the real user through the database information. Even if the password is leaked to the outside, others cannot know what the plaintext of the password is, which greatly protects the security of user information.
Shiro provides PasswordService and CredentialsMatcher to provide encryption password and verification password services.
Let's take a look at an example:
PasswordService service = new DefaultPasswordService(); String str1 = service.encryptPassword("123456"); String str2 = service.encryptPassword("123456"); System.out.println(str1); System.out.println(str2); // The salt value is stored in the encrypted password boolean boolean1 = service.passwordsMatch("123456",str1); System.out.println(boolean1); boolean boolean2 = service.passwordsMatch("123456",str2); System.out.println(boolean2)
We found that the ciphertext obtained by encrypting the same "123456" password with the encryptPassword() method of the DefaultPasswordService implementation class is different. However, we can use the passwordmatch() of the DefaultPasswordService implementation class to match the plaintext password with the ciphertext password. This method is successful and reliable, which we need to understand.
Then, how can we specify Shiro to use the defaultpasswordmatch () method of the implementation class DefaultPasswordService to match passwords in Realm?
We can use dependency injection. The code is as follows:
Customize the authentication implementation part of Realm:
public class MyPasswordRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String username = authenticationToken.getPrincipal().toString(); // String password = new String((char[]) authenticationToken.getCredentials()); // At this time, we should query the corresponding password from the database according to username // String passwordFromDB = "$shiro1$SHA-256$500000$tSqKNsDkQ4hLsGwJ9JnNUw==$7UxMi+gx5OjIdu8knQdrWlB0RDfWDEF/LwxjL0JdDRs="; // String passwordFromDB = "$shiro1$SHA-256$500000$79CtWg6E9u4Bxp0v7BNpxA==$eKAezOeAAQrS2cxTDJZJM8vwovgbCXrqRFSHvbgaMEY="; String passwordFromDB = "$shiro1$SHA-256$500000$b282jV6ARlycFe0B4OaUQA==$9r6F9OdmO/kvy0ig0FZbEg9etkJ7hd+10CTS1rRjUdo="; SimpleAuthenticationInfo info= new SimpleAuthenticationInfo(username,passwordFromDB,getName()); return info; } }
If we do not specify that Realm uses the implementation class of PasswordMatcher, the default matching method is to use the matching method of whether the strings are the same, which obviously cannot meet our requirements. So we need to specify the PasswordMatcher attribute in Realm:
# Declare a password matching class that Shiro already has passwordMatcher=org.apache.shiro.authc.credential.PasswordMatcher # Declare a custom Realm class myPasswordRealm=com.liwei.realm.MyPasswordRealm # Inject passwordMatcher into the custom Realm class myPasswordRealm.credentialsMatcher=$passwordMatcher # Inject custom Realm into securityManager securityManager.realms=$myPasswordRealm
Thus, the ciphertext password matching function is realized.
In practical applications, we like to define our own encryption algorithms, such as the MD5 algorithm. We can also specify a salt value for the MD5 algorithm to make the ciphertext obtained by MD5 encryption more secure.
We first introduce the use of MD5 algorithm to obtain the ciphertext of "123456" by adding salt.
String originPassword = "123456"; String salt = "hello"; String md5 = new Md5Hash(originPassword,salt).toString(); System.out.println(md5);
Next, write the authentication implementation part in custom Realm.
public class MyPasswordRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String username = authenticationToken.getPrincipal().toString(); // String password = new String((char[]) authenticationToken.getCredentials()); // At this time, we should query the corresponding password from the database according to username String passwordFromDB = "eeb9bad681184779aa6570e402d6ef6c"; String salt = "hello"; SimpleAuthenticationInfo info= new SimpleAuthenticationInfo(username,passwordFromDB,getName()); // Set the salt value of the encryption algorithm, and use the ByteSource tool class info.setCredentialsSalt(ByteSource.Util.bytes(salt.getBytes())); return info; } }
Finally, we'll go to Shiro Ini configuration file (configure encryption algorithm and salt value):
# Declare a password matching class that Shiro already has hashMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher hashMatcher.hashAlgorithmName=md5 hashMatcher.hashSalted=hello # Declare a custom Realm class myPasswordRealm=com.liwei.realm.MyPasswordRealm # Inject passwordMatcher into the custom Realm class myPasswordRealm.credentialsMatcher=$hashMatcher # Inject custom Realm into securityManager securityManager.realms=$myPasswordRealm
Here, the encryption part has been briefly introduced. We can see that our authentication is more secure and standardized mainly through configuration.