Interpretation of Apache Shiro realm actual combat and authentication authorization source code

Time:2022-6-20

In the previous article, we explained some basic knowledge of Apache Shiro. Today, we will practice Shiro realm and interpret the source code of Shiro authentication and authorization.

  1. Real combat of Shiro security data source

From the explanation in the previous chapter, we learned that our realm is actually used to verify permission information. Shiro obtains security data from the realm. By default, the Shiro framework provides two implementations, one is query The inirealm of the INI file and the jdbcrealm of the query database. In addition, we can customize the realm according to our own needs. There are two concepts that need to be understood first:

Principal: there can be more than one entity identifier, but it needs to be unique. The common ones are user name, mobile phone number, e-mail, etc

Credential: a credential, usually a password

Next, let’s look at how to use Shiro’s default implemented realm and how to customize realm:

New Java development training system of shangsilicon Valley

The new course system of 2020java in Silicon Valley, project practice and training of java development practice talents!

Shangsilicon Valley IT training

see

1.1 real operation and common usage of Shiro’s default implementation

1.1.1 inirealm operation of Shiro built-in realm

1) First we need to create one Ini configuration file and configure it according to the corresponding syntax format:

Format username=password, role1, role2 roleN

[users]

atguigu=123456,user

tom=456789,root,admin

Format role=permission1, permission2 Permissionn can also use wildcards

Next, configure the permissions of the user role as all video:find, video:buy. If you need to configure all video operations crud, then user=video:*

Permissions are self configured. The general format is: Resource Name: operation. For example, the video update operation can be defined as: video:update

[roles]

user = video:find,video:buy

admin = video:update,video:delete,comment:*

The root role has all permissions, which can be represented by the wildcard *

root=*

2) Create a [inirealmtest] test class:

package com.atguigu.shiro.demo;

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.apache.shiro.util.Factory;

import org.junit.Test;

public class IniRealmTest {
@Test

public void test() {
//Create the securitymanager factory and read the configuration file shiro ini

Factory<SecurityManager> factory = new IniSecurityManagerFactory(“classpath:shiro.ini”);

SecurityManager securityManager = factory.getInstance();

//Set securitymanager to the current running environment

SecurityUtils.setSecurityManager(securityManager);

//Get current principal

Subject subject = SecurityUtils.getSubject();

//Account and password entered by the user

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(“atguigu”, “123456”);

//Principal submits login authentication

subject.login(usernamePasswordToken);

System. out. Println (“authentication result:” +subject.isauthenticated());

System. out. Println (“whether there is a corresponding user role:” + subject.hasrole (“user”));

System. out. Println (“whether there is a corresponding root role:” + subject.hasrole (“root”);

System. out. Println (“the user name of the current user is:” + subject. Getprincipal());

System. out. Println (“does the current user have video:find permission:” + subject.ispermitted (“video:find”));

System. out. Println (“does the current user have video:delete permission:” + subject.ispermitted (“video:delete”));

}

}

3) Run the test case, and you can see the following results:

1.1.2 jdbcrealm operation of Shiro built-in realm

1) Since the jdbcrealm needs to operate the database, you need to import the related dependency packages connecting to the database before writing the test case:

<!– MySQL driver package — >

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<!– Comment out –>

<!–<scope>runtime</scope>–>

</dependency>

<!– Alibaba Druid data source –>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.1.6</version>

</dependency>

2) Create corresponding database tables to store user permission information. Since jdbcrealm provides SQL written by default for database query, table names and field names need to be mapped when creating database tables. The following provides SQL scripts required for creating database tables and inserts corresponding test data:

Role permission correspondence table

DROP TABLE IF EXISTS roles_permissions;

CREATE TABLE roles_permissions (

id bigint(20) NOT NULL AUTO_INCREMENT,

role_name varchar(100) DEFAULT NULL,

permission varchar(100) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY idx_roles_permissions (role_name,permission)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES roles_permissions WRITE;

Insert corresponding test data

INSERT INTO roles_permissions (id, role_name, permission)

VALUES

(4,’admin’,’video:*’),

(3,’role1′,’video:buy’),

(2,’role1′,’video:find’),

(5,’role2′,’video:list’),

(1,’root’,’*’);

UNLOCK TABLES;

User role correspondence table

DROP TABLE IF EXISTS user_roles;

CREATE TABLE user_roles (

id bigint(20) NOT NULL AUTO_INCREMENT,

username varchar(100) DEFAULT NULL,

role_name varchar(100) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY idx_user_roles (username,role_name)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES user_roles WRITE;

Insert test data

INSERT INTO user_roles (id, username, role_name)

VALUES

(1,’jack’,’role1′),

(2,’jack’,’role2′),

(4,’atguigu’,’admin’),

(3,’atguigu’,’root’);

UNLOCK TABLES;

User information table

DROP TABLE IF EXISTS users;

CREATE TABLE users (

id bigint(20) NOT NULL AUTO_INCREMENT,

username varchar(100) DEFAULT NULL,

password varchar(100) DEFAULT NULL,

password_salt varchar(100) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY idx_users_username (username)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES users WRITE;

Insert test data

INSERT INTO users (id, username, password, password_salt)

VALUES

(1,’jack’,’123′,NULL),

(2,’atguigu’,’123456′,NULL);

UNLOCK TABLES;

3) After running the SQL script to create the corresponding data table, you need to write the engineering code. Jdbcrealm provides two ways to implement it. The following two ways are introduced and code implemented respectively:

Method 1: use Ini configuration file

Write the configuration file jdbcrealm ini

Note that the file format must be ini and the encoding must be ANSI

Declare realm, specify realm type

jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm

Configure data source because we have introduced Druid data source dependency, we use Druid data source here

dataSource=com.alibaba.druid.pool.DruidDataSource

The drive URL for MySQL connector Java 5 is com mysql. jdbc. Driver and mysql-connector-java6 will use com mysql. cj. jdbc. Driver

dataSource.driverClassName=com.mysql.cj.jdbc.Driver

Data source link

dataSource.url=jdbc:mysql://192.168.200.128:3306/atguigu_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false

dataSource.username=root

dataSource.password=123456

specify data source

jdbcRealm.dataSource=$dataSource

Enable the search permission, which is false by default. If you do not enable it, you will not find the permission corresponding to the role. This is a pit!!!!!

jdbcRealm.permissionsLookupEnabled=true

Specify the realms implementation of securitymanager and set realms. There can be multiple realms separated by commas

securityManager.realms=$jdbcRealm

Create a new [jdbcrealmtest] test class:

@Test

public void test() {
//Create a securitymanager factory and load jdbcrealm Ini configuration file

Factory<SecurityManager> factory = new IniSecurityManagerFactory(“classpath:jdbcrealm.ini”);

SecurityManager securityManager = factory.getInstance();

//Set securitymanager to the current running environment

SecurityUtils.setSecurityManager(securityManager);

Subject subject = SecurityUtils.getSubject();

//Account and password entered by the user

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(“jack”, “123”);

subject.login(usernamePasswordToken);

System. out. Println (“authentication result:” +subject.isauthenticated());

System. out. Println (“whether there is a corresponding role1 role:” +subject.hasrole (“role1”));

System. out. Println (“have video:find permission:” + subject.ispermitted (“video:find”));

}

Run the test case and the results are as follows:

Method 2: self configuring data source and jdbcrealm

Dependency introduction and mode

Write a test case in the [jdbcrealmtest] test class:

@Test

public void test2(){
DefaultSecurityManager securityManager = new DefaultSecurityManager();

DruidDataSource ds = new DruidDataSource();

ds.setDriverClassName(“com.mysql.cj.jdbc.Driver”);

ds.setUrl(“jdbc:mysql://192.168.200.128:3306/atguigu_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false”);

ds.setUsername(“root”);

ds.setPassword(“123456”);

JdbcRealm jdbcRealm = new JdbcRealm();

jdbcRealm.setPermissionsLookupEnabled(true);

jdbcRealm.setDataSource(ds);

securityManager.setRealm(jdbcRealm);

//Set securitymanager to the current running environment

SecurityUtils.setSecurityManager(securityManager);

Subject subject = SecurityUtils.getSubject();

//Account and password entered by the user

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(“jack”, “123”);

subject.login(usernamePasswordToken);

System. out. Println (“authentication result:” +subject.isauthenticated());

System. out. Println (“whether there is a corresponding role1 role:” +subject.hasrole (“role1”));

System. out. Println (“have video:find permission:” + subject.ispermitted (“video:find”));

System. out. Println (“have any permission:” + subject.ispermitted (“aaaa:xxxxxxxx”));

}

Run the test case and the results are as follows:

1.2 Shiro custom realm practice

1.2.1 practical basis of custom realm

1) Steps:

Create a class to inherit the authorizingrealm. The inheritance relationship of the authorizingrealm is shown in the following figure:

It can be seen from the above figure that the inheritance relationship is: authorizingrealm->authenticatingream->cacheingream->realm

Override authorization method dogetauthorizationinfo

Override authentication method dogetauthenticationinfo

method:

When the user logs in, dogetauthenticationinfo will be called to obtain authentication information for user identity authentication

During permission verification, dogetauthorizationinfo will be called to obtain authorization information for user authorization

Object introduction

Usernamepasswordtoken: corresponds to the account and password information entered by the user. There are principal and credential in the token. The inheritance diagram of usernamepasswordtoken is as follows:

It can be seen from the above figure that the inheritance relationship is: usernamepasswordtoken->hostauthenticationtoken->authenticationtoken

Simpleauthorizationinfo: represents user role permission information

Simpleauthenticationinfo: authentication information on behalf of the user

1.2.2 custom realm code practice

With the above understanding of basic knowledge and authentication authorization, we first create a [customrealm] class under the appropriate package, inherit the authorizingream class of Shiro framework, and implement two default methods:

package com.atguigu.shiro.demo;

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.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

public class CustomRealm extends AuthorizingRealm {
//Here, the map set is used to simulate the association relationship among users, roles and permissions. In actual development, queries will be made from the database

private final Map<String,String> userInfoMap = new HashMap<>();

{
userInfoMap.put(“jack”,”123″);

userInfoMap.put(“atguigu”,”123456″);

}

//role -> permission

private final Map<String, Set<String>> permissionMap = new HashMap<>();

{
Set<String> set1 = new HashSet<>();

Set<String> set2 = new HashSet<>();

set1.add(“video:find”);

set1.add(“video:buy”);

set2.add(“video:add”);

set2.add(“video:delete”);

permissionMap.put(“jack”,set1);

permissionMap.put(“atguigu”,set2);

}

//user -> role

private final Map<String,Set<String>> roleMap = new HashMap<>();

{
Set<String> set1 = new HashSet<>();

Set<String> set2 = new HashSet<>();

set1.add(“role1”);

set1.add(“role2”);

set2.add(“root”);

roleMap.put(“jack”,set1);

roleMap.put(“atguigu”,set2);

}

//Called during permission verification

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System. out. Println (“permission dogetauthorizationinfo”);

String name = (String)principals.getPrimaryPrincipal();

Set<String> permissions = getPermissionsByNameFromDB(name);

Set<String> roles = getRolesByNameFromDB(name);

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

simpleAuthorizationInfo.setRoles(roles);

simpleAuthorizationInfo.setStringPermissions(permissions);

return simpleAuthorizationInfo;

}

//Called when the user logs in

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System. out. Println (“authentication dogetauthenticationinfo”);

//Obtain identity information from the token, which represents the information entered by the user

String name = (String)token.getPrincipal();

//Simulate fetching password from database

String pwd = getPwdByUserNameFromDB(name);

if( pwd == null || “”.equals(pwd)){
return null;

}

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pwd, this.getName());

return simpleAuthenticationInfo;

}

/**

  • Simulate getting user role collection from database
  • @param name
  • @return

*/

private Set<String> getRolesByNameFromDB(String name) {
return roleMap.get(name);

}

/**

  • Simulate getting permission sets from the database
  • @param name
  • @return

*/

private Set<String> getPermissionsByNameFromDB(String name) {
return permissionMap.get(name);

}

private String getPwdByUserNameFromDB(String name) {
return userInfoMap.get(name);

}

}

Then we write a test class to verify whether it is correct:

package com.atguigu.shiro.demo;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.mgt.DefaultSecurityManager;

import org.apache.shiro.subject.Subject;

import org.junit.Test;

public class AuthenticationTest {
private CustomRealm customRealm = new CustomRealm();

private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

@Before

public void init(){
//Build environment

defaultSecurityManager.setRealm(customRealm);

SecurityUtils.setSecurityManager(defaultSecurityManager);

}

@Test

public void testAuthentication() {
//Get the body of the current operation

Subject subject = SecurityUtils.getSubject();

//Account and password entered by the user

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(“jack”, “123”);

subject.login(usernamePasswordToken);

//Login

System. out. Println (“authentication result:” +subject.isauthenticated());

//Get the subject identification attribute

System.out.println(” getPrincipal=” + subject.getPrincipal());

subject.checkRole(“role1”);

System. out. Println (“whether there is a corresponding role:” +subject.hasrole (“role1”));

System. out. Println (“whether there is corresponding permission:” +subject.ispermitted (“video:add”));

}

}

Run the test case and the results are as follows:

  1. In depth interpretation of Shiro source code authentication and authorization process

2.1 interpretation of certification process source code

1) We use subject Use login (token) as the starting point for breakpoint debugging

2)DelegatingSubject. Login (token) will delegate the authentication request to the login () method of defaultsecuritymanager for processing, and then continue to enter the login () method of defaultsecuritymanager

public class DelegatingSubject implements Subject {
……

public void login(AuthenticationToken token) throws AuthenticationException {
this.clearRunAsIdentitiesInternal();

//Call the login method of defaultsecuritymanager for identity authentication

Subject subject = this.securityManager.login(this, token);

……

}

……

}

3) Defaultsecuritymanager’s login() method will call authenticatingsecuritymanager’s authenticate() method for authentication, and then continue to enter authenticatingsecuritymanager’s authenticate() method

public class DefaultSecurityManager extends SessionsSecurityManager {
……

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;

try {
//Call the authenticate() method of authenticatingsecuritymanager to verify

info = this.authenticate(token);

} catch (AuthenticationException var7) {
……

}

Subject loggedIn = this.createSubject(token, info, subject);

this.onSuccessfulLogin(token, info, loggedIn);

return loggedIn;

}

……

}

4) The authenticate() method of authenticatingsecuritymanager will call the authenticate() method of abstractauthenticator for authentication, and then continue to enter the authenticate() method of abstractauthenticator

public abstract class AuthenticatingSecurityManager extends RealmSecurityManager {
……

public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
//Call the authenticate() method of abstractauthenticator to verify

return this.authenticator.authenticate(token);

}

……

}

5) The authenticate() method of abstractauthenticator will call the doauthenticate() method of modularrealmauthenticator for authentication, and then continue to enter the doauthenticate() method of modularrealmauthenticator

public abstract class AbstractAuthenticator implements Authenticator, LogoutAware {
……

public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException(“Method argument (authentication token) cannot be null.”);

} else {
log.trace(“Authentication attempt received for token [{}]”, token);

AuthenticationInfo info;

try {
//Call doauthenticate() of modularrealmauthenticator to verify

info = this.doAuthenticate(token);

if (info == null) {
String msg = “No account information found for authentication token [” + token + “] by this ” + “Authenticator instance. Please check that it is configured correctly.”;

throw new AuthenticationException(msg);

}

} catch (Throwable var8) {
……

}

log.debug(“Authentication successful for token [{}]. Returned account [{}]”, token, info);

this.notifySuccess(token, info);

return info;

}

}

……

}

6) The doauthenticate() method of modularrealmauthenticator will obtain the corresponding realm. Since we currently have only one realm configured, we will execute the dosinglerealauthentication() method, then the getauthenticationinfo() method of authenticatingrealm, and then continue to enter the getauthenticationinfo() method of authenticatingrealm

public class ModularRealmAuthenticator extends AbstractAuthenticator{
……

protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();

//Get the currently configured realm

Collection<Realm> realms = this.getRealms();

//If the number of currently configured realms is 1, execute dosinglerealauthentication() method; otherwise, execute domultirealauthentication()

return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);

}

protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = “Realm [” + realm + “] does not support authentication token [” + token + “]. Please ensure that the appropriate Realm implementation is ” + “configured correctly or that the realm accepts AuthenticationTokens of this type.”;

throw new UnsupportedTokenException(msg);

} else {
AuthenticationInfo info = realm.getAuthenticationInfo(token);

if (info == null) {
String msg = “Realm [” + realm + “] was unable to find account data for the ” + “submitted AuthenticationToken [” + token + “].”;

throw new UnknownAccountException(msg);

} else {
return info;

}

}

}

……

}

7) Here we can see that the getauthenticationinfo () method of authenticatingream will call dogetauthenticationinfo (token) of our custom configured realm to obtain authentication information, and call the assertcredentialsmatch method of authenticatingream to perform password matching authentication

public abstract class AuthenticatingRealm extends CachingRealm implements Initializable{
……

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = this.getCachedAuthenticationInfo(token);

if (info == null) {
//Call the dogetauthenticationinfo (token) method of the custom configured realm

info = this.doGetAuthenticationInfo(token);

log.debug(“Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo”, info);

if (token != null && info != null) {
this.cacheAuthenticationInfoIfPossible(token, info);

}

} else {
log.debug(“Using cached authentication info [{}] to perform credentials matching.”, info);

}

if (info != null) {
//Password verification for the obtained authentication information

this.assertCredentialsMatch(token, info);

} else {
log.debug(“No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.”, token);

}

return info;

}

……

}

At this point, our authentication process is over. From the source code, we can see that the authentication process ends with the calling of our custom realm and rewritten dogetauthenticationinfo (token) method. From the above source code analysis, we can get the sequence diagram of the following method calls:

2.2 interpretation of authorization process source code

1) In the authorization process, we use subject Using the checkrole() method as the starting point for breakpoint debugging

2) The checkrole() method of delegatingsubject will delegate the authorization request to the checkrole() method of authorizingsecuritymanager for processing, and then continue to enter the checkrole() method of authorizingsecuritymanager

public class DelegatingSubject implements Subject {
……

public void checkRole(String role) throws AuthorizationException {
this.assertAuthzCheckPossible();

//Delegate the request to the checkrole() method of the authorizingsecuritymanager

this.securityManager.checkRole(this.getPrincipals(), role);

}

……

}

3) The authorizingsecuritymanager’s checkrole() method delegates the request to the modularrealmauthorizer’s checkrole() method for processing, and then continues to enter the modularrealmauthorizer’s checkrole() method

public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
……

public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
//Delegate the request to the checkrole() method of modularrealmauthorizer

this.authorizer.checkRole(principals, role);

}

……

}

4) The checkrole() method of modularrealmauthorizer will call the hasrole() method of authorizingrealm to verify whether the current user has the corresponding permission. If there is no corresponding permission, an exception will be thrown, and then continue to enter the hasrole() method of authorizingrealm

public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
……

public void checkRole(PrincipalCollection principals, String role) throws AuthorizationException {
this.assertRealmsConfigured();

//Verify whether the current user has corresponding permissions. If not, an exception will be thrown

if (!this.hasRole(principals, role)) {
throw new UnauthorizedException(“Subject does not have role [” + role + “]”);

}

}

public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
this.assertRealmsConfigured();

Iterator var3 = this.getRealms().iterator();

Realm realm;

do {
if (!var3.hasNext()) {
return false;

}

realm = (Realm)var3.next();

//Call the hasrole() method of authorizingrealm to verify. If there are corresponding permissions, it returns true. Otherwise, it returns false

} while(!(realm instanceof Authorizer) || !((Authorizer)realm).hasRole(principals, roleIdentifier));

return true;

}

……

}

5) The hasrole () method of the authorizingream will call the dogetauthorizationinfo () method of the custom realm and rewrite it through the getauthorizationinfo () method to obtain the authorization information owned by the current user

public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
……

public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
AuthorizationInfo info = this.getAuthorizationInfo(principal);

return this.hasRole(roleIdentifier, info);

}

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;

} else {
AuthorizationInfo info = null;

……

if (info == null) {
//Call the dogetauthorizationinfo() method of the custom realm and rewrite to obtain the authorization information of the current user

info = this.doGetAuthorizationInfo(principals);

//If the authorization information exists and there is a cache, the current authorization information is cached

if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace(“Caching authorization info for principals: [” + principals + “].”);

}

key = this.getAuthorizationCacheKey(principals);

cache.put(key, info);

}

}

return info;

}

}

……

}

At this point, our authorization process is over. From the source code, we can see that the authorization process finally calls our custom realm and rewritten dogetauthorizationinfo() method. From the above source code analysis, we can get the sequence diagram of the following method calls:

So far, we have interpreted Shiro’s authentication and authorization process through the source code. From the above analysis of java training, we can know that when Shiro authenticates and authorizes, we will call the dogetauthenticationinfo() method and dogetauthorizationinfo() method rewritten when we customize the realm to obtain the authentication and authorization information of the current subject, Therefore, we need to define the logic for obtaining authentication and authorization information in the custom realm according to the specific situation of the project.

  1. Data encryption and decryption for Shiro data security

Why do I need encryption and decryption?

In the previous study, the passwords we saved in the database are all clear text. Once the database data is leaked, it will cause incalculable losses.

What is a hash algorithm?

It is generally called hash. In short, it is a function that compresses messages of any length to message summaries of a fixed length. It is suitable for storing passwords, such as MD5

What is salt?

If the encrypted data is directly obtained through the hash function, it is easy to be brutally cracked by the corresponding decryption website. Generally, special fields will be added to the application for processing, such as user ID. for example, encrypted data = MD5 (plaintext password + user ID), which will be more difficult to crack. Multiple hashes can also be used, such as multiple MD5

How does Shiro encrypt and match the database password according to the password passed by the user?

Shiro will use the assertcredentialsmatch() method of authenticatingrealm to verify whether the password is correct

Therefore, in Shiro, we usually customize password verification rules. Examples are as follows:

@Bean

public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

//Hash algorithm, using MD5 algorithm;

hashedCredentialsMatcher.setHashAlgorithmName(“md5”);

//The number of hashes, such as twice hashing, is equivalent to MD5 (MD5 (“XXX”);

hashedCredentialsMatcher.setHashIterations(2);

return hashedCredentialsMatcher;

}

After customizing the authentication rules, Shiro will call the corresponding logic to encrypt the password entered by the user and match it with the password stored in the database.

To sum up: in today’s article, we will carry out Shiro real combat and interpret the source code of Shiro authentication and authorization. After that, we will explain Shiro’s cache and session module, and integrate springboot2 X for comprehensive actual combat, please look forward to it.

key word:Java training