shiro verifies shiro's authentication process through the configuration file realm.ini

Keywords: Java html5 html css

shiro's core architecture diagram  

  I didn't understand this diagram when learning shiro before. Later, after learning it systematically, I can have a global control over shiro from this diagram.

The top part is the subject body

Subject stay shiro Is an interface through which external programs subject Conduct certification

There will be such a method later

    subject.login(token);//User login

In the middle is security management, which is the main part of shiro: Security Manager

adopt SecurityManager Can complete subject Authentication, authorization, etc., in essence SecurityManager Yes Authenticator Certified, passed Authorizer Authorized by SessionManager Session management, etc
SecurityManager Is an interface that inherits Authenticator, Authorizer, SessionManager These three interfaces.


This code will follow:
        //Create securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));
        //The default security manager will be set in the installation tool class
        SecurityUtils.setSecurityManager(defaultSecurityManager);

At the bottom is the Realm related to the data (which can be the data of the configuration file realm.ini or the data from the database)

This code will follow:
        //Create securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

       //Add authentication and authorization data to the security manager
       defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));

I've seen some problems in this area before

Question 1: why don't I just create a realm.ini file, user's role and permission information from the database?

In fact, it is equivalent to a method provided by shiro on how to implement shiro authentication and authorization without a database (or without a database). This method is generally a user demo. In actual work, the user's role and permission information are obtained from the database.

Question 2: I get the user's role and permission information from the database. Do I need to create a realm.ini file?

  No, realm.ini will not be created in actual work, but will be read from the database. In some places, creating knowledge demo tests in this way is equivalent to creating some test data in realm.ini to demonstrate that shiro can authenticate and authorize

Question 3: what is shiro's certification process

 

The user takes the user name and password and generates a token through shiro. Then shiro logs in through this token and judges whether the authentication is passed

For example, if I don't connect to the database, how can I test shiro to realize user authentication?

Using the configuration file to test shiro can realize the user's ID card

[users]
xiaochen=123
zhangsan=456

Test shiro's authentication using the default SimpleAccountRealm

public class TestAuthenticator {
    public static void main(String[] args) {
        //Create securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));
        //The default security manager will be set in the installation tool class
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //Get principal object
        Subject subject = SecurityUtils.getSubject();
        //Create token token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen1", "123");
        try {
            subject.login(token);//User login
            System.out.println("Login succeeded~~");
        } catch (UnknownAccountException e) {
             throw new RuntimeException("Authentication failed: user name does not exist~");
        }catch (IncorrectCredentialsException e){
             throw new RuntimeException("Authentication failed: password error~");
       
        }
    }
}

Explanation: the securityManager security manager is described above  , This is an interface

In addition to DefaultSecurityManager, it is the only implementation class

   //Create securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

Others

SessionsSecurityManager,, AuthenticatingSecurityManager, RealmSecurityManager and cacheingsecuritymanager are abstract implementation classes

Set the information saved by the user in the Security Manager (read the information of the user in the configuration file through new IniRealm("classpath:shiro.ini")

  defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));

The SecurityUtils tool class is used to obtain the Subject object. The parameter is the security manager interface SecurityManage

    Subject subject = SecurityUtils.getSubject();

The shiro process can know that shiro first gets the user name and password and generates a token

  UsernamePasswordToken token = new UsernamePasswordToken("xiaochen","123");

Then log in and authenticate with the token through subject.login

   subject.login(token);//User authentication

 

From the source code, you can see that SimpleAccountRealm

Verify whether the account is correct, and finally implement the authentication implemented by the doGetAuthenticationInfo method in the SimpleAccountRealm class


 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }



Here, first judge whether the account exists or not. If it does not exist, throw an exception directly, otherwise return it directly account,Then judge whether the password is correct

Verifying that the password is correct is actually implemented using equals in the SimpleCredentialsMatcher (password matcher)

 public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = getCredentials(token);
        Object accountCredentials = getCredentials(info);
        return equals(tokenCredentials, accountCredentials);
    }

It should be noted that the user's password verification is automatically completed by shiro (therefore, it is suggested that we do not need to consider the password comparison operation when making a self-determination)

The AuthenticationInfo obtained in the AuthenticatingRealm abstract class only has account information, and the stored password is not obtained

    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
            //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }

In other words, shiro implements user authentication and authorization through the SimpleAccountRealm class by default

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }

Question 3: if we customize an authentication authorization Realm, how to implement it?

Of course, refer to its default writing method,   By default, it is implemented by inheriting an authoringrealm. We also need to inherit an authoringrealm and override it

doGetAuthenticationInfo and doGetAuthorizationInfo are OK
class SimpleAccountRealm extends AuthorizingRealm
/**
 * Custom realm
 */
public class CustomerRealm extends AuthorizingRealm {

    //Authorization method
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

       //Authentication method
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
       //In practice, jdbc mybatis is used to query relevant data according to identity information
       if("xiaochen".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}

Use custom   CustomerRealm tests shiro's certification

public class TestCustomerRealmAuthenticator {
    public static void main(String[] args) {

        //Create securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //Set custom realm
        defaultSecurityManager.setRealm(new CustomerRealm());
        //Set security tool class to security tool class
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //Obtain the subject through the security tool class
        Subject subject = SecurityUtils.getSubject();
        //Create token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);
            System.out.println(subject.isAuthenticated());
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("User name error");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("Password error");
        }
        //Authenticate users for authorization
        if(subject.isAuthenticated()){
            //1. Role based permission control
            System.out.println(subject.hasRole("admin"));
        }
    }
}

Posted by jrodd32 on Mon, 04 Oct 2021 16:53:34 -0700