This is the 22nd day of my participation in Gwen Challenge

Shiro profile

1. Shiro’s basic function points

  1. Authentication: the Authentication
    • Verify whether the user is legitimate when logging in
  2. Authorization Authorization:
    • Authorizes who has access to certain resources
  3. Session Management: Session Management
    • User information after login is managed through Session Management, no matter in what application
  4. Cryptography: encryption
    • It provides some common encryption algorithms, which make it very convenient to realize data security in the application and use very convenient
  5. Web Support: Web application Support
    • Shiro can be easily integrated into Web applications
  6. Caching: the cache
    • Shiro provides support for caching and supports multiple caching architectures, such as Redis
    • Directly through the cache permission authentication, speed up efficiency
  7. Concurrency: Concurrency support
    • Supports multi-threaded concurrent access
  8. Testing: Testing
  9. The Run AS:
    • One user can log in with another identity if allowed
  10. -Chuck: Remember Me

Second, Shiro’s architecture

  1. Subject: the Subject
    • It can be a user, a third party application, etc
    • Subject is used to get information about the Principals and Credentials.
  2. Security Manager: Security Manager
    • The security manager is at the heart of Shiro’s architecture. It coordinates and manages the work of shiro’s various components
  3. Authenticator: indicates the Authenticator
    • Responsible for verifying the user’s identity
  4. Authorizer: Authorizer
    • Responsible for assigning permissions to legitimate users. Controls which resources a user can access
  5. Realms: domain
    • Shiro does the security work for the user and does not maintain the data.
    • During Shiro’s work, data is queried and retrieved from various data sources via Realm
    • Realm can get database information, text information, and so on
    • There can be one or more realms in Shiro. It can be configured either in the database or in a file.

Shiro basic use

First, environmental preparation

  1. Maven rely on
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.32.</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version> <scope>test</scope> </dependency> <! -- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>
Copy the code

Case: User authentication

process

  1. Create the SecurityManagerFactory from the configuration file
  2. Get the SecurityManager from the factory
  3. Bind the SecurityManager to the current running environment
  4. Construct the Subject from the current run environment
  5. Construct shiro login data
  6. Theme login

conclusion

  • The above sequence of engineering method code is designed to create the Subject object
  • Subject is the subject, which means that all user actions are done through this object
    • For example, login, verify whether you have permission

code

  1. shiro-test-1.ini
[users] # select * from user where username = password zhangsan=123456
lisi=654321
Copy the code
  1. ShiroTest01
package com.lark.shiro;

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

public class ShiroTest01 {

    /** * Test user authentication: * authentication: user login ** 1. Create SecurityManagerFactory * from the configuration file. Get the SecurityManager from the factory * 3. Bind the SecurityManager to the current running environment * 4. Construct subject * 5. Construct shiro login data * 6. Subject login * */

    @Test
    public void testLogin(a) {
        //1. Create SecurityManagerFactory based on the configuration file
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-test-1.ini");
        //2. Obtain the SecurityManager from the factory
        SecurityManager securityManager = factory.getInstance();
        //3. Bind the SecurityManager to the current running environment
        SecurityUtils.setSecurityManager(securityManager);
        //4. Construct the subject from the current running environment
        Subject subject = SecurityUtils.getSubject();
        //5. Construct shiro login data
        String username = "zhangsan";
        String password = "1234567";
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        //6. Principal login
        subject.login(token);
        //7. Verify that the login is successful
        System.out.println("User login successfully:"+subject.isAuthenticated());
        //8. Obtain successful login dataSystem.out.println(subject.getPrincipal()); }}Copy the code

It is worth noting that

  1. subject.login(token);
    • This code uses login to login, step by step into the code, and finds login using the securityManager first, then login using the validator
    • If there is no custom realm, the default realm is used to authenticate the login

Case: User authorization

process

  1. Same as above, login first
  2. Use the Subject method to determine whether the currently logged in user has this permission

code

  1. shiro-test-2.ini
[users]
# username = password, role name
zhangsan=123456,role1,role2
lisi=654321,role1
[roles]
The # character
# Role name = Permission list
role1=user:save,user:update
role2=user:find
Copy the code
  1. ShiroTest02
package com.lark.shiro;

import org.apache.shiro.SecurityUtils;
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.junit.Before;
import org.junit.Test;

public class ShiroTest02 {

    private SecurityManager securityManager;

    @Before
    public void init(a){
        //1. Create SecurityManagerFactory based on the configuration file
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-test-2.ini");
        //2. Obtain the SecurityManager from the factory
        SecurityManager securityManager = factory.getInstance();
        //3. Bind the SecurityManager to the current running environment
        SecurityUtils.setSecurityManager(securityManager);
    }

    @Test
    public void testAuth(a) {
        Subject subject = SecurityUtils.getSubject();
        String username = "zhangsan";
        String password = "123456";
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        subject.login(token);

        // After successful login, complete authorization
        // Authorization: Check whether the current login user has operation rights and has a role
        System.out.println(subject.hasRole("role1"));
        System.out.println(subject.isPermitted("user:save")); }}Copy the code

Create a custom realm

Steps to customize a realm

  1. What is realm?

Shiro obtains security data (such as users, roles, and permissions) from a Realm. To authenticate users, The SecurityManager needs to obtain the corresponding users from a Realm for comparison to determine whether the users are legitimate. You also need to get the user’s role/permissions from Realm to verify that the user can operate. You can think of a Realm as a DataSource

  1. Code level steps

    • Define yourself a class must inheritAuthorizingRealmclass
    public class PermissionRealm extends AuthorizingRealm {
    Copy the code
    • AuthorizingRealmThere are two methods that need to be overridden
    / / authorization
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {}
    / / certification
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {}
    Copy the code
  2. Verify the note

    • The purpose of authentication is to determine whether the user name and password are correct
    • If yes, save the username, password, and realm name toAuthenticationInfoIn this object
    • If you don’t throw an exception directly
  3. Authorized notice

    • When I first started learning, I often confused the difference between authorization and verification
    • Checking permissions means checking whether you have the permission or not. This is done in the business code via Subject, which has nothing to do with this
    • What is done here is: after verifying the user in the previous step, go to the database to find what permissions and roles he has, and then save the permissions and role information respectively toSimpleAuthorizationInfoIn the object
  4. code

package com.lark.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/** * Custom realm object * inherit AuthAuthorizingRealm * override method * doGetAuthorizationInfo: * doGetAuthenticationInfo: authentication * Log in with user name and password, store user data (security data) */
public class PermissionRealm extends AuthorizingRealm {

    /** * Custom realm name *@param name
     */
    public void setName(String name){
        super.setName("permissionRealm");
    }


    /** * Authorization: Main purpose: Obtain user permission information * based on authentication data@paramThe Principals contains all certified safety data *@returnAuthorization data * * 1. Obtain security data username and user ID * 2. Querying user information by ID or name * 3. Querying user role and permission information * 4. Construct returns */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("Implementation authorization Method");
        //1. Obtain security data username and user ID
        String username = (String) principals.getPrimaryPrincipal();
        //2. Query users by ID or name
        //3. Query the role and permission information of the user
        // The role permissions will be saved to the authorization data
        List<String> perms = new ArrayList<>();
        perms.add("user:save");
        perms.add("user:update");
        List<String> roles = new ArrayList<>();
        roles.add("role1");
        roles.add("role2");
        //4. Construct returns
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(perms);
        info.addRoles(roles);
        return info;
    }



    /** * Authentication. The main purpose of authentication is to compare the user name and password with the database * to store the security data in Shiro for safekeeping *@paramToken UsernamePasswordToken * of the login construct@return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("Implementation of authentication Method");
        1. Construct the UserNamepasswordToken object
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        //2. Obtain the entered user name and password
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        //3. Query the database based on the user name
        //4. Compare the password with the database password (password may need to be encrypted)
        if("123456".equals(password)){
            //5. If successful, store security data to Shiro
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());//1. Security data 2. Password 3
            return info;
        }else{
            //6. Fail, throw an exception or return NULL
            throw new RuntimeException("Wrong username or password"); }}}Copy the code

shiro-test-3.ini

  1. steps
    • Where is the custom realm
    • Register realm with the Security Manager
[main]
permRealm=com.lark.shiro.PermissionRealm
Register a realm with SecurityManager
securityManager.realms=$permRealm
Copy the code

ShiroTest03

  • Just like above, the login and authorization code used our custom realm instead of the default one
package com.lark.shiro;

import org.apache.shiro.SecurityUtils;
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.junit.Before;
import org.junit.Test;

public class ShiroTest03 {

    private SecurityManager securityManager;

    @Before
    public void init(a){
        //1. Create SecurityManagerFactory based on the configuration file
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-test-3.ini");
        //2. Obtain the SecurityManager from the factory
        SecurityManager securityManager = factory.getInstance();
        //3. Bind the SecurityManager to the current running environment
        SecurityUtils.setSecurityManager(securityManager);
    }

    @Test
    public void testAuth(a) {
        Subject subject = SecurityUtils.getSubject();
        String username = "zhangsan";
        String password = "123456";
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        // Execute the authentication method in the login-->realm domain
        subject.login(token);

        // After successful login, complete authorization
        // Authorization: Check whether the current login user has operation rights and has a role
        // Authorization: --> Authorization method in realm
        System.out.println(subject.hasRole("role1"));
        System.out.println(subject.isPermitted("user:save")); }}Copy the code

Summary of certification Process

  1. First, call subject.login (token) to login, which is automatically entrusted to the SecurityManager and must be set through securityutils.setsecuritymanager () before invoking.
  2. The SecurityManager is responsible for the real authentication logic; It delegates to the Authenticator for authentication;
  3. Authenticator is the real Authenticator, the core authentication entry point in Shiro API, where you can insert your own implementation.
  4. The Authenticator may delegate to the corresponding AuthenticationStrategy for multi-realm authentication, The default ModularRealmAuthenticator AuthenticationStrategy will call for many Realm authentication;
  5. The Authenticator passes the corresponding token to the Realm and gets authentication information from the Realm. If no exception is returned/thrown, authentication failed. Multiple realms can be configured here and will be accessed in the appropriate order and policy.

Summary of Authorization Process

  1. . First call the Subject isPermitted/hasRole interface, its will be entrusted to the SecurityManager, the SecurityManager then will be entrusted to Authorizer;
  2. Authorizer is the true Authorizer. If we called isPermitted(” user:view “), it would first convert the string into a corresponding Permission instance through a PermissionResolver.
  3. Prior to authorization, it calls the corresponding Realm to get the Subject’s corresponding role/permission to match the role/permission passed in;
  4. If there are more than one Realm, the Authorizer will delegate to the ModularRealmAuthorizer to determine if the role/permission of a Realm matches the one passed in. If a Realm matches as isPermitted/hasRole, the ModularRealmAuthorizer returns true. Otherwise, false is returned indicating authorization failure.