shiro进行登录认证和权限管理的实现。其中需求涉及使用两个角色分别是:门店,公司。现在要两者实现分开登录。即需要两个Realm——MyShiroRealmSHOP和MyShiroRealmCOMPANY,分别处理门店,公司的验证功能。

但是正常情况下,当定义了多个Realm,无论是门店登录还是公司登录,都会由这两个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。

为了实现需求,我会创建一个org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类,并重写doAuthenticate()方法,让特定的Realm完成特定的功能。如何区分呢?我会同时创建一个org.apache.shiro.authc.UsernamePasswordToken的子类,在其中添加一个字段VirtualType,用来标识登录的类型,即是门店登录还是公司登录。具体步骤如下:

public enum VirtualType {
COMPANY, // 公司
SHOP // 门店
}

  接下来新建org.apache.shiro.authc.UsernamePasswordToken的子类UserToken

import org.apache.shiro.authc.UsernamePasswordToken;

public class UserToken extends UsernamePasswordToken {
private VirtualType virtualType; public UserToken(final String username, final String password, VirtualType virtualType) {
super(username, password);
this.virtualType = virtualType;
} public VirtualType getVirtualType() {
return virtualType;
} public void setVirtualType(VirtualType virtualType) {
this.virtualType = virtualType;
}
}

  新建org.apache.shiro.authc.pam.ModularRealmAuthenticator的子类UserModularRealmAuthenticator:

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; public class UserModularRealmAuthenticator extends ModularRealmAuthenticator { private static final Logger logger = LoggerFactory.getLogger(UserModularRealmAuthenticator.class); @Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
throws AuthenticationException {
logger.info("UserModularRealmAuthenticator:method doAuthenticate() execute ");
// 判断getRealms()是否返回为空
assertRealmsConfigured();
// 强制转换回自定义的CustomizedToken
UserToken userToken = (UserToken) authenticationToken;
// 登录类型
VirtualType virtualType = userToken.getVirtualType();
// 所有Realm
Collection<Realm> realms = getRealms();
// 登录类型对应的所有Realm
Collection<Realm> typeRealms = new ArrayList<>();
for (Realm realm : realms) {
if (realm.getName().contains(virtualType.toString()))  // 注:这里使用类名包含枚举,区分realm
typeRealms.add(realm);
}
// 判断是单Realm还是多Realm
if (typeRealms.size() == 1) {
logger.info("doSingleRealmAuthentication() execute ");
return doSingleRealmAuthentication(typeRealms.iterator().next(), userToken);
} else {
logger.info("doMultiRealmAuthentication() execute ");
return doMultiRealmAuthentication(typeRealms, userToken);
}
}
}

  创建分别处理门店登录还是公司登录的Realm:

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 org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet;
import java.util.Set; /**公司登陆realm
*/
public class MyShiroRealmCOMPANY extends AuthorizingRealm { @Autowired
IUserService userService; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal(); // 一定是String类型,在SimpleAuthenticationInfo
SystemUser systemUser = userService.getUserByName(username, VirtualType.COMPANY);
if (systemUser == null) {
throw new RuntimeException("system concurrent exception: COMPANY user not found:username=" + username);
} SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> stringPermissions = new HashSet<>(256);
     // 字符串资源
authorizationInfo.addStringPermissions(stringPermissions);
return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserToken token = (UserToken)authenticationToken;
// 逻辑登陆
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}

  

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 org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet;
import java.util.Set; /**门店登陆realm
*/
public class MyShiroRealmSHOP extends AuthorizingRealm { @Autowired
IUserService userService; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal(); // 一定是String类型,在SimpleAuthenticationInfo
SystemUser systemUser = userService.getUserByName(username, VirtualType.SHOP);
if (systemUser == null) {
throw new RuntimeException("system concurrent exception: SHOP user not found:username=" + username);
} SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); Set<String> stringPermissions = new HashSet<>(256);
     // 字符串资源
authorizationInfo.addStringPermissions(stringPermissions);
return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
UserToken token = (UserToken)authenticationToken;
// 逻辑登陆
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
}
}

  ShiroConfig配置

    @Bean("securityManager")
public SecurityManager securityManager(RedisTemplate redisTemplate) {          // redisTemplate配置的redis缓存,可忽略
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
List<Realm> realms = new ArrayList<>();
//添加多个Realm
realms.add(myShiroRealmSHOP(redisTemplate));
realms.add(myShiroRealmCOMPANY(redisTemplate));
securityManager.setAuthenticator(modularRealmAuthenticator()); // 需要再realm定义之前
securityManager.setRealms(realms);
securityManager.setSessionManager(myShiroSession(redisTemplate));
return securityManager;
} /**
* 系统自带的Realm管理,主要针对多realm 认证
*/
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
} @Bean("myShiroRealmSHOP")
public MyShiroRealmSHOP myShiroRealmSHOP(RedisTemplate redisTemplate) {
return new MyShiroRealmSHOP();
} @Bean("myShiroRealmCOMPANY")
public MyShiroRealmCOMPANY myShiroRealmCOMPANY(RedisTemplate redisTemplate) {
return new MyShiroRealmCOMPANY();
}

  登陆即可:

subject.login(new UserToken(username, password, virtualType))  

这里需要注意的是,上述配置的Authenticator主要针对登陆认证,对于授权时没有控制的,使用资源注入时会发现,使用的是myShiroRealmSHOP的doGetAuthorizationInfo方法(上面SHOP的定义在前),没有走对应的realm的授权,产生问题错乱;

新建org.apache.shiro.authz.ModularRealmAuthorizer子类:

import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserModularRealmAuthorizer extends ModularRealmAuthorizer {
@Override
public boolean isPermitted(PrincipalCollection principals, String permission) {
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)){ continue;}
// todo 授权配置
if (realm.getName().contains(VirtualType.COMPANY.toString())) {    // 判断realm
if (permission.contains("company")) {    // 判断是否改realm的资源
return ((MyShiroRealmCOMPANY) realm).isPermitted(principals, permission); // 使用改realm的授权方法
}
}
if (realm.getName().contains(VirtualType.SHOP.toString())) {
if (permission.contains("shop")) {
return ((MyShiroRealmSHOP) realm).isPermitted(principals, permission);
}
}
}
return false;
}
}

  然后在ShiroConfig更改:

    @Bean("securityManager")
public SecurityManager securityManager(RedisTemplate redisTemplate) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 使用注解,@RequiresPermissions,读取缓存权限信息保存key对象为:{@link org.apache.shiro.subject.SimplePrincipalCollection},所以redis缓存配置的String不能转换
// securityManager.setCacheManager(redisCacheManager(redisTemplate));
List<Realm> realms = new ArrayList<>();
//添加多个Realm
realms.add(myShiroRealmSHOP(redisTemplate));
realms.add(myShiroRealmCOMPANY(redisTemplate));
securityManager.setAuthenticator(modularRealmAuthenticator());
securityManager.setAuthorizer(modularRealmAuthorizer());    // 这里
securityManager.setRealms(realms);
securityManager.setSessionManager(myShiroSession(redisTemplate));
return securityManager;
} /**
* 系统自带的Realm管理,主要针对多realm 认证
*/
@Bean
public ModularRealmAuthenticator modularRealmAuthenticator() {
//自己重写的ModularRealmAuthenticator
UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return modularRealmAuthenticator;
} /**
* 系统自带的Realm管理,主要针对多realm 授权
*/
@Bean
public ModularRealmAuthorizer modularRealmAuthorizer() {
//自己重写的ModularRealmAuthorizer
UserModularRealmAuthorizer modularRealmAuthorizer = new UserModularRealmAuthorizer();
return modularRealmAuthorizer;
} @Bean("myShiroRealmSHOP")
public MyShiroRealmSHOP myShiroRealmSHOP(RedisTemplate redisTemplate) {
return new MyShiroRealmSHOP();
} @Bean("myShiroRealmCOMPANY")
public MyShiroRealmCOMPANY myShiroRealmCOMPANY(RedisTemplate redisTemplate) {
return new MyShiroRealmCOMPANY();
}

  

参考:https://blog.csdn.net/cckevincyh/article/details/79629022

springboot shiro 多realm配置认证、授权的更多相关文章

  1. SpringBoot整合shiro实现用户的认证授权

    * 项目环境搭建 * 配置ShiroConfig,用于shiro的基本配置和注入自定义规则 * 实现自定义的realm,继承AuthorizingRealm * 编写测试controller和页面 基 ...

  2. Shiro学习笔记 三(认证授权)

    第一种首先基于角色的权限控制 1.由于不断的创建SecurityFactory工程等步骤重复多次,所以应该将这些步骤封装成一个工具类 还是首先看一下目录结构 主要用到文件 首先贴一下工具类的方法 pa ...

  3. 30分钟了解Shiro与Springboot的多Realm基础配置

    写在前面的话: 我之前写过两篇与shiro安全框架有关的博文,居然能够广受欢迎实在令人意外.说明大家在互联网时代大伙对于安全和登录都非常重视,无论是大型项目还是中小型业务,普遍都至少需要登录与认证的逻 ...

  4. SpringBoot 集成Shiro之使用Redis缓存授权认证信息

    因为用户认证与授权需要从数据库中查询并验证信息,但是对于权限很少改变的情况,这样不断从数据库中查询角色验证权限,对整个系统的开销很大,对数据库压力也随之增大.因此可以将用户认证和授权信息都缓存起来,第 ...

  5. 【原】无脑操作:IDEA + maven + Shiro + SpringBoot + JPA + Thymeleaf实现基础认证权限

    开发环境搭建参见<[原]无脑操作:IDEA + maven + SpringBoot + JPA + Thymeleaf实现CRUD及分页> 需求: ① 除了登录页面,在地址栏直接访问其他 ...

  6. springboot shiro和freemarker集成之权限控制完全参考手册(跳过认证,登录由三方验证,全网首发)

    本文主要考虑单点登录场景,登录由其他系统负责,业务子系统只使用shiro进行菜单和功能权限校验,登录信息通过token从redis取得,这样登录验证和授权就相互解耦了. 用户.角色.权限进行集中式管理 ...

  7. Spring Cloud之路:(七)SpringBoot+Shiro实现登录认证和权限管理

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/sage_wang/article/details/79592269一.Shiro介绍1.Shiro是 ...

  8. (转)Springboot+shiro配置笔记+错误小结

    springboot不像springmvc,它没有xml配置文件,那该如何配置shiro呢,其实也不难,用java代码+注解来解决这个问题.仅以此篇记录我对shiro的学习,如有对过客造成不便,实在抱 ...

  9. Springboot+shiro配置笔记+错误小结

    软件152 尹以操 springboot不像springmvc,它没有xml配置文件,那该如何配置shiro呢,其实也不难,用java代码+注解来解决这个问题.仅以此篇记录我对shiro的学习,如有对 ...

随机推荐

  1. bzoj1790: [Ahoi2008]Rectangle 矩形藏宝地

    被统考草翻回来做题不太行啊,线段树和cdq都写挂细节 这题大概就是四维偏序吧,欸n怎么到了20w,只能水70啊 但是这个好像只要有1个在里面就可以ans就可以++了耶 突然想到高中奥数老师说的,大概是 ...

  2. malloc和new出来的地址都是虚拟地址 你就说内存管理单元怎么可能让你直接操作硬件内存地址!

    malloc的实现与物理内存自然是无关的,内核为每个进程维护一张页表,页表存储进程空间内每页的虚拟地址,页表项中有的虚拟内存页对应着某个物理内存页面,也有的虚拟内存页没有实际的物理页面对应.无论mal ...

  3. UESTC--1265--宝贵资源(简单数学)

    宝贵资源 Time Limit: 1000MS   Memory Limit: 65535KB   64bit IO Format: %lld & %llu Submit Status Des ...

  4. oracle故障处理之删除大表空间hang住

    背景 数据库分区表数据越来越大,需要对过期话的数据进行迁移,以及大的分区表需要进行数据的清理和删除,达到释放磁盘空间的目的. 问题说明 环境:linux 6.X 数据库:oracle 11.2.0.4 ...

  5. tabBar的图标不被系统渲染

    navi.tabBarItem.selectedImage = [[UIImage imageNamed:imageStr]imageWithRenderingMode:UIImageRenderin ...

  6. SQLserver中用convert函数转换日期格式(1)

    SQLserver中用convert函数转换日期格式2008-01-15 15:51SQLserver中用convert函数转换日期格式 SQL Server中文版的默认的日期字段datetime格式 ...

  7. BZOJ 3796 后缀数组+KMP

    思路: 写得我头脑发蒙,,, 旁边还有俩唱歌的 抓狂 (感谢lh大爷查错) 首先 1.w是s1的子串 2.w是s2的子串 这两步很好办啊~ 后缀数组一下O(n)就可以搞 重点是 这个:3.s3不是w的 ...

  8. linux下服务启动脚本

    #!/usr/bin/env python# -*- coding: utf-8 -*-# @File : deployment.py# @Author: Anthony.waa# @Date : 2 ...

  9. 软件测试作业-selenium

    一.作业要求 1.安装SeleniumIDE插件. 2.学会使用SeleniumIDE录制脚本和导出脚本. 3.访问网址使用学号登录系统,进入系统后可以看到该同学的git地址. 4.编写Seleniu ...

  10. Apache-TomCat安装配置

    Apache-TomCat安装配置 本文是免安装版的Tomcat!(安装JavaJDK的步骤就不多述了!) (1)官网下载地址:https://tomcat.apache.org/download-8 ...