zl程序教程

您现在的位置是:首页 >  后端

当前栏目

SpringBoot 与Shiro 整合系列(四)多realm延伸之实现多realm不同数据表用户登录认证和鉴权

SpringBoot认证 实现 系列 用户 登录 不同 整合
2023-09-11 14:20:19 时间

一、前言

shiro是一个很好的登陆以及权限管理框架,但是默认是单realm单数据表,如果业务中用户分布在不同的数据表,单realm就很难实现登陆以及权限管理的功能,本章简单的介绍一个普通用户和系统管理员账号分布在不同的数据表情况下,即需要两个Realm——MemberRealm和UserRealm,分别处理普通用户和系统管理员的验证功能。

shiro的多realm登陆验证,使用springboot,mybatis mysql等相关技术。

在这里插入图片描述

正常情况下,当定义了多个Realm,无论是普通用户登录,还是系统管理员登录,都会由这2个Realm共同处理。这是因为,当配置了多个Realm时,我们通常使用的认证器是shiro自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator其中决定使用的Realm的是doAuthenticate()方法,源代码如下:

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        assertRealmsConfigured();
        Collection<Realm> realms = getRealms();
        if (realms.size() == 1) {
            return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
        } else {
            return doMultiRealmAuthentication(realms, authenticationToken);
        }
    }

上述代码的意思就是如果有多个Realm就会使用所有配置的Realm。 只有一个的时候,就直接使用当前的Realm。

二、Shiro 多Realm配置的流程

1.添加依赖 pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  
  <!-- 一、继承 SpringBoot 默认的父工程 -->
  <!--  SpringBoot 父工程 -->
  <parent>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-parent</artifactId>
     <version>2.0.4.RELEASE</version>
  </parent>
  
  <groupId>com.koncord</groupId>
  <artifactId>springboot-shiro</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <!-- 二、导入依赖 -->
  <dependencies>
    <!-- 导入web支持:SpringMVC开发支持,Servlet相关的程序 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  
    <!--thymeleaf依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- thymeleaf对shiro的扩展坐标 -->
    <!-- thymeleaf整合shiro标签 -->
    <dependency>
      <groupId>com.github.theborakompanioni</groupId>
      <artifactId>thymeleaf-extras-shiro</artifactId>
      <version>2.0.0</version>
    </dependency>
    
    
    
    <!-- shiro 与 spring 整合依赖 -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.4.0</version>
    </dependency>
    
    <!-- 导入mybatis相关的依赖 -->
    <!-- SpringBoot的mybatis启动器 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.0</version>
    </dependency>
    <!--Mysql 驱动  -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
		<version>8.0.21</version>
	</dependency>
	<!--druid数据源(连接池)  -->
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid</artifactId>
		<version>1.1.9</version>
	</dependency>    
    
  </dependencies>
  
  <!-- 三、修改参数 -->
  <properties>
     <!-- 修改JDK的编译版本为1.8 -->
     <java.version>1.8</java.version>
  </properties>
  <build/>
</project>

2.用户数据表

CREATE TABLE `sys_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(50) DEFAULT NULL COMMENT '登录密码',
  `perms` varchar(50) DEFAULT NULL COMMENT '权限字符串',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统管理员';

CREATE TABLE `t_member` (
  `member_id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(50) DEFAULT NULL COMMENT '登录密码',
  `perms` varchar(50) DEFAULT NULL COMMENT '权限字符串',
  PRIMARY KEY (`member_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='普通用户';


系统管理员 添加两条数据:
在这里插入图片描述
普通用户 添加1条数据:
在这里插入图片描述

3.shiro 多Realm配置

实现多realm的关键:在于继承shiro默认的UsernamePasswordToken,添加一个字段用于标识不同的realm,用户登录的时候带上标识的字段shiro则根据字段到不同的realm去验证登录,授权等.

3.1 自定义类(UserToken)继承UsernamePasswordToken添加loginType属性

package com.koncord.shiro;

import org.apache.shiro.authc.UsernamePasswordToken;

public class UserToken extends UsernamePasswordToken{

	private static final long serialVersionUID = 1L;
	
	private String loginType;//登录类型 
	
	public UserToken(){};
	
	public UserToken(final String username,final String password,final String loginType){
		super(username,password);
		this.loginType = loginType;
	}

	public String getLoginType() {
		return loginType;
	}

	public void setLoginType(String loginType) {
		this.loginType = loginType;
	}
	
	
}

3.2 登录时使用继承后的UserToken,添加loginType属性

两种用户使用一个登录方法,可做如下处理:

(1)修改登录页面,添加用户类型下拉框

<!DOCTYPE html>
<html>
  <head>
    <title>登录</title>
	
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->

  </head>
  
  <body>
    <h3>登录</h3>
    <h3 th:text="${msg}" style="color:red;"></h3>
    <form method="post" action="login">
       <div>用户名:<input type="text" name="userName" autocomplete="off"></div> 
       <div>密码:<input type="password" name="password" autocomplete="new-password"></div>
       <div>用户类型:
         <select id="loginType" name="loginType">
           <option value="User">系统管理员</option>
           <option value="Member">普通用户</option>
         </select>
       </div>
       <input type="submit" value="登录">              
    </form>
  </body>
</html>

(2)修改 登录逻辑处理方法:
登录时使用继承后的UserToken,添加loginType属性

/**
	 * 登录逻辑处理
	 * @param userName:用户名
	 * @param password:密码
	 * @param loginType:登录用户类型
	 */
	@RequestMapping("/login")
	public String login(String userName,String password,String loginType,Model model){
		/**
		 * 使用Shiro编写认证操作
		 */
		//1.获取Subject
		Subject subject=SecurityUtils.getSubject();
		//2.封装用户数据(把用户名和密码封装为UsernamePasswordToken对象)
//		UsernamePasswordToken token = new UsernamePasswordToken(userName,password);
		UserToken token = new UserToken(userName, password, loginType);
		try {
			//3.执行登录方法
			subject.login(token);
			//只要没有异常,则登录成功;有异常则登录失败
			System.out.println("用户["+userName+"]登录成功");
			model.addAttribute("userName", userName);
			//登录成功,跳转test.html
			return "test";
			//return "redirect:/testThymeleaf";//重定向请求
		} 
		//若没有指定的账户,则shiro会抛出UnknownAccountException 异常
		catch (UnknownAccountException e) {
			//登录失败
			model.addAttribute("msg", "用户不存在");
			return "login";//返回页面链接
		} 
		//若账户存在,密码不匹配,则shiro会抛出IncorrectCredentialsException 异常
		catch (IncorrectCredentialsException e) {
			//登录失败
			model.addAttribute("msg", "密码错误");
			return "login";
		}
		/*//所有认证时异常的父类
		catch (AuthenticationException e) {
			return "login";
		}*/
	}

(3)修改登录成功跳转的页面内容:

<!DOCTYPE html>
<html>
  <head>
    <title>测试thymeleaf的使用</title>
	
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->

  </head>
  
  <body>
    <div style="display: flex"> 
      欢迎<div style="font-weight: bold" th:text="${userName}"></div>访问!
   </div>
    <a href="toLogin">切换账号</a>
  </body>
</html>

3.3 创建一个org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类,并重写doAuthenticate()方法,让特定的Realm完成特定的功能

package com.koncord.shiro;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;

/**
 * 当配置了多个Realm时,我们通常使用的 认证器 是shiro自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator,
 * 其中决定使用的Realm的是doAuthenticate()方法。
 * 
 * 自定义Authenticator
 * 注意,当需要分别定义处理 普通用户和系统管理员验证的Realm时,对应Realm的全类名应该包含字符串“Member”或者“User”。
 * 并且,他们不能相互包含,例如 处理 普通用户的Realm的全类名中不应该包含字符串“User”
 * 
 * @author Administrator
 *
 */
public class UserModularRealmAuthenticator extends ModularRealmAuthenticator{

	@Override
	protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
			throws AuthenticationException {
		//判断getRealms()是否返回为空
		assertRealmsConfigured();
		//强制转换回自定义的UserToken
		UserToken userToken = (UserToken) authenticationToken;
		//获取登录类型
		String loginType = userToken.getLoginType();
		
		//所有Realm
        Collection<Realm> realms = getRealms();
        
        //登录类型对应的所有Realm
        Collection<Realm> loginTypeRealms = new ArrayList<Realm>();
        for(Realm realm : realms){
        	if(realm.getName().contains(loginType)){
        		loginTypeRealms.add(realm);
        	}
        }
        //判断是 单Realm 还是 多Realm
        if (loginTypeRealms.size() == 1) {
        	System.out.println("doSingleRealmAuthentication() 执行");
            return doSingleRealmAuthentication(loginTypeRealms.iterator().next(), userToken);
        } else {
        	System.out.println("doMultiRealmAuthentication() 执行");
            return doMultiRealmAuthentication(loginTypeRealms, userToken);
        }
	}
}

3.4 创建两个自定义Realm,分别处理普通用户和系统管理员:

MemberRealm:

package com.koncord.shiro;


import org.apache.shiro.SecurityUtils;
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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import com.koncord.model.Member;
import com.koncord.service.MemberService;

/**
 * 自定义 Realm:用于普通用户 认证和授权
 * @author Administrator
 *
 */
public class MemberRealm extends AuthorizingRealm{

	@Autowired
	private MemberService memberService;
	
	/**
	 * 执行授权逻辑
	 * 当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("MemberRealm 执行授权逻辑");
		//给资源进行授权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//添加资源的授权字符串
		//info.addStringPermission("user:add");
		
		//导数据库查询当前登录用户的授权字符串
		//获取当前登录用户
		Subject subject=SecurityUtils.getSubject();
		Member member = (Member) subject.getPrincipal(); 
		
		Member memberInfo=memberService.findMemberByName(member.getName());
		
		info.addStringPermission(memberInfo.getPerms());
		return info;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("MemberRealm 执行认证逻辑");
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据用户名获取用户信息
		Member memberInfo=memberService.findMemberByName(userToken.getUsername());
				
		if(memberInfo == null){
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:数据库密码       第三个参数:realm的名字
//		return new SimpleAuthenticationInfo(user, user.getPassword(), "");
		
		//根据用户的情况,来构建AuthenticationInfo对像并返回,通常使用的实现类是SimpleAuthenticationInfo
		//以下信息是从数据库获取的
		//1).principal:认证的实体类信息。可以是username,也可以是数据表对应的用户的体类对象
		Object principal=memberInfo;
		//2).credentials:密码(数据库获取的用户的密码)
		Object credentials=memberInfo.getPassword();
		//3).realmName:当前realm对象的name,调用父类的getName()方法即可
		String realmName=getName();
		//4).盐值
		ByteSource credentialsSalt = ByteSource.Util.bytes(memberInfo.getName());//盐值 要唯一
		return new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
	}

	public static void main(String[] args) {
		String hashAlgorithmName="MD5";//加密算法(与配置文件中的一致)
		String credentials="111111";//密码
		ByteSource salt=ByteSource.Util.bytes("用户1");//盐值
//		ByteSource salt=null;//盐值
		int hashIterations=1024;//加密次数(与配置文件中的一致)
		System.out.println(new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations));
	}
}

UserRealm:

package com.koncord.shiro;


import org.apache.shiro.SecurityUtils;
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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import com.koncord.model.User;
import com.koncord.service.UserService;

/**
 * 自定义 Realm:用于系统管理员 认证和授权
 * @author Administrator
 *
 */
public class UserRealm extends AuthorizingRealm{

	@Autowired
	private UserService userService;
	
	/**
	 * 执行授权逻辑
	 * 当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("UserRealm 执行授权逻辑");
		//给资源进行授权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//添加资源的授权字符串
		//info.addStringPermission("user:add");
		
		//导数据库查询当前登录用户的授权字符串
		//获取当前登录用户
		Subject subject=SecurityUtils.getSubject();
		User user = (User) subject.getPrincipal(); 
		
		User sbUser=userService.findUserByName(user.getName());
		
		info.addStringPermission(sbUser.getPerms());
		return info;
	}

	/**
	 * 执行认证逻辑
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("UserRealm 执行认证逻辑");
		
		//编写shiro 判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken userToken=(UsernamePasswordToken) token;
		//根据用户名获取用户信息
		User user=userService.findUserByName(userToken.getUsername());
				
		if(user == null){
			//用户不存在
			return null;//shiro底层会抛出 UNknowAccountException
		}
		//2.判断密码     第一个参数:需要返回给 subject.login方法的数据   第二个参数:数据库密码       第三个参数:realm的名字
//		return new SimpleAuthenticationInfo(user, user.getPassword(), "");
		
		//根据用户的情况,来构建AuthenticationInfo对像并返回,通常使用的实现类是SimpleAuthenticationInfo
		//以下信息是从数据库获取的
		//1).principal:认证的实体类信息。可以是username,也可以是数据表对应的用户的体类对象
		Object principal=user;
		//2).credentials:密码(数据库获取的用户的密码)
		Object credentials=user.getPassword();
		//3).realmName:当前realm对象的name,调用父类的getName()方法即可
		String realmName=getName();
		//4).盐值
		ByteSource credentialsSalt = ByteSource.Util.bytes(user.getName());//盐值 要唯一
		return new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
	}

	public static void main(String[] args) {
		String hashAlgorithmName="MD5";//加密算法(与配置文件中的一致)
		String credentials="111111";//密码
		ByteSource salt=ByteSource.Util.bytes("admin");//盐值
//		ByteSource salt=null;//盐值
		int hashIterations=1024;//加密次数(与配置文件中的一致)
		System.out.println(new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations));
	}
}

3.5 在ShiroConfig配置类中配置多Realm:

主要代码如下:

 * 2.创建 DefaultWebSecurityManager
	 * SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("userRealm")UserRealm userRealm){
		DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//		//关联realm(单个realm)
//		securityManager.setRealm(userRealm);
		//设置realm(需要在realm定义之前)
		securityManager.setAuthenticator(modularRealmAuthenticator());
		//添加多个realms
		List<Realm> realms =new ArrayList<Realm>();
		realms.add(userRealm);
		realms.add(memberRealm());
//		realms.add(secondRealm());
		securityManager.setRealms(realms);
		return securityManager;
	}
	
	/**
	 * 1.创建Realm
	 */
	@Bean
	public UserRealm userRealm(){
		UserRealm userRealm = new UserRealm();
		//设置 凭证匹配器
		userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return userRealm;
	}
	
	/**
	 * 创建Realm:普通用户
	 * @return
	 */
	@Bean
	public MemberRealm memberRealm(){
		MemberRealm memberRealm = new MemberRealm();
		//设置 凭证匹配器
		memberRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return memberRealm;
	}	/**
	 * 配置 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了,
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码;)
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		//设置加密算法
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");
		//设置加密次数,比如两次,相当于md5(md5())
		hashedCredentialsMatcher.setHashIterations(1024);
		return hashedCredentialsMatcher;
	}
	/**
	 * 系统自带的Realm管理,主要针对多realm
	 */
	@Bean
	public ModularRealmAuthenticator modularRealmAuthenticator(){
		//自己重写的ModularRealmAuthenticator
		UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
		//设置认证策略
		modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
//		ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
//		//设置认证策略
//		modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
		return modularRealmAuthenticator;
	}

ShiroConfig全部代码:

package com.koncord.shiro;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**
 * Shiro配置类
 * @author Administrator
 *
 */
@Configuration
public class ShiroConfig {

	/**
	 * 3.创建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean =new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro内置过滤器
		/**
		 * Shiro内置过滤器,可以实现权限相关的拦截
		 *    常用的过滤器:
		 *       anon:无需认证(登录)可以访问
		 *       authc:必须认证才可以访问
		 *       user:如果使用rememberMe的功能可以直接访问
		 *       perms:该资源必须得到资源权限才可以访问
		 *       role:该资源必须得到角色权限才可以访问
		 */
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
//		filterMap.put("/add", "authc");
//		filterMap.put("/update", "authc");
		filterMap.put("/testThymeleaf", "anon");
		filterMap.put("/login", "anon");
		
		//授权过滤器
		//注意:当前授权拦截后,shiro会自动跳转到未授权页面
		filterMap.put("/add", "perms[user:add]");
		filterMap.put("/update", "perms[user:update]");
		
		filterMap.put("/*", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		//设置登录跳转链接
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		//设置未授权提示页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 2.创建 DefaultWebSecurityManager
	 * SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager securityManager(@Qualifier("userRealm")UserRealm userRealm){
		DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();
//		//关联realm(单个realm)
//		securityManager.setRealm(userRealm);
		//设置realm(需要在realm定义之前)
		securityManager.setAuthenticator(modularRealmAuthenticator());
		//添加多个realms
		List<Realm> realms =new ArrayList<Realm>();
		realms.add(userRealm);
		realms.add(memberRealm());
//		realms.add(secondRealm());
		securityManager.setRealms(realms);
		return securityManager;
	}
	
	/**
	 * 1.创建Realm
	 */
	@Bean
	public UserRealm userRealm(){
		UserRealm userRealm = new UserRealm();
		//设置 凭证匹配器
		userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return userRealm;
	}
	
	/**
	 * 创建Realm:普通用户
	 * @return
	 */
	@Bean
	public MemberRealm memberRealm(){
		MemberRealm memberRealm = new MemberRealm();
		//设置 凭证匹配器
		memberRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return memberRealm;
	}
	
	/**
	 * 创建第二个Realm
	 */
	@Bean
	public SecondRealm secondRealm(){
		SecondRealm secondRealm = new SecondRealm();
		//设置 凭证匹配器
		secondRealm.setCredentialsMatcher(hashedCredentialsMatcherSHA());
		return secondRealm;
	}
	
	/**
	 * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
	 */
	@Bean
	public ShiroDialect getShiroDialect(){
		return new ShiroDialect();
	}
	
	/**
	 * 配置 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了,
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码;)
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		//设置加密算法
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");
		//设置加密次数,比如两次,相当于md5(md5())
		hashedCredentialsMatcher.setHashIterations(1024);
		return hashedCredentialsMatcher;
	}
	
	/**
	 * 配置 凭证匹配器 ,加密算法为 SHA1
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcherSHA(){
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		//设置加密算法
		hashedCredentialsMatcher.setHashAlgorithmName("SHA1");
		//设置加密次数,比如两次,相当于SHA1(SHA1())
		hashedCredentialsMatcher.setHashIterations(1024);
		return hashedCredentialsMatcher;
	}
	
	/**
	 * 系统自带的Realm管理,主要针对多realm
	 */
	@Bean
	public ModularRealmAuthenticator modularRealmAuthenticator(){
		//自己重写的ModularRealmAuthenticator
		UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
		//设置认证策略
		modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
//		ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
//		//设置认证策略
//		modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
		return modularRealmAuthenticator;
	}
}

SpringBoot 整合shiro实现多realm配置就完成了,下面看一下实现效果。

4.实现效果

在这里插入图片描述

5.源码

点击此处下载

上一篇:SpringBoot 与Shiro 整合系列(三)多Realm验证和认证策略

参考资料:
springboot整合shiro实现多realm不同数据表登陆
Spring Boot 集成Shiro的多realm配置