shiro - Getting Started with Realm

Why use Realm

In the last article, we wrote the identity information (username/password/role/permission) in the configuration file, but in actual development, these identity information should be stored in the data, so we need to customize Realm to get it from the data identity information for verification.

Custom Realm

 

  • Define a MyRealm, inheritAuthorizingRealm

 

package com.shiro.realm;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyRealm1 extends AuthorizingRealm{

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    /**
     * Get identity information, we can get the user's permissions and role information from the database in this method
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("----------doGetAuthorizationInfo method is called----------");
        String username = (String) getAvailablePrincipal(principals);
        //We can get permission/role information from database by username
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // permission
        Set<String> s = new HashSet<String>();
        s.add("printer:print");
        s.add("printer:query");
        info.setStringPermissions(s);
        //Role
        Set<String> r = new HashSet<String>();
        r.add("role1");
        info.setRoles(r);

        return info;
    }
    /**
     * In this method, perform authentication
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        //username
        String username = (String) token.getPrincipal();
        log.info("username:"+username);
        //password
        String password = new String((char[])token.getCredentials());
        log.info("password:"+password);
        //Get the username and password from the database for matching, here for the sake of aspect, the database operation is omitted
        if(!"admin".equals(username)){
            throw new UnknownAccountException();
        }
        if(!"123".equals(password)){
            throw new IncorrectCredentialsException();
        }
        //Authentication passed, return an identity information
        AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username,password,getName());

        return aInfo;
    }
}

 

 

  • To make the Realm we define work, we need to configure it in the configuration file (shiro-realm.ini)

#declare a realm

MyRealm1=com.shiro.realm.MyRealm1

#Specify the realms implementation of securityManager

securityManager.realms=$MyRealm1

 

 

  • test
package com.shiro.realm;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        //Get an instance of SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currenUser = SecurityUtils.getSubject();

        //if not authenticated
        if(!currenUser.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
            token.setRememberMe(true);
            try {
                currenUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("No such user: " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info( token.getPrincipal() + "Incorrect password!");
            } catch (LockedAccountException lae) {
                log.info( token.getPrincipal() + "locked, please contact administrator");
            }catch (AuthenticationException ae) {
                //other unknown exception
            }
        }

        if(currenUser.isAuthenticated())
            log.info("User"+currenUser.getPrincipal() +"Login successful");

        //Is there a role of role1
        if(currenUser.hasRole("role1")){
            log.info("has role role1");
        }else{
            log.info("No role role1");
        }
        //Do you have permission to print to the printer?
        if(currenUser.isPermitted("printer:print")){
            log.info("Can print to the printer");
        }else {
            log.info("Cannot print to printer");
        }
    }
}

 

Hash Algorithm Support

Generally, the passwords we store in the database are encrypted, such as performing one or more MD5 calculations on "original password + salt", and shiro provides support for hashing algorithms

package com.shiro.realm;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

public class UserRealm extends AuthorizingRealm {
    private String salt = "hehe";//盐
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        //Username entered by the user
        String username = (String) token.getPrincipal();
        //If there is no such user in the database, return null and login fails
        if(!username.equals("xiaozhou"))
            return null;

        //Query password from database
        String password = "42029a889cc26562c986346114c02367";

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,
                password, ByteSource.Util.bytes (salt), getName ());
        return info;
    }
}

 There is not much difference between the realm using MD5 and the general realm. The only difference is that if the hash algorithm is not used (that is, the password is encrypted), the password queried from the database is plaintext, otherwise the ciphertext is queried. The method uses the ciphertext to directly compare and judge whether the password is correct. In order to let shiro automatically help us encrypt and then compare, we need to tell shiro what algorithm to use in the configuration file ini

 

[main]
#password matcher
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
# matcher uses md5
credentialsMatcher.hashAlgorithmName=md5
#Do several hashes (use md5 algorithm to do several operations)
credentialsMatcher.hashIterations = 1

#realm
userRealm=com.shiro.realm.UserRealm
#Which matcher is used by this realm
userRealm.credentialsMatcher=$credentialsMatcher
# Which realm to use
securityManager.realms=$userRealm

 

Multiple Realms

Sometimes, we need to perform multiple authentications, we can define multiple Realms, like a pipeline, shiro will call the Realm in turn

MyRealm1

package com.shiro.mutilrealm;
import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.shiro.realm.Main;

public class MyRealm1 extends AuthorizingRealm{

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        String username = (String) getAvailablePrincipal(principals);
        //Get permission string from database by username
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // permission
        Set<String> s = new HashSet<String>();
        s.add("printer:print");
        s.add("printer:query");
        info.setStringPermissions(s);
        //Role
        Set<String> r = new HashSet<String>();
        r.add("role1");
        info.setRoles(r);

        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        log.info("MyRealm1 started authentication...");
        //username
        String username = (String) token.getPrincipal();
        log.info("username:"+username);
        //password
        String password = new String((char[])token.getCredentials());
        log.info("password:"+password);
        //Get the username and password from the database for matching, here for the sake of aspect, the database operation is omitted
        if(!"admin".equals(username)){
            throw new UnknownAccountException();
        }
        if(!"123".equals(password)){
            throw new IncorrectCredentialsException();
        }
        //authentication passed
        AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username,password,getName());
        return aInfo;
    }

}

 

  • The codes of MyRealm2 and MyRealm1 are basically the same, just copy one copy directly. Of course, if there is demand, we can freely define and modify Realm. Here is just an example.

Configure Authenticator and AuthenticationStrategy

  • What are these two things?

    Above, we have configured multiple Realms for authentication. Suppose: MyRealm1 passes the verification, and MyRealm2 fails to pass the verification. This requires defining an authentication strategy to deal with this situation. Strategy means strategy. Authenticator is the authenticator

 

Configuration statement (shiro-mutil-realm.ini)

 

#declare a realm  
MyRealm1=com.shiro.mutilrealm.MyRealm1
MyRealm2=com.shiro.mutilrealm.MyRealm2

#Configure the validator
authenticator = org.apache.shiro.authc.pam.ModularRealmAuthenticator
#Configure policy
# AllSuccessfulStrategy means that both MyRealm1 and MyRealm2 certifications are passed.
authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
#Associate the validator with the policy
authenticator.authenticationStrategy = $authcStrategy
#Configure the Realm used by the validator
authenticator.realms=$MyRealm2,$MyRealm1

#Set Authenticator to securityManager
securityManager.authenticator = $authenticator

##########################################################################
# 1. AtLeastOneSuccessfulStrategy : If one (or more) Realm validates successfully, the overall attempt is considered
# to be successful. If none of the verifications succeed, the entire attempt fails.

# 2. FirstSuccessfulStrategy Only the information returned by the first successfully authenticated Realm will be used. all further
# Realm will be ignored. If none of the verifications succeed, the overall attempt fails

# 3. AllSucessfulStrategy For the overall attempt to succeed, all configured Realms must be verified successfully. if there is no one
# verifications succeed, then the overall attempt fails.

# ModularRealmAuthenticator defaults to AtLeastOneSuccessfulStrategy
###########################################################################

 

 

  • test
package com.shiro.mutilrealm;

import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

    private static final transient Logger log = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        //Get an instance of SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-mutil-realm.ini");
        SecurityManager securityManager = factory.getInstance();

        SecurityUtils.setSecurityManager(securityManager);

        Subject currenUser = SecurityUtils.getSubject();

        //if not authenticated
        if(!currenUser.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken("admin","123");
            token.setRememberMe(true);
            try {
                currenUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("No such user: " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info( token.getPrincipal() + "Incorrect password!");
            } catch (LockedAccountException lae) {
                log.info( token.getPrincipal() + "locked, please contact administrator");
            }catch (AuthenticationException ae) {
                //other unknown exception
            }
        }

        if(currenUser.isAuthenticated())
            log.info("User"+currenUser.getPrincipal() +"Login successful");

        // get an identity set
        PrincipalCollection principalCollection = currenUser.getPrincipals();

    }

}

 

The result is obvious, MyRealm1 and MyRealm2 execute in sequence

 

Multiple Realm validation order

  • Implicit arrangement

    • When you configure multiple realms, the processing order defaults to the order you configure.
    • In this case, only the realm is defined, and the realms of the securityManager are not configured.
  • Explicit arrangement

    • That is, the displayed configuration securityManager.realms, then the order of execution is the order in which you configure the realm of the value.
    • Display arrangement is usually more recommended.

We can simply understand that the order of multiple Realm verifications is the order of our configuration

 

 

 

 

 

Guess you like

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