Talking about shiro's login verification

Let’s talk about shiro’s login verification. The function of shiro is to verify the identity of the user and which functions the authorized user can access. If we don't use shiro, how do we implement this function? Generally, we let the user enter the user password on the page, the background control layer receives the user password from the front desk, and then goes to the library to check the corresponding password according to the user name. The entered passwords are compared, and if they are the same, the verification is considered to have passed.
  As long as we know the above principle, shiro is the same, we enter the user password on the jsp page and submit it to the background control layer, look at the following code
@RequestMapping(value = "login", method = RequestMethod.POST, produces = "text/html; charset=utf-8")
	public String login(String username, String password, HttpServletRequest request) {
		try {
			Subject user = SecurityUtils.getSubject();
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			try {
				user.login(token);
			} catch (LockedAccountException lae) {
				token.clear();
				request.setAttribute("error", "The user has been locked and cannot log in, please contact the administrator!");
				return "/login";
			} catch (ExcessiveAttemptsException e) {
				token.clear();
				request.setAttribute("error", "Account: " + username + " Too many login failures, locked for 10 minutes!");
				return "/login";
			} catch (AuthenticationException e) {
				token.clear();
				request.setAttribute("error", "Incorrect user or password!");
				return "/login";
			}
			request.removeAttribute("error");
		} catch (Exception e) {
			e.printStackTrace ();
			request.setAttribute("error", "Login exception, please contact the administrator!");
			return "/login";
		}
		return "redirect:index.shtml";
	}

The most important thing here is user.login(token); there is a parameter token, which is the user password entered by the user. We may use an object user to encapsulate the user name and password. Shiro uses token, which is the control The code of the layer has not yet reached shiro. After calling user.login(token), it will be handed over to shiro for processing. Next, shiro should go to the token to get the username, and then check the database according to the user, and put the data in the database. Password checked out. This part of the code generally requires us to customize the implementation, customize a realm, and override the doGetAuthenticationInfo method
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String username = (String) token.getPrincipal();
		// query database
		UserFormMap userFormMap = new UserFormMap();
		userFormMap.put("accountName", "" + username + "");
		List<UserFormMap> userFormMaps = userMapper.findByNames(userFormMap);
		if (userFormMaps.size() != 0) {
			
			SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,userFormMaps.get(0).get("password"),ByteSource.Util.bytes(username + "" + userFormMaps.get(0).get("credentialsSalt")),getName());
			return authenticationInfo;
		} else {
			throw new UnknownAccountException();// Account not found
		}

	}

This custom realm is actually quite simple. It is to check the database, and then construct an Authentication. What is this Authentication? It can be understood in this way. The previous token is the user password entered by the user, and this Authentication is checked from the library. User password, then the two can be compared to verify that the login is unsuccessful. The main code is SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,userFormMaps.get(0).get("password"),ByteSource.Util.bytes(username + "" + userFormMaps.get(0).get("credentialsSalt")) ,getName()); This constructs an Authentication with a lot of parameters. Explain that the first is the username, the second is the password, the third is the salt, and the fourth is the realm. Needless to say the first and second parameters, the third salt is used for encryption. It is impossible to store the user's password in plaintext. Generally, it is encrypted. If md5 is used, the financial industry such as banks will use encryption machines to encrypt. This salt is a parameter of md5. If you don't use this parameter, you think, if your password is 123456, and others are 123456, after md5 encryption, they are all the same ciphertext. It is easy for others to guess your honeycode. After adding this , the same is the plaintext of 123456, the encrypted ciphertext is different, the encryption machine also has this thing, called encryption factor. The fourth parameter is your custom realm.
   Up to now, user authentication has not been performed, but the data has been prepared, and the next step is to verify the two identities.
Here we define a RetryLimitHashedCredentialsMatcher that inherits HashedCredentialsMatcher,
@Override
    public boolean doCredentialsMatch(AuthenticationToken authcToken,
        AuthenticationInfo info) {
    	 UsernamePasswordToken token = (UsernamePasswordToken) authcToken;   
        String username = (String) token.getPrincipal();
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            // clear retry count
            passwordRetryCache.remove(username);
        }

        return matches;
    }

The key code here: boolean matches = super.doCredentialsMatch(token, info); is to compare the token with the Authentication. In fact, we know that the token is the username and password entered by the user. The password has not undergone any processing and is still in plain text, while authentication is in the database and is cipher text, so it needs to be processed to verify it. This processing is handled internally by shiro. Yes, the source code inside is actually very simple. He just determines whether the authentication is SaltedAuthenticationInfo. If so, he takes the salt out of SaltedAuthenticationInfo and encrypts the password in the token. Let's see now this is a subclass diagram of the authentication interface

protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
        Object salt = null;
        if (info instanceof SaltedAuthenticationInfo) {
            salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
        } else {
            //retain 1.0 backwards compatibility:
            if (isHashSalted()) {
                salt = getSalt(token);
            }
        }
        return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
    }

The above paragraph is the original code of shiro, he is to judge if it is SaltedAuthenticationInfo. In fact, we don't necessarily use shiro's encryption method for a lot of time. If we don't use his encryption method, we don't need to adjust shiro's authentication method. In RetryLimitHashedCredentialsMatcher, we have already obtained the token and authentication, and we can take out the password and compare it by ourselves. Now, the plaintext of the token calls custom encryption.
String tokenpwd=String.valueOf(token.getPassword());
        String databasepwd= String.valueOf(info.getCredentials());
        boolean matches =tokenpwd.equals(databasepwd);
        if (matches) {
            // clear retry count
            passwordRetryCache.remove(username);
        }
        return matches;

There is no encryption here. If there is encryption, just encrypt the tokenpwd and compare it with the databasepwd. This completes the entire user identity verification.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326223504&siteId=291194637