Shiro learning (5) - encryption and decryption of passwords

foreword

The passwords for the previous articles are all stored in plain text. Of course, it is impossible to store passwords in plain text in real projects, and passwords must be stored in encrypted form. We can see earlier that when we give the account number and password, shiro will search the ini configuration file or the corresponding field of the database to match the account password. Then if we encrypt the stored password, how can Shiro match the stored encrypted password according to the plaintext password we submitted?

The principle of Shiro encryption and matching

I am not going to show the source code here. I believe that many readers are at a loss when faced with a large amount of source code when reading the article, and can't sum up anything, so here is a general idea, and interested readers can track the source code by themselves. Here is an example of reading the ini file:

1. When we use the command subject.login(token), we will call doGetAuthenticationInfo of SimpleAccountRealm, the parent class of IniRealm, to obtain the stored authentication information. PS: If we customize the realm, how to obtain authentication is usually rewriting this method.

2. Use the assertCredentialsMatch method of AuthenticatingRealm to compare whether the account password token we submitted matches the authentication information obtained in the previous step. If there is a match, the authentication information is returned, otherwise an exception is thrown.

3. The matching method in step 2 calls the doCredentialsMatch method of the CredentialsMatcher interface. For example, the default matching class is SimpleCredentialsMatcher, and its doCredentialsMatch is very simple, which is to compare whether the submitted password is consistent with the authentication password obtained in step 1. This process does not do any encryption and decryption processing. Therefore, for encryption or decryption operations, the main implementation is performed on the doCredentialsMatch method.

Shiro itself provides some encryption and decryption classes and methods, such as the PasswordMatcher class. Of course, it can also be customized, just configure it in the securityManager.

Configure encryption and decryption classes

matching class

Shiro's default matching class is SimpleCredentialsMatcher. Readers can refer to its source code, the implementation is very simple. The matching interface provided by shiro is CredentialsMatcher, as long as the doCredentialsMatch method is implemented:

public interface CredentialsMatcher {
    boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}

Encryption and Authentication Services

Shiro provides the interface PasswordService interface for encryption and authentication services:

public interface PasswordService {
    String encryptPassword(Object plaintextPassword) throws IllegalArgumentException;
    boolean passwordsMatch(Object submittedPlaintext, String encrypted);
}

The first method is used for encryption, and the second method is used for matching passwords. The encryption and decryption classes provided by Shiro itself all implement this interface. Usually this interface is used with CredentialsMatcher. Generally, the doCredentialsMatch method of CredentialsMatcher performs some processing on the submitted account password and stored account password information, and then calls the passwordsMatch method of PasswordService for matching.

Why do you need to design PasswordService with CredentialsMatcher?

From my understanding, only CredentialsMatcher can be used. The default matching class SimpleCredentialsMatcher does not use PasswordService. The parameters passed in by the CredentialsMatcher interface doCredentialsMatch are AuthenticationToken (account password information submitted by the user) and AuthenticationInfo (account password information stored in the system). We can extract and combine them in doCredentialsMatch and put them into passwordsMatch of PasswordService for processing. Proposing PasswordService can be more convenient to replace different encryption algorithms.

code example

Now make a simple example to show how to use PasswordService and CredentialsMatcher. Shiro's default CredentialsMatcher is PasswordMatcher, and the default PasswordService is DefaultPasswordService. Even if we don't set it, we still want to set it manually in the example, so that readers can understand it better.

First we get an encrypted password:

public class PassWordCrypto {
	
	public static String encriptPassword(String password) {
		PasswordService service = new DefaultPasswordService();
		return service.encryptPassword(password);
	}
	
	public static void main(String[] args) {	
		System.out.println(encriptPassword("123"));
	}
}

Execute the above code, and the encrypted result of password "123" is $shiro1$SHA-256$500000$3mRjauseIWB330SU++9msA==$eHTXKKktQduiDi6Kut8HzwBJHeiwx7pXDemieEhgDkE=

 

This time we take the ini location file as an example, create password.ini, and fill in the encrypted password we just got:

[main]
passwordMatcher=org.apache.shiro.authc.credential.PasswordMatcher
passwordService=org.apache.shiro.authc.credential.DefaultPasswordService
passwordMatcher.passwordService=$passwordService
iniRealm.credentialsMatcher=$passwordMatcher
securityManager.realms=$iniRealm
[users]
zhang=$shiro1$SHA-256$500000$3mRjauseIWB330SU++9msA==$eHTXKKktQduiDi6Kut8HzwBJHeiwx7pXDemieEhgDkE=,role1
li=123
[roles]
role1=user:create,update
role2=user:create,delete

In [main] above, set DefaultPasswordService to PasswordMatcher, and then set PasswordMatcher to iniRealm. The iniRealm here is created by default. If we create a new IniRealm and set it in, it will report an error instead.

Then write the test code:

package com.sadoshi.shiro.crypto;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.env.BasicIniEnvironment;
import org.apache.shiro.env.Environment;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

public class PasswordServiceTest {
	
	private Subject subject;
	
	@Before
	public void init() {
		Environment env = new BasicIniEnvironment("classpath:password.ini");
		org.apache.shiro.mgt.SecurityManager securityManager = env.getSecurityManager();
		SecurityUtils.setSecurityManager(securityManager);
		subject = SecurityUtils.getSubject();
	}

	@Test
	public void hashCrypto() {
		UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
		try {
			subject.login(token);
			System.out.println("ok");
		} catch (AuthenticationException e) {
			e.printStackTrace();
		}
		subject.logout();
	}
}

If the password does not match, an exception will be thrown. Our execution here is correct.

summary

This article is just a preliminary demonstration of how CredentialsMatcher and PasswordService are used. Later articles will also show other encryption methods.

Guess you like

Origin blog.csdn.net/sadoshi/article/details/120174919