2. Shiro authentication

This article refers to Zhang Kaitao's "Learn Shiro from Me"

Authentication

Simply put, who can prove that he is himself in the application. Generally provide their identity information to show that he is himself, for example, provide ID card, username/password to prove.

In Shiro, the user needs to provide proof of principals and credentials to Shiro, so that the application can verify the user's identity;

  1. principals:身份,就是主体的标识属性, Can be anything, such as username, email, etc., as long as it is unique. A subject can have multiple identities, but only one identity certificate, usually a username/password/mobile phone number.
  2. credentials:证明/凭证,就是只有主体才知道的安全信息,比如密码. The most common combination of principals and credentials is the username and password.

Code

1. Add dependencies

First add the dependencies of junit, common-logging and shiro-core:

	<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.2</version>
        </dependency>
    </dependencies>

2. Prepare shiro.ini file

Use the ini configuration file here to configure basic user information

[users]
javaboy=123

3. Test code

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
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.junit.Assert;

/**
 * @Author: 红颜祸水nvn <[email protected]>
 * @Description: CSDN <https://blog.csdn.net/qq_43647359>
 */
public class TestHelloWorld {
    
    
    public static void main(String[] args) {
    
    
        // 1.获取 SecurityManager 工厂,此处使用 ini 配置文件初始化 SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 2.得到 SecurityManager 实例,并绑定给 SecurityUtils
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        // 3.得到 Subject 及创建用户名/密码身份验证 Token
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("javaboy", "123");

        try {
    
    
            // 4.登陆(身份验证)
            subject.login(token);
        } catch (AuthenticationException e) {
    
    
            // 5.身份验证失败
            System.out.println("用户名或密码错误");
        }
        // 断言用户已经登陆
        Assert.assertEquals(true,subject.isAuthenticated());
        // 6.退出
        subject.logout();
        System.out.println("登陆成功且退出!");
    }
}
  1. First specify an ini configuration file through new IniSecurityManagerFactory to create a SecurityManager factory;

  2. Then get the SecurityManager and bind it to SecurityUtils. This is a global setting, which can be set once.

  3. Then get the Subject through SecurityUtils, it will be automatically bound to the current thread; if the Web environment needs to unbind at the end of the request; then get the authentication token, such as username/password;

  4. Call the subject.login method to log in, it will automatically delegate to the SecurityManager.login method to log in, that is to saysubject.login 实际上是 SecurityManger.login 方法来执行登陆操作的。

  5. If an authentication error occurs, an AuthenticationException will be triggered. Common ones are:

    DisabledAccountException:禁用的账号
    LockedAccountException:锁定的账号
    UnknownAccountException:错误的账号
    ExcessiveAttemptsException:登陆失败次数过多
    IncorrectCredentialsException:错误的凭证
    ExpiredCredentialsException:过期的凭证

  6. Finally, you can call subject.logout to exit.

Identity authentication process

Insert picture description here

  1. First call Subject.login(token)for landing, it will automatically be delegated to Security Manager, before calling must SecurityUtils.setSecurityManager()be set.
  2. SecurityManager 负责真正的身份验证逻辑; It will be handed over to Authenticator for authentication;
  3. Authenticator 才是真正的身份校验者. The core identity authentication entry point of Shiro API, here you can customize the implementation scheme.
  4. Authenticator may be handed over to the corresponding AuthenticationStrategy for multi-Realm authentication. By default, ModularRealmAuthenticator will call AuthenticationStrategy to continue multi-Realm authentication;
  5. Authenticator 会把相应的 token 传入 Realm 中To get from the Realm 身份验证信息, 如果拿到了返回的信息或者中途没有抛出异常表示身份验证成功了. Multiple Realms can be configured here, and they will be accessed in the corresponding order and strategy.

A brief introduction to Realm

Realm: We can call it a "domain" and Shiro obtains security data from Realm.

Simply put, SecurityManager needs to verify the user's identity, then it needs to obtain the corresponding user from the Realm for comparison to confirm whether the user's identity is legal;

Of course, you also need to obtain the user's corresponding permissions from the Realm to verify whether the user can operate;

Realm can be regarded as a DataSource, which is a secure data source. For example, our previous ini configuration file method is this way.

The org.apache.shiro.realm.Realm interface is as follows:

Insert picture description here

// 返回一个唯一的 Realm 名字
String getName(); 
// 判断此 Realm 是否支持此 Token
boolean supports(AuthenticationToken token);
// 根据Token 获取认证信息
AuthenticationInfo 
getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

Custom Realm

We can customize a class to implement the Realm interface:

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;

/**
 * @Author: 红颜祸水nvn <[email protected]>
 * @Description: CSDN <https://blog.csdn.net/qq_43647359>
 */
public class MyRealm implements Realm {
    
    
    @Override
    public String getName() {
    
    
        return "MyRealm";
    }

    @Override
    public boolean supports(AuthenticationToken token) {
    
    
        // 仅仅支持 UsernamePassworkToken 类型的 Token
        return token instanceof UsernamePasswordToken;
    }

    @Override
    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        // 通过 token.getPrincipal 拿到用户名
        String username = (String) token.getPrincipal();
        // 通过 token.getCredentials 拿到密码
        String password = new String((char[]) token.getCredentials());
        if (!"javaboy".equals(username)) {
    
    
            // 用户名错误
            throw new UnknownAccountException();
        }
        if (!"123".equals(password)) {
    
    
            // 密码错误
            throw new IncorrectCredentialsException();
        }
        // 如果身份校验成功,返回一个 AuthenticationInfo
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

The ini configuration file specifies a custom Realm implementation, first create a shiro-realm.ini file

# 声明一个 Realm
myRealm=org.shiro.test.MyRealm
# 指定 securityManager 的 realms 实现
securityManager.realms=$myRealm

Introduce custom realm through $name

You only need to modify the previously loaded shiro.ini configuration file during testing.
Insert picture description here

Multi Realm configuration

Modify in the ini configuration file

# 声明一个 realm
myRealm1=org.shiro.test.MyRealm1
myRealm2=org.shiro.test.MyRealm2
# 指定 securityManager 的 realms 实现
securityManager.realms=$myRealm1,$myRealm2

securityManager will perform authentication in the order specified by realms. We can also specify the order of Realm by displaying the specified order. If you delete "securityManager.realms = my R ealm 1, myRealm1,m y R e a l m 1 , myRealm2", then the securityManager will be used in the order of realm declaration, but when we display the specified realm, other realm that is not specified will be ignored. For example: "securityManager.realms=$myRealm1", Then myRealm2 will not be automatically set in.

The test is the same as above.

Realm provided by Shiro by default

Insert picture description here

The main implementation is as follows:

  • org.apache.shiro.realm.text.IniRealm:

    [users] is used to specify user name/password and its role;
    [roles] is used to specify role authority information;

  • org.apache.shiro.realm.text.PropertiesRealm:

    user.username=password,role1,role2 specifies the user name/password and its role;
    role.role1=permission1,permission2 specifies the role and permission information;

  • org.apache.shiro.realm.jdbc.JdbcRealm:

    Query the corresponding information through sql:

// 获取用户密码
select password from users where username=?
// 获取用户密码及盐
select password,password_salt from users where username=?
// 获取用户角色
select role_name from user_roles where username=?
// 获取角色对应的权限信息
select permission from roles_permissions where role_name=?

Authenticator 及 AuthenticationStrategy

The role of Authenticator is to verify the user account, which is the entry point of the core authentication in Shiro API:

public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)  throws AuthenticationException;

If the authentication is successful, it will return AuthenticationInfo authentication information, which contains the identity and credentials; if the authentication fails, the corresponding AuthenticationException will be thrown.

The SecurityManager interface inherits the Authenticator, and there is also a ModularRealmAuthenticator implementation, which delegates to multiple Realms for authentication. The authentication rules are specified through the AuthenticationStrategy interface. The implementation provided by default:

  • FirstSuccessfulStrategy:只要有一个 Realm 验证成功,只返回第一个 Realm 身份验证成功的认证信息,其他的都会被忽略。
  • AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息。
  • AllSuccessfulStrategy:所有的 Realm 验证成功才算成功,且返回所有的 Realm 身份验证成功的认证信息,如果有一个失败就全部失败。

Guess you like

Origin blog.csdn.net/qq_43647359/article/details/105878493